Browse Source

报名工具小程序初始代码

master
liupopo 1 year ago
commit
d7420944ba
  1. 131
      .gitignore
  2. 154
      App.vue
  3. 32
      common/Back.js
  4. 79
      common/ChooseUpload.js
  5. 37
      common/Distance.js
  6. 72
      common/GameCache.js
  7. 465
      common/Http.js
  8. 48
      common/PreferenceHelper.js
  9. 5
      common/PxToRpxOrPxToUpx.js
  10. 11
      common/TextUtils.js
  11. 201
      common/Time.js
  12. 8
      common/Toast.js
  13. 12
      common/intent.js
  14. 353
      common/utils.js
  15. 170
      common/wxAuthLogin.js
  16. 73
      common/wxSilentLogin.js
  17. 76
      components/EditText/EditText.vue
  18. 51
      components/PublishItem/PublishItem.vue
  19. 585
      components/RefreshView/RefreshView.vue
  20. 65
      components/RefreshView/mescroll-down.css
  21. 79
      components/RefreshView/readme.md
  22. 19
      components/RefreshView/refresh-option.js
  23. 275
      components/RichTextEditor/RichTextEditor.vue
  24. 239
      components/RichTextEditor/editor-icon.css
  25. 30
      components/RichTextEditor/readme.md
  26. 108
      components/SendCodeItem/SendCodeItem.vue
  27. 245
      components/TabLayout/TabLayout.vue
  28. 93
      components/UserItem/UserItem.vue
  29. 516
      components/biaofun-datetime-picker/biaofun-datetime-picker.vue
  30. 30
      components/biaofun-datetime-picker/使用说明.md
  31. 422
      components/city-select/city-select.vue
  32. 36
      components/city-select/citySelect.js
  33. 1024
      components/city-select/citys.js
  34. 697
      components/cropper/cropper.vue
  35. 79
      components/cropper/readme.md
  36. 163
      components/drop-item/drop-item.vue
  37. BIN
      components/firstui/.DS_Store
  38. 379
      components/firstui/fui-button/fui-button.vue
  39. 445
      components/firstui/fui-dropdown-menu/fui-dropdown-menu.vue
  40. 154
      components/firstui/fui-icon/fui-icon.js
  41. BIN
      components/firstui/fui-icon/fui-icon.ttf
  42. 93
      components/firstui/fui-icon/fui-icon.vue
  43. 217
      components/firstui/fui-list-cell/fui-list-cell.vue
  44. 85
      components/firstui/fui-theme/fui-theme.css
  45. 21
      components/grid/grid.vue
  46. 68
      components/helang-asyncSwitch/helang-asyncSwitch.vue
  47. 186
      components/item-drop/item-drop.vue
  48. 149
      components/item-scan/item-scan.vue
  49. 41
      components/loading/loading.vue
  50. 56
      components/mescroll-uni/components/mescroll-down.css
  51. 54
      components/mescroll-uni/components/mescroll-down.vue
  52. 92
      components/mescroll-uni/components/mescroll-empty.vue
  53. 80
      components/mescroll-uni/components/mescroll-top.vue
  54. 47
      components/mescroll-uni/components/mescroll-up.css
  55. 39
      components/mescroll-uni/components/mescroll-up.vue
  56. 15
      components/mescroll-uni/mescroll-body.css
  57. 309
      components/mescroll-uni/mescroll-body.vue
  58. 60
      components/mescroll-uni/mescroll-mixins.js
  59. 34
      components/mescroll-uni/mescroll-uni-option.js
  60. 33
      components/mescroll-uni/mescroll-uni.css
  61. 869
      components/mescroll-uni/mescroll-uni.js
  62. 386
      components/mescroll-uni/mescroll-uni.vue
  63. 23
      components/mescroll-uni/mixins/mescroll-comp.js
  64. 48
      components/mescroll-uni/mixins/mescroll-more-item.js
  65. 56
      components/mescroll-uni/mixins/mescroll-more.js
  66. 20
      components/noData/noData.vue
  67. 135
      components/pick-regions/pick-regions.vue
  68. 10330
      components/pick-regions/regions.json
  69. 459
      components/sl-filter/filter-view.vue
  70. 20
      components/sl-filter/iconfont/iconfont.css
  71. 122
      components/sl-filter/popup-layer.vue
  72. 301
      components/sl-filter/sl-filter.vue
  73. 173
      components/tabRefresh/tabRefresh.vue
  74. 112
      components/tabTop/tabTop.vue
  75. 45
      components/uni-datetime-picker/keypress.js
  76. 804
      components/uni-datetime-picker/uni-datetime-picker.vue
  77. 243
      components/uni-popup-dialog/uni-popup-dialog.vue
  78. 22
      components/uni-popup/message.js
  79. 25
      components/uni-popup/popup.js
  80. 294
      components/uni-popup/uni-popup.vue
  81. 280
      components/uni-transition/uni-transition.vue
  82. 22
      index.html
  83. 99
      main.js
  84. 68
      manifest.json
  85. 255
      pages.json
  86. 164
      pages/city/CitySelectActivity.vue
  87. 482
      pages/find/ActivityDetail.vue
  88. 255
      pages/find/ActivityList.vue
  89. 136
      pages/find/BaoMingListActivity.vue
  90. 217
      pages/find/TeamEnroll.vue
  91. 364
      pages/home/AddFragment.vue
  92. 326
      pages/home/FindFragment.vue
  93. 299
      pages/home/UserFragment.vue
  94. 312
      pages/index/ArenaDetailActivity.vue
  95. 136
      pages/index/BaoMingListActivity.vue
  96. 146
      pages/index/BindPhone.vue
  97. 394
      pages/index/DetailActivity.vue
  98. 470
      pages/index/InputHappyGameAreaActivity.vue
  99. 128
      pages/index/InputHappyGameLimitActivity.vue
  100. 127
      pages/index/UserAuthentication.vue

131
.gitignore

@ -0,0 +1,131 @@
# ---> Node
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
.env.production
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
**/.idea/
**/target/
*.iml
*.ipr
*.iws
*.log
### VS Code ###
.vscode/
.hbuilderx/
unpackage/

154
App.vue

@ -0,0 +1,154 @@
<script>
export default {
globalData: {
// baseURL: "http://127.0.0.1:8111/",
// wxSilentLoginURL: "http://127.0.0.1:8111/portal/v1/sysUser/wxSilentLogin",
// wxAuthLoginURL: "http://127.0.0.1:8111/portal/v1/sysUser/wxAuthLogin",
baseURL: "https://gateway.yyundong.com/",
wxSilentLoginURL: "https://gateway.yyundong.com/portal/v1/sysUser/wxSilentLogin",
wxAuthLoginURL: "https://gateway.yyundong.com/portal/v1/sysUser/wxAuthLogin",
isDebug: true,
token: "",
isLogin: false,
sysUserSid: "",
isOnLunch: false,
statusBarHeight: "",
navHeight: "",
navWidth: "",
totalHeight: "",
statusBarHeightPx: "",
navHeightPx: "",
navWidthPx: "",
totalHeightPx: "",
statusBarHeightUpx: "",
navHeightUpx: "",
navWidthUpx: "",
totalHeightUpx: "",
windowWidth: "",
isAuthentication: false,
gameCache: {
gameData: {
"isCache": false
},
cache: false
}
},
onLaunch: function() {
this.WxSilentLogin()
let sysUserSid = this.ReadPreference("sysUserSid")
if (sysUserSid != null && sysUserSid.length != 0) {
this.globalData.sysUserSid = sysUserSid
}
let isLogin = this.ReadPreference("isLogin")
if (isLogin != null && isLogin == 1) {
this.globalData.isLogin = isLogin
}
let token = this.ReadPreference("token")
if (token != null && token.length != 0) {
this.globalData.token = token
}
console.log('用户Sid', this.globalData.sysUserSid)
console.log('是否登陆', this.globalData.isLogin)
console.log('App', 'onLaunch')
//
console.log('高德地图定位Web服务key', '59970402d1c3f7dc1efff17d4dfcff21')
this.globalData.isOnLunch = true
let cache = this.ReadPreference("cache")
if (cache != null && cache.length != 0) {
this.globalData.gameCache.cache = cache
} else {
this.globalData.gameCache.cache = false
}
if (this.globalData.gameCache.cache) {
let gameCache = this.ReadPreference2("cacheData")
if (gameCache == undefined || gameCache == null || gameCache === "" || gameCache === "null") {
this.globalData.gameCache.cache = false
} else {
gameCache.isCache = true
this.globalData.gameCache.gameData = gameCache
}
} else {
this.globalData.gameCache.gameData = {
"isCache": false
}
}
//
let info = uni.getSystemInfoSync()
//
this.globalData.statusBarHeight = info.statusBarHeight
this.globalData.windowWidth = info.windowWidth
this.globalData.windowWidthPx = info.windowWidth + 'px'
//console.log("" + this.globalData.windowWidthPx)
//
this.globalData.navHeight = '45'
//
this.globalData.navWidth = this.globalData.windowWidth
//
this.globalData.totalHeight = 45 + this.globalData.statusBarHeight
//
// #ifdef MP-WEIXIN||MP-BAID||MP-QQ||MP-TOUTIAO
let menuButton = uni.getMenuButtonBoundingClientRect()
let top = menuButton.top
let bottom = menuButton.bottom
let navHeight = bottom - top + (top - this.globalData.statusBarHeight) * 2 + 4
this.globalData.navHeight = navHeight
this.globalData.navWidth = menuButton.left
this.globalData.totalHeight = navHeight + this.globalData.statusBarHeight
// #endif
//console.log('', this.globalData.navWidth)
//console.log('', this.globalData.navHeight)
//console.log('', this.globalData.statusBarHeight)
//console.log('', this.globalData.totalHeight)
this.globalData.navWidthPx = this.globalData.navWidth + 'px'
this.globalData.navHeightPx = this.globalData.navHeight + 'px'
this.globalData.statusBarHeightPx = this.globalData.statusBarHeight + 'px'
this.globalData.totalHeightPx = this.globalData.totalHeight + 'px'
//console.log('', this.globalData.navWidthPx)
//console.log('', this.globalData.navHeightPx)
//console.log('', this.globalData.statusBarHeightPx)
//console.log('', this.globalData.totalHeightPx)
this.globalData.navWidthUpx = this.Px2Upx(this.globalData.windowWidth, this.globalData.navWidth)
this.globalData.navHeightUpx = this.Px2Upx(this.globalData.windowWidth, this.globalData.navHeight)
this.globalData.statusBarHeightUpx = this.Px2Upx(this.globalData.windowWidth, this.globalData
.statusBarHeight)
this.globalData.totalHeightUpx = this.Px2Upx(this.globalData.windowWidth, this.globalData.totalHeight)
//console.log('upx', this.globalData.navWidthUpx)
//console.log('upx', this.globalData.navHeightUpx)
//console.log('upx', this.globalData.statusBarHeightUpx)
//console.log('upx', this.globalData.totalHeightUpx)
//
// #ifdef APP-PLUS
plus.navigator.closeSplashscreen();
// #endif
},
onShow: function() {
//console.log('App Show')
},
onHide: function() {
//console.log('App Hide')
}
}
</script>
<style>
/*每个页面公共css */
/*每个页面公共css */
page {
height: 100%;
width: 100%;
}
</style>

32
common/Back.js

@ -0,0 +1,32 @@
const back = () => {
uni.navigateBack({
delta: 1
})
}
const onActivityResult = () => {
var pages = getCurrentPages();
var currPage = pages[pages.length - 1]; //当前页面
let res = currPage.data;
return res.onActivityResult
}
// 返回数据到onShow()方法,取值 onActivityResult
const setResult = (options) => {
var pages = getCurrentPages();
var prevPage = pages[pages.length - 2]; //上一个页面
//直接调用上一个页面的setData()方法,把数据存到上一个页面中去
// 上一个页面最后设置userdata
let res = prevPage.data;
res.onActivityResult = options
uni.navigateBack({ //返回
delta: 1
})
}
export {
back,
setResult,
onActivityResult
}

79
common/ChooseUpload.js

@ -0,0 +1,79 @@
const chooseUpload = (num, url, type, formData) => {
if (num == undefined || num == null || num == "")
num = 1
if (url == undefined || url == null || url == "")
url = "aos/file/batchUploadImage"
if (type == undefined || type == null || type == "")
type = "files"
if (formData == undefined || formData == null || formData == "")
formData = {}
let totalImgs = 0;
let httpUrl = [];
return new Promise((resolve) => {
uni.chooseImage({
count: num, //默认1
sizeType: ['compressed'], //可以指定是原图还是压缩图,默认二者都有
sourceType: ['album'], //从相册选择
success: function(res) {
let chooseImgs = res.tempFilePaths;
totalImgs = chooseImgs.length;
console.log("chooseImgs", res.tempFilePaths)
if (totalImgs > 0)
uni.showLoading({
title: '请稍后...',
mask: true
});
for (var i = 0; i < chooseImgs.length; i++) {
uni.uploadFile({
url: getApp().globalData.baseURL + url, //仅为示例,非真实的接口地址
filePath: chooseImgs[i],
name: type,
formData: formData,
success: (uploadFileRes) => {
let data = JSON.parse(uploadFileRes.data)
console.log("data", data)
if (data.success) {
if (data.data != undefined && data.data.length != 0)
// 必须用变量去接受
httpUrl = httpUrl.concat(data.data)
}
},
fail: (err) => {
console.log("err-->", err)
},
complete: () => {
totalImgs--;
if (totalImgs <= 0) {
uni.hideLoading();
resolve({
"num": chooseImgs.length,
"urls": httpUrl
})
}
}
});
}
},
fail: function(res) {
resolve({
"num": 0,
"urls": []
})
}
});
})
}
export default chooseUpload

37
common/Distance.js

@ -0,0 +1,37 @@
/**
* @desc 根据两点间的经纬度计算距离
* @param float $lat 纬度值
* @param float $lng 经度值
*/
const getDistance = (lat1, lon1, lat2, lon2) => {
console.log(lat1)
console.log(lon1)
console.log(lat2)
console.log(lon2)
let ew1, ns1, ew2, ns2;
let distance;
// 角度转换为弧度
ew1 = lon1 * 0.01745329252;
ns1 = lat1 * 0.01745329252;
ew2 = lon2 * 0.01745329252;
ns2 = lat2 * 0.01745329252;
distance = distance = Math.sin(ns1) * Math.sin(ns2) + Math.cos(ns1) * Math.cos(ns2) *
Math.cos(ew1 - ew2);
// 调整到[-1..1]范围内,避免溢出
if (distance > 1.0)
distance = 1.0;
else if (distance < -1.0)
distance = -1.0;
// 求大圆劣弧长度
distance = 6370693.5 * Math.acos(distance);
let isBig = false; // 是否为大于等于1000m
if (distance >= 1000) {
distance /= 1000;
isBig = true;
}
return distance.toFixed(2) + (isBig ? "km" : "m");
}
export default getDistance

72
common/GameCache.js

@ -0,0 +1,72 @@
const writeGameCahce = (gameData) => {
gameData.isCache = true
uni.setStorageSync("cache", true);
uni.setStorageSync("cacheData", gameData);
console.log('保存game缓存成功')
}
const clearGameCache = (gameData) => {
if (gameData.isCache) {
uni.setStorageSync("cache", false);
uni.setStorageSync("cacheData", {});
console.log('清除game缓存成功')
return true;
}
return false;
}
/**
* 使用读取时 最好 hasCache() 返回True 在进行调用
*/
const readGameCahce = () => {
let value
try {
value = uni.getStorageSync("cacheData");
if (value) {
console.log(value);
}
} catch (e) {
// error
value = null
console.log("error");
}
if (value == undefined || value == null || value === "" || value === "null") {
return {};
}
return value;
}
/**
* 是否有缓存
*/
const hasCache = () => {
let value
try {
value = uni.getStorageSync("cache");
if (value) {
console.log(value);
}
} catch (e) {
// error
value = null
console.log("error");
}
if (value == undefined || value == null || value === "" || value === "null") {
return false;
}
return value;
}
export {
writeGameCahce,
clearGameCache,
readGameCahce,
hasCache
}

465
common/Http.js

@ -0,0 +1,465 @@
const baseImgURL = 'https://www.ourpyw.com/static/uni-app/';
import {
isEmpty
} from './TextUtils.js'
/**
* 通用网络请求
* loading: 是否显示网络请求时的loading默认不提示允许不传值
* showError: 是否显示错误信息默认显示如有特殊code需要自己判断逻辑时请改成false允许不传值
* method: 网络请求方式 GET/POST/PUT禁止不传值
* paramsType: 网络请求传参方式 JSON/FORM禁止不传值
* autoUrl: 自动拼接网址默认是 getApp().globalData.baseURL+ 相对地址允许不传值false认为非本项目域名是第三方接口
* 响应
* 响应到success则代表网络请求成功直接返回对象需自行判断逻辑
* 响应到fail则代表无网络或无连接情况
*/
const http = (options) => {
console.log(getApp().globalData.token)
if (getApp().globalData.isDebug) {
console.log('Http网络请求info', options)
}
if (isEmpty(options.loading)) {
// 显示loading(默认不显示loading)
options.loading = false
}
if (isEmpty(options.showError)) {
// 失败弹出提示(默认提示错误信息)
options.showError = true
}
// if (isEmpty(options.method) || (options.method != "POST" && options.method != "GET"&& options.method != "PUT")) {
if (isEmpty(options.method) || (options.method != "POST" && options.method != "GET" && options.method != "PUT" && options.method != "DELETE")) {
uni.showToast({
title: "网络请求 'method' 为 POST/GET/PUT",
icon: 'none',
duration: 4000
})
return
}
if ("JSON" == options.paramsType) {
options.paramsType = "application/json"
} else if ("FORM" == options.paramsType) {
options.paramsType = "application/x-www-form-urlencoded"
} else {
uni.showToast({
title: "网络请求 'paramsType' 为 JSON/FORM",
icon: 'none',
duration: 4000
})
return
}
if (isEmpty(options.autoUrl)) {
// 默认拼接网址
options.autoUrl = true
}
return new Promise((resolve, reject) => {
if (options.loading) {
uni.showLoading({
title: '加载中...',
// 默认遮罩出现可以继续操作
mask: options.load || true
});
}
var url = options.autoUrl ? getApp().globalData.baseURL + options.url : options.url
uni.request({
// 组装请求地址
url: url,
// 请求方式 GET POST
method: options.method,
header: {
// 传参方式
'content-type': options.paramsType,
'token': getApp().globalData.token
},
// 具体参数
data: options.data,
success: res => {
// 关闭显示框
uni.hideLoading();
console.log(res)
if (getApp().globalData.isDebug) {
console.log('Http网络路径', url)
console.log('Http网络请求结果', JSON.parse(JSON.stringify(res.data)))
}
if (res.statusCode == 200) {
// 自动拼接类型,认为是公司后台提供接口服务,按照规定标准进行检查返回内容
if (options.autoUrl) {
// 进行success字段检查
if (!res.data.success) {
if ("5000" == res.data.code) {
uni.setStorageSync("memberSid", null);
getApp().globalData.isLogin = false
getApp().globalData.memberSid = null
uni.showToast({
title: "出错了,请重试",
// 保证文字长度
icon: "none",
duration: 3000
})
return
} else {
// 错误提示
if (options.showError) {
if (isEmpty(res.data)) {
// 未成功获取到服务器返回的json
uni.showToast({
// 不能超过7个字
title: "服务器响应为空",
icon: "error"
})
} else {
let errorMsg = res.data.msg
if (isEmpty(errorMsg)) {
errorMsg = url + "服务器未返回错误信息"
}
uni.showToast({
title: errorMsg,
// 保证文字长度
icon: "none",
duration: 3000
})
}
}
}
}
}
// 直接返回Response
resolve(res.data)
} else {
uni.showToast({
title: res.statusCode + ":" + res.data.error,
// 保证文字长度
icon: "none",
duration: 3000
})
// 返回错误数据
reject(res.data.error)
}
},
fail: (err) => {
// 关闭显示框
uni.hideLoading();
if (getApp().globalData.isDebug) {
console.log("Http网络请求fail", err)
}
uni.showToast({
title: '请检查网络',
icon: 'error'
})
// 返回错误数据
reject(err)
},
complete: () => {
}
});
})
}
const upload = (options) => {
if (getApp().globalData.isDebug) {
console.log('Http网络请求info', options)
}
if (options.loading == undefined || options.loading == null || options.loading === "") {
options.loading = false
}
if (options.key == undefined || options.key == null || options.key === "") {
options.key = "file"
}
return new Promise((resolve, reject) => {
if (options.loading) {
uni.showLoading({
title: '加载中...',
mask: options.load || true // 默认遮罩出现可以继续操作
});
}
try {
uni.uploadFile({
url: getApp().globalData.baseURL + options.url,
filePath: options.filePath,
name: options.key,
formData: options.data,
success: (res) => {
uni.hideLoading();
// 不知道什么谜之操作 需要手动转换成json对象
res.data = JSON.parse(res.data)
if (getApp().globalData.isDebug) {
console.log('Http网络路径', url)
console.log('Http网络请求结果', JSON.stringify(res.data))
}
if (res.data.success) {
// 成功提示
if (options.toast) {
uni.showToast({
title: res.data.msg,
icon: "none"
})
}
// 成功数据返回
resolve(res.data)
} else {
// 错误提示
uni.showToast({
title: res.data.msg,
icon: "none"
})
// 返回错误数据
reject(res.data)
}
},
fail: (err) => {
uni.hideLoading();
if (getApp().globalData.isDebug) {
console.log(err)
}
uni.showToast({
title: '请检查网络连接',
icon: 'none'
})
// 返回错误数据
reject(err)
},
complete: () => {
}
});
} catch (e) {
uni.hideLoading();
uni.showToast({
title: '服务端异常',
icon: 'none'
})
// 返回错误数据
reject(err)
}
})
}
const httpCookie = (options) => {
if (isDebug) {
console.log('Http网络请求info', options)
}
if (options.loading == undefined || options.loading == null || options.loading === "") {
options.loading = false
}
return new Promise((resolve, reject) => {
if (options.loading) {
uni.showLoading({
title: '加载中...',
mask: options.load || true // 默认遮罩出现可以继续操作
});
}
try {
uni.request({
url: baseURL + options.url,
method: 'POST',
header: {
'content-type': 'application/x-www-form-urlencoded',
// #ifdef MP-WEIXIN
'Cookie': options.cookie
// #endif
},
data: options.data,
success: res => {
uni.hideLoading();
if (isDebug) {
console.log('Http网络路径', url)
console.log('Http网络请求结果', res.data)
}
if (res.data.success) {
// 成功提示
if (options.toast) {
uni.showToast({
title: res.data.msg,
icon: "none"
})
}
// 成功数据返回
resolve(res.data)
} else {
// 错误提示
uni.showToast({
title: res.data.msg,
icon: "none"
})
// 返回错误数据
reject(res.data)
}
},
fail: (err) => {
uni.hideLoading();
if (isDebug) {
console.log(err)
}
uni.showToast({
title: '请检查网络连接',
icon: 'none'
})
// 返回错误数据
reject(err)
},
complete: () => {
}
});
} catch (e) {
uni.hideLoading();
uni.showToast({
title: '服务端异常',
icon: 'none'
})
// 返回错误数据
reject(err)
}
})
}
// 加载其他网站的地址
const httpOtherUrl = (options) => {
if (getApp().globalData.isDebug) {
console.log('Http第三方网络请求info', options)
}
if (options.loading == undefined || options.loading == null || options.loading === "") {
options.loading = false
}
return new Promise((resolve, reject) => {
if (options.loading) {
uni.showLoading({
title: '加载中...',
mask: options.load || true // 默认遮罩出现可以继续操作
});
}
try {
uni.request({
url: options.url,
method: 'GET',
data: options.data,
success: res => {
if (getApp().globalData.isDebug) {
console.log('Http第三方网络请求结果', res.data)
}
uni.hideLoading();
resolve(res.data)
},
fail: (err) => {
if (getApp().globalData.isDebug) {
console.log('Http第三方网络请求错误', err)
}
uni.hideLoading();
reject(err)
uni.showToast({
title: '请检查网络连接',
icon: 'none'
})
},
complete: () => {
}
});
} catch (e) {
uni.hideLoading();
reject(e)
uni.showToast({
title: '服务端异常',
icon: 'none'
})
}
})
}
export {
http,
httpOtherUrl,
baseImgURL,
httpCookie,
upload
}

48
common/PreferenceHelper.js

@ -0,0 +1,48 @@
const write = (keyStr, keyValue) => {
uni.setStorageSync(keyStr, keyValue);
console.log('保存' + keyStr + '成功')
}
const write2 = (keyStr, objectValue) => {
uni.setStorageSync(keyStr, objectValue);
console.log('保存' + keyStr + '成功')
}
const read = (keyStr) => {
let value
try {
value = uni.getStorageSync(keyStr);
if (value) {
console.log(value);
}
} catch (e) {
// error
value = null
console.log("error");
}
return value;
}
const read2 = (keyStr) => {
let value
try {
value = uni.getStorageSync(keyStr);
if (value) {
console.log(value);
}
} catch (e) {
// error
value = null
console.log("error");
}
return value;
}
export {
write,
read,
write2,
read2
}

5
common/PxToRpxOrPxToUpx.js

@ -0,0 +1,5 @@
const px2Upx = (windowWidth, px) => {
return px / (windowWidth / 750)
}
export default px2Upx

11
common/TextUtils.js

@ -0,0 +1,11 @@
const isEmpty = (obj) => {
if (obj == undefined || obj == null || obj === "" || obj === "null") {
return true;
} else {
return false;
}
}
export {
isEmpty
}

201
common/Time.js

@ -0,0 +1,201 @@
const timeText = (time, format) => {
if (format == null) {
format = "yyyy-MM-dd HH:mm:ss"
}
let timeStr = "";
let todayEndTime = getTodayEndTime();
let todayStartTime = getTodayStartTime();
let thisWeekStartTime = getThisWeekStartTime();
console.log("kaishi" + todayStartTime)
console.log("jieshu" + thisWeekStartTime)
// 今天23:59:59:999之后
if (time > todayEndTime) {
// 显示年月日
timeStr = timeFormat(time, format)
} else if (thisWeekStartTime > time) {
// 此周前(本周星期一之前)
let i = format.indexOf(" ");
let formatStyle = format.substring(i + 1);
let formatNew = timeFormat(time, formatStyle);
if (time > todayStartTime - 86399999 && time < todayStartTime) {
// 显示昨天
timeStr = "昨天 " + formatNew;
} else {
timeStr = timeFormat(time, format)
}
} else {
// 显示星期 时分
let i = format.indexOf(" ");
let formatStyle = format.substring(i + 1);
let formatNew = timeFormat(time, formatStyle);
if (todayStartTime - 86399999 > time) {
// 显示星期
timeStr = getWeekStr(time) + " " + formatNew
} else if (time > todayStartTime - 86399999 && time < todayStartTime) {
// 显示昨天
timeStr = "昨天 " + formatNew;
} else {
// 显示
timeStr = formatNew;
}
}
return timeStr
}
/**
* 获取本周开始时间
*/
const getThisWeekStartTime = () => {
let todayEndTime = getTodayEndTime();
var date = new Date();
var weekDays = date.getDay();
var weeks = new Array("星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六");
var week = weeks[weekDays];
return todayEndTime + 1 - weekDays * 86400000;
}
/**
* 获取今天是周几
*/
const getWeekStr = (time) => {
if (time instanceof Date) {
} else {
let temp = new Date(time);
time = temp;
}
let todayEndTime = getTodayEndTime();
var weekDays = time.getDay();
var weeks = new Array("星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六");
var week = weeks[weekDays];
return week;
}
/**
* 获取今天开始时间
*/
const getTodayStartTime = () => {
let startTime = 0;
let myDate = new Date();
let current = myDate.getTime();
try {
let format = timeFormat(myDate);
let split = format.split(" ");
let time = split[1].split(":");
startTime = current - time[0] * 60 * 60 * 1000 - time[1] * 60 * 1000 - time[2] * 1000 - time[3]
} catch (e) {
console.log(e)
}
return startTime;
}
/**
* 获取今天结束时间
*/
const getTodayEndTime = () => {
let endTime = 0;
let myDate = new Date();
let current = myDate.getTime();
try {
let format = timeFormat(myDate);
let split = format.split(" ");
let time = split[1].split(":");
let startTime = current - time[0] * 60 * 60 * 1000 - time[1] * 60 * 1000 - time[2] * 1000 - time[3]
endTime = startTime + 86399999;
} catch (e) {
console.log(e)
}
return endTime;
}
/**
* 格式化时间
* 支持时间戳 以及 date类型
*/
const timeFormat = (date, formoat) => {
if (date == undefined || date == null) {
return "";
}
if (date instanceof Date) {
console.log("正确")
} else {
let temp = new Date(Number(date));
date = temp;
}
let fmt = formoat;
if (fmt == null) {
fmt = "yyyy-MM-dd HH:mm:ss:SSS"
}
let ret;
const opt = {
"y+": date.getFullYear().toString(), // 年
"M+": (date.getMonth() + 1).toString(), // 月
"d+": date.getDate().toString(), // 日
"H+": date.getHours().toString(), // 时
"m+": date.getMinutes().toString(), // 分
"s+": date.getSeconds().toString(), // 秒
"S+": date.getMilliseconds().toString()
};
for (let k in opt) {
ret = new RegExp("(" + k + ")").exec(fmt);
if (ret) {
fmt = fmt.replace(ret[1], (ret[1].length == 1) ? (opt[k]) : (opt[k].padStart(ret[1].length, "0")))
};
};
return fmt;
}
const currentMillions = () => {
let current = new Date().getTime();
return current;
}
export {
timeText,
timeFormat,
currentMillions,
getWeekStr,
getTodayEndTime
}

8
common/Toast.js

@ -0,0 +1,8 @@
const toast = (msg) => {
uni.showToast({
title: msg,
icon: 'none'
})
}
export default toast

12
common/intent.js

@ -0,0 +1,12 @@
const putWEBExtra = (url) => {
return encodeURIComponent(url)
}
const getWEBExtra = (options) => {
return decodeURIComponent(options.url)
}
export {
putWEBExtra,
getWEBExtra
}

353
common/utils.js

@ -0,0 +1,353 @@
/**
* @说明工具集
* @作者陈万照
* @公司山东标梵互动技术有限公司
* @官网http://biaofun.com/
* @版本v1.0.0
* @时间2020年4月28日11:28:13
*/
export default {
/**
* 同步 try catch 的进一步封装处理
* 使用方法
* let [err, res] = await this.$utils.asyncTasks(Promise函数);
* if(res) 成功
* if(err) 失败
*/
asyncTasks(promise) {
return promise.then(data => {
return [null, data];
}).catch(err => [err]);
},
/**
* 精确判断数据是否是 Object 类型
* @param {Any} val 要判断的数据
* @returns {Boolean} truefalse不是
*/
isObject(val) {
return Object.prototype.toString.call(val) === '[object Object]' && val !== null && val !== undefined;
},
/**
* 判断数据是否是 Array 类型
* @param {Any} val 要判断的数据
* @returns {Boolean} truefalse不是
*/
isArray(val) {
return Object.prototype.toString.call(val) === '[object Array]';
},
/**
* 判断数据是否是 String 类型
* @param {Any} val 要判断的数据
* @returns {Boolean} truefalse不是
*/
isString(val) {
return Object.prototype.toString.call(val) === '[object String]';
},
/**
* 精确判断数据是否是 Date 类型
* @param {Any} val 要判断的数据
* @returns {Boolean} truefalse不是
*/
isDate(val) {
return Object.prototype.toString.call(val) === '[object Date]';
},
/**
* 精确判断数据是否是 Function 类型
* @param {Any} val 要判断的数据
* @returns {Boolean} truefalse不是
*/
isFunction(val) {
return Object.prototype.toString.call(val) === '[object Function]';
},
/**
* 精确判断数据是否是 Number 类型
* @param {Any} val 要判断的数据
* @returns {Boolean} truefalse不是
*/
isNumber(val) {
return Object.prototype.toString.call(val) === '[object Number]';
},
/**
* 精确判断数据是否是 Boolean 类型
* @param {Any} val 要判断的数据
* @returns {Boolean} truefalse不是
*/
isBoolean(val) {
return Object.prototype.toString.call(val) === '[object Boolean]';
},
/**
* 判断 URL 是否是绝对 URL
* @param {String} url 要判断的 URL
* @return {Boolean} true是绝对URLfalse不是绝对URL
*/
isAbsoluteURL(url) {
// 如果 URL 以 “<scheme>://” 或 “//”(协议相对URL)开头,则认为它是绝对的
// RFC 3986 将方案名称定义为以字母开头的字符序列,然后是字母,数字,加号,句点或连字符的任意组合
return /^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(url);
},
/**
* 合并 baseURL 和相对 URL 成一个完整的 URL
* @param {String} baseURL baseURL
* @param {String} relativeURL 相对 URL
* @returns {String} 返回组合后的完整 URL
*/
combineURLs(baseURL, relativeURL) {
return relativeURL && this.isString(relativeURL) && this.isString(baseURL) ? baseURL.replace(/\/+$/, '') + '/' +
relativeURL.replace(/^\/+/, '') : baseURL;
},
/**
* 深度合并对象只支持合并两个对象该方法不会改变原有的对象
* @param {Object} FirstOBJ 第一个对象
* @param {Object} SecondOBJ 第二个对象
* @return {Object} 返回深度合并后的对象
*/
deepMargeObject(FirstOBJ, SecondOBJ) {
let ResultOBJ = {};
for (let key in FirstOBJ) {
ResultOBJ[key] = ResultOBJ[key] && ResultOBJ[key].toString() === "[object Object]" ? this.deepMargeObject(
ResultOBJ[
key], FirstOBJ[key]) : ResultOBJ[key] = FirstOBJ[key];
}
for (let key in SecondOBJ) {
ResultOBJ[key] = ResultOBJ[key] && ResultOBJ[key].toString() === "[object Object]" ? this.deepMargeObject(
ResultOBJ[
key], SecondOBJ[key]) : ResultOBJ[key] = SecondOBJ[key];
}
return ResultOBJ;
},
/**
* 生成指定长度的随机字符串
* @param {Number} min 最小程度
* @param {Number} max 最大长度
* @return {String} 返回生成的字符串
*/
randomString(min, max) {
let returnStr = "",
range = (max ? Math.round(Math.random() * (max - min)) + min : min),
arr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
'k', 'l',
'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
'H', 'I',
'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
];
for (let i = 0; i < range; i++) {
let index = Math.round(Math.random() * (arr.length - 1));
returnStr += arr[index];
}
return returnStr;
},
/**
* 格式化日期
* @param {Date|String} date 日期或日期字符串
*/
formatDate(date) {
let YYYY = null;
let M = null;
let MM = null;
let D = null;
let DD = null;
let h = null;
let hh = null;
let m = null;
let mm = null;
let s = null;
let ss = null;
let ms = null;
let ms2 = null;
let ms3 = null;
let ms4 = null;
let dt = null;
// 如果 date 是 String 类型
if (date && this.isString(date)) {
// 真机运行时,如果直接用 new Date('YYYY-MM-DD hh:mm:ss') 会报 Invalid Date 错误,所以采用下面的方式创建日期
let dtArr = date.replace(/\//g, '.').replace(/-/g, '.').replace(/:/g, '.').replace(/T/g, ' ').replace(' ',
'.').replace(
'Z', '').split('.');
let year = 2020;
let month = 12;
let day = 18;
let hour = 0;
let minute = 0;
let second = 0;
let millisecond = 0;
// 年
if (dtArr.length > 0 && !isNaN(dtArr[0])) {
year = parseInt(dtArr[0]);
}
// 月
if (dtArr.length > 1 && !isNaN(dtArr[1])) {
month = parseInt(dtArr[1]);
}
// 日
if (dtArr.length > 2 && !isNaN(dtArr[2])) {
day = parseInt(dtArr[2]);
}
// 时
if (dtArr.length > 3 && !isNaN(dtArr[3])) {
hour = parseInt(dtArr[3]);
}
// 分
if (dtArr.length > 4 && !isNaN(dtArr[4])) {
minute = parseInt(dtArr[4]);
}
// 秒
if (dtArr.length > 5 && !isNaN(dtArr[5])) {
second = parseInt(dtArr[5]);
}
// 毫秒
if (dtArr.length > 6 && !isNaN(dtArr[6])) {
millisecond = parseInt(dtArr[6]);
}
date = new Date(year, month - 1, day, hour, minute, second, millisecond);
}
// 如果 date 是 Date 类型
if (date && this.isDate(date)) {
YYYY = date.getFullYear();
M = date.getMonth() + 1;
MM = M >= 10 ? M : '0' + M;
D = date.getDate();
DD = D >= 10 ? D : '0' + D;
h = date.getHours();
hh = h >= 10 ? h : '0' + h;
m = date.getMinutes();
mm = m >= 10 ? m : '0' + m;
s = date.getSeconds();
ss = s >= 10 ? s : '0' + s;
ms = date.getMilliseconds();
ms2 = ms;
ms3 = ms;
ms4 = ms;
if (ms < 10) {
ms2 = '0' + ms;
ms3 = '00' + ms;
ms4 = '000' + ms;
} else if (ms < 100) {
ms3 = '0' + ms;
ms4 = '00' + ms;
} else {
ms4 = '0' + ms;
}
}
// 返回的数据对象
let result = {
YYYY: YYYY,
MM: MM,
M: M,
DD: DD,
D: D,
hh: hh,
h: h,
mm: mm,
m: m,
ss: ss,
s: s,
ms: ms,
ms2: ms2,
ms3: ms3,
ms4: ms4,
dt: date,
f1: `${YYYY}-${MM}-${DD}`,
f2: `${YYYY}${M}${D}`,
f3: `${YYYY}-${M}-${D} ${hh}:${mm}`,
f4: `${h}:${m}:${s}`,
f5: `${MM}-${DD}`,
f6: `${YYYY}-${MM}`,
f7: `${YYYY}${M}`,
f8: `${h}:${m}`,
f9: `${M}${D}`,
notes: 'YYYY(年),MM(月,补0),M(月,不补0),DD(日,补0),D(日,不补0),hh(时,补0),h(时,不补0),mm(分,补0),m(分,不补0),ss(秒,补0),s(秒,不补0),ms(毫秒,不补0),ms2(毫秒,补0到2位),ms3(毫秒,补0到3位),ms4(毫秒,补0到4位),其余的f1,f2,... 看格式就知道了!'
};
return result;
},
/**
* 数字转中文
* @param {Number} num 数字
*/
numberToChinese(num) {
if (!/^\d*(\.\d*)?$/.test(num)) return "Number is wrong!";
let AA = new Array("零", "一", "二", "三", "四", "五", "六", "七", "八", "九");
let BB = new Array("", "十", "百", "千", "万", "亿", "点", "");
let a = ("" + num).replace(/(^0*)/g, "").split("."),
k = 0,
re = "";
for (let i = a[0].length - 1; i >= 0; i--) {
switch (k) {
case 0:
re = BB[7] + re;
break;
case 4:
if (!new RegExp("0{4}\\d{" + (a[0].length - i - 1) + "}$").test(a[0]))
re = BB[4] + re;
break;
case 8:
re = BB[5] + re;
BB[7] = BB[5];
k = 0;
break;
}
if (k % 4 == 2 && a[0].charAt(i + 2) != 0 && a[0].charAt(i + 1) == 0) re = AA[0] + re;
if (a[0].charAt(i) != 0) re = AA[a[0].charAt(i)] + BB[k % 4] + re;
k++;
}
if (a.length > 1) //加上小数部分(如果有小数部分)
{
re += BB[6];
for (let i = 0; i < a[1].length; i++) re += AA[a[1].charAt(i)];
}
return re;
},
/**
* 计算两个经纬度点之间的距离
* @param {Number} lng1 第一个点的经度
* @param {Number} lat1 第一个点的纬度
* @param {Number} lng2 第二个点的经度
* @param {Number} lat2 第二个点的纬度
*/
calcDistance(lng1, lat1, lng2, lat2) {
var radLat1 = lat1 * Math.PI / 180.0;
var radLat2 = lat2 * Math.PI / 180.0;
var a = radLat1 - radLat2;
var b = lng1 * Math.PI / 180.0 - lng2 * Math.PI / 180.0;
var s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) +
Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)));
s = s * 6378.137; // EARTH_RADIUS;
s = Math.round(s * 10000) / 10000;
return s;
},
/**
* 获取数组最小值的下标
* @param {Array} arr 数组
*/
getArrayMixValueIndex(arrar) {
let min = arrar[0];
let index = 0;
for (let i = 0; i < arrar.length; i++) {
if (min > arrar[i]) {
min = arrar[i];
index = i;
}
}
return index;
}
}

170
common/wxAuthLogin.js

@ -0,0 +1,170 @@
import {
isEmpty
} from './TextUtils.js'
const wxAuthLogin = () => {
// 判断是否已经登陆
if (getApp().globalData.isLogin) {
return new Promise((resolve, reject) => {
resolve(getApp().globalData.sysUserSid)
})
}
return new Promise((resolve, reject) => {
// 授权登录
wx.login({
success: function(res) {
if (res.code) {
if (getApp().globalData.isDebug) {
console.log('Http网络请求info', {
"wxCode": res.code
})
}
uni.showLoading({
title: '加载中...',
mask: true
});
uni.request({
// 组装请求地址
url: getApp().globalData.wxAuthLoginURL,
// 请求方式 GET POST
method: "GET",
header: {
// 传参方式
'content-type': "application/x-www-form-urlencoded"
},
// 具体参数
data: {
"wxCode": res.code
},
success: res => {
// 关闭显示框
uni.hideLoading();
console.log(res)
if (getApp().globalData.isDebug) {
console.log('Http网络路径', getApp().globalData.wxAuthLoginURL)
console.log('Http网络请求结果', JSON.parse(JSON.stringify(
res.data)))
}
if (res.statusCode == 200) {
// 下面是接口返回的数据
if (!res.data.success) {
if ("A01001" == res.data.code || "A01002" == res.data.code ) {
// 绑定手机号
uni.navigateTo({
url: '../index/BindPhone?sysUserWxAuthSid=' + res.data.data
})
// reject("绑定手机号")
}
else {
// 错误提示
if (isEmpty(res.data)) {
// 未成功获取到服务器返回的json
uni.showToast({
// 不能超过7个字
title: "服务器响应为空",
icon: "error"
})
reject("服务器响应为空")
} else {
let errorMsg = res.data.msg
if (isEmpty(errorMsg)) {
errorMsg = url+"服务器未返回错误信息"
}
uni.showToast({
title: errorMsg,
// 保证文字长度
icon: "none",
duration: 3000
})
}
reject(errorMsg)
}
return
}
// 保存
uni.setStorageSync("sysUserSid", res.data.data.sysUserSid);
uni.setStorageSync("isLogin", res.data.data.isLogin);
getApp().globalData.isLogin = res.data.data.isLogin
getApp().globalData.sysUserSid = res.data.data.sysUserSid
getApp().globalData.token = res.data.data.token
// 直接返回Response
resolve(getApp().globalData.sysUserSid)
} else {
uni.showToast({
title: res.statusCode + ":" + res
.errMsg,
// 保证文字长度
icon: "none",
duration: 3000
})
reject(res.errMsg)
}
},
fail: (err) => {
// 关闭显示框
uni.hideLoading();
if (getApp().globalData.isDebug) {
console.log("Http网络请求fail", err)
}
uni.showToast({
title: '请检查网络',
icon: 'error'
})
reject(err)
},
complete: () => {
}
});
} else {
uni.showToast({
title: "授权登录获取code失败:" + res.errMsg,
icon: 'none'
})
reject(res.errMsg)
}
},
fail: function(res) {
uni.showToast({
title: "授权登录失败:" + res.errMsg,
icon: 'none'
})
reject(res.errMsg)
}
});
})
}
export default wxAuthLogin

73
common/wxSilentLogin.js

@ -0,0 +1,73 @@
// 静默登录
import {
isEmpty
} from './TextUtils.js'
const wxSilentLogin = () => {
return new Promise((resolve, reject) => {
wx.login({
success: function(res) {
if (res.code) {
if (getApp().globalData.isDebug) {
console.log('Http网络请求信息', {
"wxCode": res.code
})
}
uni.request({
// 组装请求地址
url: getApp().globalData.wxSilentLoginURL,
// 请求方式 GET POST
method: "GET",
header: {
// 传参方式
'content-type': "application/x-www-form-urlencoded"
},
// 具体参数
data: {
"wxCode": res.code
},
success: res => {
console.log(res)
if (getApp().globalData.isDebug) {
console.log('Http网络路径', getApp().globalData.wxSilentLoginURL)
}
if (res.statusCode == 200) {
if (!res.data.success) {
return
}
else
{
uni.setStorageSync("sysUserSid", res.data.data.sysUserSid);
uni.setStorageSync("token", res.data.data.token);
uni.setStorageSync("isLogin", res.data.data.isLogin);
getApp().globalData.isLogin = res.data.data.isLogin
getApp().globalData.sysUserSid = res.data.data.sysUserSid
resolve(res.data.data.sysUserSid)
}
}else
{
getApp().globalData.isLogin = false
}
},
fail: (err) => {
if (getApp().globalData.isDebug) {
console.log("Http网络请求fail", err)
}
},
complete: () => {
}
});
}
},
fail: function(res) {
uni.showToast({
title: "静默登录失败:" + res.errMsg,
icon: 'none'
})
reject(res.errMsg)
}
});
})
}
export default wxSilentLogin

76
components/EditText/EditText.vue

@ -0,0 +1,76 @@
<template>
<view style="display: flex;flex-direction: row;align-items: center;height:100rpx;">
<input v-if="(inputType=='text')" type="text"
style="margin-right: 30rpx;flex: 1;padding-left: 36rpx;font-size: 30rpx;" :placeholder="hint"
@input="textChange" :value="value" :maxlength="maxlength"></input>
<input v-if="(inputType=='number')" type="number"
style="margin-right: 30rpx;flex: 1;padding-left: 36rpx;font-size: 30rpx;" :placeholder="hint"
@input="textChange" :value="value" :maxlength="maxlength"></input>
<input v-if="(inputType=='idcard')" type="idcard"
style="margin-right: 30rpx;flex: 1;padding-left: 36rpx;font-size: 30rpx;" :placeholder="hint"
@input="textChange" :value="value" :maxlength="maxlength"></input>
<input v-if="(inputType=='digit')" type="digit"
style="margin-right: 30rpx;flex: 1;padding-left: 36rpx;font-size: 30rpx;" :placeholder="hint"
@input="textChange" :value="value" :maxlength="maxlength"></input>
<image v-if="isShowDelete" style="width: 50rpx;height: 50rpx;margin-right: 20rpx;flex-shrink: 0;"
src="../../static/img/public/shanchu1.png" mode="aspectFill" @click="clickRight()">
</image>
</view>
</template>
<script>
export default {
props: {
hint: {
type: String,
default: ""
},
text: {
type: String,
default: ""
},
inputType: {
type: String,
default: "text"
},
maxlength: {
type: Number,
default: 99999999
},
},
watch: {
text: {
handler(t, oldT) {
this.value = t
this.$emit("onTextChange", this.value)
this.isShowDelete = !this.IsEmpty(this.value)
},
immediate: true,
}
},
data() {
return {
isShowDelete: false,
value: ""
};
},
methods: {
textChange(e) {
this.isShowDelete = !this.IsEmpty(e.detail.value)
this.$emit("onTextChange", e.detail.value)
this.value = e.detail.value
},
clickRight() {
this.value = ""
this.$emit("onTextChange", this.value)
this.isShowDelete = !this.IsEmpty(this.value)
}
}
}
</script>
<style>
</style>

51
components/PublishItem/PublishItem.vue

@ -0,0 +1,51 @@
<template>
<view
style="display: flex;flex-direction: row;box-sizing: border-box;width: 100%;align-items: center;height: 100rpx;padding: 0rpx 15rpx; background-color: #FFFFFF;">
<image style="width: 45rpx;height: 45rpx;" src="../../static/custom-icon/line.png" mode="aspectFill">
</image>
<text style="flex-shrink: 0;font-size: 30rpx; color: #101010;">{{leftText}}</text>
<text
style="flex: 1;padding-left: 30rpx;padding-right: 30rpx;color: #898989 ;font-size: 26rpx;text-align: center;">{{middleText}}</text>
<text v-if="isShowRight" style="flex-shrink: 0;font-size: 26rpx;color: #080808;"
@click="clickRight()">{{rightText}}</text>
<image v-if="isShowRight" style="width: 30rpx;height: 30rpx;margin-right: 20rpx;"
src="../../static/img/public/more.png" mode="aspectFill" @click="clickRight()">
</image>
</view>
</template>
<script>
export default {
props: {
leftText: {
type: String,
default: ""
},
middleText: {
type: String,
default: ""
},
rightText: {
type: String,
default: ""
},
isShowRight: {
type: Boolean,
default: true
}
},
data() {
return {
clickRight() {
this.$emit("rightClick", this.leftText)
}
};
}
}
</script>
<style>
</style>

585
components/RefreshView/RefreshView.vue

@ -0,0 +1,585 @@
<template>
<view :style="{'background-color':pageBg}">
<view v-if="!hideTop" :style="{'height':totalHeightPx,'background-color': barColor,'box-sizing': 'border-box','width':'100%',
'z-index':'9900','position':'fixed','top':'--window-top','left':'0'}">
<view :style="{'height':statusBarHeightPx}">
</view>
<view
:style="{'height':navHeightPx,'width':navWidthPx,'display': 'flex','flex-direction':'row','box-sizing': 'border-box','justify-content': 'space-between'}">
<view
:style="{'height':navHeightPx,'display': 'flex','flex-direction':'row','box-sizing': 'border-box'}">
<image :style="{'height':navHeightPx,'width':iconPx,'flex-shrink':0}" mode="aspectFit"
:src="backIcon" @click="clickBack"></image>
<text class="titleStyle"
:style="{'height':navHeightPx,'line-height':navHeightPx,'font-size':'33rpx','color':'#FFFFFF'}">{{text}}</text>
</view>
<view
:style="{'height':navHeightPx,'display': 'flex','flex-direction':'row','box-sizing': 'border-box'}">
<view v-if="useTitleLeftBtn!=0"
:style="{'height':navHeightPx,'display': 'flex','flex-direction':'row','box-sizing': 'border-box'}"
@click="clickLeftBtn">
<image v-if="useTitleLeftBtn == 2 && dropLeftList.length == 0"
:style="{'height':navHeightPx,'width':navHeightPx}" mode="aspectFit"
:src="titleLeftBtnSource"></image>
<text v-if="useTitleLeftBtn == 1 && dropLeftList.length == 0"
:style="{'height':navHeightPx,'line-height':navHeightPx,'font-size':'28rpx','color':'#FFFFFF','padding-left':'20rpx','padding-right':'20rpx'}">{{titleLeftBtnSource}}</text>
<drop-item ref="dropview" :candidates="dropLeftList" :btnType="useTitleLeftBtn"
:btnSource="titleLeftBtnSource" @onselect="selectDrop"></drop-item>
</view>
<view v-if="useTitleRightBtn!=0"
:style="{'height':navHeightPx,'display': 'flex','flex-direction':'row','box-sizing': 'border-box'}"
@click="clickRightBtn">
<image v-if="useTitleRightBtn==2 && dropRightList.length == 0"
:style="{'height':navHeightPx,'width':navHeightPx}" mode="aspectFit"
:src="titleRightBtnSource"></image>
<text v-if="useTitleRightBtn==1 && dropRightList.length == 0"
:style="{'height':navHeightPx,'line-height':navHeightPx,'font-size':'28rpx','color':'#FFFFFF','padding-left':'20rpx','padding-right':'20rpx'}">{{titleRightBtnSource}}</text>
<drop-item ref="dropview" :candidates="dropRightList" :btnType="useTitleRightBtn"
:btnSource="titleRightBtnSource" @onselect="selectDrop"></drop-item>
</view>
</view>
</view>
</view>
<view class="mescroll-body"
:style="{'minHeight':minHeight, 'padding-top': totalHeightPx, 'padding-bottom': padBottom, 'padding-bottom': padBottomConstant, 'padding-bottom': padBottomEnv }"
@touchstart="touchstartEvent" @touchmove="touchmoveEvent" @touchend="touchendEvent"
@touchcancel="touchendEvent">
<view class="mescroll-body-content" :style="{ transform: translateY, transition: transition }">
<!-- 下拉加载区域 (支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-down组件实现)-->
<!-- <mescroll-down :option="mescroll.optDown" :type="downLoadType"></mescroll-down> -->
<view v-if="useDownScroll" class="mescroll-downwarp"
:style="{'background-color':mescroll.optDown.bgColor,'color':mescroll.optDown.textColor}">
<view class="downwarp-content">
<image class="downwarp-slogan" src="../../static/img/public/mescroll-slogan.png"
mode="widthFix" />
<view v-if="isDownLoading" class="downwarp-loading mescroll-rotate"></view>
<view v-else class="downwarp-progress" :style="{'transform':downRotate}"></view>
</view>
</view>
<sl-filter id="header" ref="slLilter" v-if="menuList.length!=0" :color="titleColor" :themeColor="themeColor"
:menuList.sync="menuList" @result="result" ></sl-filter>
<!-- 列表内容 -->
<slot></slot>
<!-- 空布局 -->
<mescroll-empty v-if="isShowEmpty" :option="mescroll.optUp.empty" @emptyclick="emptyClick">
</mescroll-empty>
<!-- 上拉加载区域 (下拉刷新时不显示, 支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-up组件实现)-->
<!-- <mescroll-up v-if="mescroll.optUp.use && !isDownLoading" :option="mescroll.optUp" :type="upLoadType"></mescroll-up> -->
<view v-if="useUpScroll && !isDownLoading" class="mescroll-upwarp"
:style="{'background-color':mescroll.optUp.bgColor,'color':mescroll.optUp.textColor}">
<!-- 加载中 (此处不能用v-if,否则android小程序快速上拉可能会不断触发上拉回调) -->
<view v-if="upLoadType===1">
<view class="upwarp-progress mescroll-rotate"
:style="{'border-color':mescroll.optUp.textColor}"></view>
<view class="upwarp-tip">{{ mescroll.optUp.textLoading }}</view>
</view>
<!-- 无数据 -->
<view v-if="upLoadType===2" class="upwarp-nodata">{{ mescroll.optUp.textNoMore }}</view>
</view>
</view>
<!-- 回到顶部按钮 (fixed元素需写在transform外面,防止降级为absolute)-->
<mescroll-top v-model="isShowToTop" :option="mescroll.optUp.toTop" @click="toTopClick"></mescroll-top>
</view>
</view>
</template>
<script>
import MeScroll from '../mescroll-uni/mescroll-uni.js';
import MescrollEmpty from '../mescroll-uni/components/mescroll-empty.vue';
import MescrollTop from '../mescroll-uni/components/mescroll-top.vue';
import GlobalOption from '../mescroll-uni/mescroll-uni-option.js';
import RefreshOption from './refresh-option.js';
import slFilter from '@/components/sl-filter/sl-filter.vue';
export default {
components: {
MescrollEmpty,
MescrollTop,
slFilter
},
data() {
return {
mescroll: null, // mescroll
downHight: 0, //:
downLoadType: 4, // inOffset1 outOffset2 showLoading3 endDownScroll4
upLoadType: 0, // 0loading1loading2
isShowEmpty: false, //
isShowToTop: false, //
windowHeight: 0, // 使
statusBarH: 0, //
statusBarHeight: "",
navHeight: "",
navWidth: "",
totalHeight: "",
statusBarHeightPx: "",
navHeightPx: "",
navWidthPx: "",
totalHeightPx: "",
bg: "#2fa1f0",
totalHeightUpx: "",
backIcon: "",
iconPx: "",
LeftPos: '0px',
themeColor: '#000000',
titleColor: '#fd6d2a',
hideTop: false
};
},
props: {
down: Object, //
up: Object, //
top: [String, Number], // (20, "20rpx", "20px", "20%", rpx, windowHeight)
topbar: Boolean, // top, false (使:,)
bottom: [String, Number], // (20, "20rpx", "20px", "20%", rpx, windowHeight)
safearea: Boolean, // bottom, false (iPhoneX使)
height: [String, Number], // mescroll,windowHeight,使
text: {
type: String,
default: ""
},
pageBg: {
type: String,
default: "#FFFFFF"
},
barColor: {
type: String,
default: "#2fa1f0"
},
hasBack: {
type: Boolean,
default: true
},
isShareIn: {
type: Boolean,
default: false
},
isInterceptBack: {
type: Boolean,
default: false
},
useUpScroll: {
type: Boolean,
default: true
},
useDownScroll: {
type: Boolean,
default: true
},
useTitleLeftBtn: {
type: String,
// 0使 1使 2使
default: "0"
},
useTitleRightBtn: {
type: String,
// 0使 1使 2使
default: "0"
},
titleLeftBtnSource: {
type: String,
default: ""
},
titleRightBtnSource: {
type: String,
default: ""
},
dropLeftList: {
type: Array,
default () {
return []
}
},
dropRightList: {
type: Array,
default () {
return []
}
},
dropShow: {
type: Boolean,
default: true
},
menuList: {
type: Array,
default () {
return []
}
}
},
computed: {
// mescroll,windowHeight,使
minHeight() {
return this.toPx(this.height || '100%') + 'px'
},
// (px)
numTop() {
return this.toPx(this.top) + (this.topbar ? this.statusBarH : 0);
},
padTop() {
return this.numTop + 'px';
},
// (px)
numBottom() {
return this.toPx(this.bottom);
},
padBottom() {
return this.numBottom + 'px';
},
padBottomConstant() {
return this.isSafearea ? 'calc(' + this.padBottom + ' + constant(safe-area-inset-bottom))' : this
.padBottom;
},
padBottomEnv() {
return this.isSafearea ? 'calc(' + this.padBottom + ' + env(safe-area-inset-bottom))' : this.padBottom;
},
//
isDownReset() {
return this.downLoadType === 3 || this.downLoadType === 4;
},
//
transition() {
return this.isDownReset ? 'transform 300ms' : this.downTransition;
},
translateY() {
return this.downHight > 0 ? 'translateY(' + this.downHight + 'px)' :
''; // transform使fixed,fixedmescroll
},
//
isDownLoading() {
return this.downLoadType === 3
},
//
downRotate() {
return this.downLoadType === 2 ? 'rotate(180deg)' : 'rotate(0deg)'
}
},
methods: {
refreshTitle(obj) {
this.$refs.slLilter.setTitle(obj)
},
reset(){
this.$refs.slLilter.resetSelectToDefault(function(e) {
callback(e);
});
},
result(val) {
console.log("val:", JSON.stringify(val));
this.$emit('result', JSON.stringify(val))
},
selectDrop(index, data) {
let left = this.arrSame(data, this.dropLeftList);
this.$emit("drop", index, left, (left ? this.dropLeftList[index] : this.dropRightList[index]))
},
clickLeftBtn() {
this.$emit("leftBtn", "左侧按钮")
},
clickRightBtn() {
this.$emit("rightBtn", "右侧按钮")
},
clickBack() {
if (this.hasBack) {
if (getCurrentPages().length > 1) {
if (this.isInterceptBack) {
this.$emit("backClick")
} else {
this.Back()
}
} else {
if (RefreshOption.homePageIsTabBar) {
uni.switchTab({
url: RefreshOption.homePagePath
})
} else {
uni.navigateTo({
url: RefreshOption.homePagePath
})
}
}
}
},
//number,rpx,upx,px,% --> px
toPx(num) {
if (typeof num === 'string') {
if (num.indexOf('px') !== -1) {
if (num.indexOf('rpx') !== -1) {
// "10rpx"
num = num.replace('rpx', '');
} else if (num.indexOf('upx') !== -1) {
// "10upx"
num = num.replace('upx', '');
} else {
// "10px"
return Number(num.replace('px', ''));
}
} else if (num.indexOf('%') !== -1) {
// ,windowHeight,"10%"windowHeight10%
let rate = Number(num.replace('%', '')) / 100;
return this.windowHeight * rate;
}
}
return num ? uni.upx2px(Number(num)) : 0;
},
//touchstart,
touchstartEvent(e) {
if (this.$props.useDownScroll) {
this.mescroll.touchstartEvent(e);
}
},
//touchmove,
touchmoveEvent(e) {
if (this.$props.useDownScroll) {
this.mescroll.touchmoveEvent(e);
}
},
//touchend,
touchendEvent(e) {
if (this.$props.useDownScroll) {
this.mescroll.touchendEvent(e);
}
},
//
emptyClick() {
this.$emit('emptyclick', this.mescroll);
},
//
toTopClick() {
this.mescroll.scrollTo(0, this.mescroll.optUp.toTop.duration); //
this.$emit('topclick', this.mescroll); //
},
refreshFinished(currentPageListSize) {
if (currentPageListSize == undefined || currentPageListSize == null || currentPageListSize === "") {
this.mescroll.endSuccess();
} else {
this.mescroll.endSuccess(currentPageListSize);
}
},
refreshError() {
this.mescroll.endErr();
},
resetPageOne() {
this.mescroll.resetUpScroll()
//
this.toTopClick()
},
closeDrop() {
//
if (this.$refs.dropview != undefined)
this.$refs.dropview.closeDropItem()
},
/**
* 验证两个object 是否相同
* @param {Object} obj [需要进行验证的数据1]
* @param {Object} newObj [需要进行验证的数据2]
*/
objSame(obj, newObj) {
let bol = true;
if (Object.keys(obj).length != Object.keys(newObj).length) {
return false;
}
for (let key in obj) {
if (obj[key] instanceof Object) {
bol = this.objSame(obj[key], newObj[key]);
if (!bol) {
break;
}
} else if (obj[key] instanceof Array) {
bol = arrSame(obj[key], newObj[key])
if (!bol) {
break;
}
} else if (obj[key] != newObj[key]) {
bol = false;
break;
}
}
return bol
},
/**
* 验证两个数组是否相同
* @param {Array} arr [需要进行验证的数据1]
* @param {Array} newArr [需要进行验证的数据2]
*/
arrSame(arr, newArr) {
let bol = true;
if (arr.length != newArr.length) {
return false;
}
for (let i = 0, n = arr.length; i < n; i++) {
if (arr[i] instanceof Array) {
bol = arrSame(arr[i], newArr[i])
if (!bol) {
break;
}
} else if (arr[i] instanceof Object) {
bol = this.objSame(arr[i], newArr[i])
if (!bol) {
break;
}
} else if (arr[i] != newArr[i]) {
bol = false;
break;
}
}
return bol;
}
},
// 使createdmescroll; mountedcssH5
created() {
this.navWidth = getApp().globalData.navWidth
this.navHeight = getApp().globalData.navHeight
this.totalHeight = getApp().globalData.totalHeight
this.statusBarHeight = getApp().globalData.statusBarHeight
this.navWidthPx = getApp().globalData.navWidthPx
this.navHeightPx = getApp().globalData.navHeightPx
this.totalHeightPx = getApp().globalData.totalHeightPx
this.statusBarHeightPx = getApp().globalData.statusBarHeightPx
this.totalHeightUpx = getApp().globalData.totalHeightUpx
if (this.$props.hasBack) {
this.backIcon = getCurrentPages().length > 1 ? RefreshOption.backIcon : RefreshOption.homeIcon
this.iconPx = this.navHeightPx
} else {
this.backIcon = '../../static/custom-icon/no-back.png'
this.iconPx = this.navHeight / 8 * 3 + 'px'
}
let vm = this;
let diyOption = {
//
down: {
inOffset(mescroll) {
vm.downLoadType = 1; // offset (mescroll,)
},
outOffset(mescroll) {
vm.downLoadType = 2; // offset (mescroll,)
},
onMoving(mescroll, rate, downHight) {
// ,;
vm.downHight = downHight; // (mescroll,)
},
showLoading(mescroll, downHight) {
vm.downLoadType = 3; // (mescroll,)
vm.downHight = downHight; // (mescroll,)
},
endDownScroll(mescroll) {
vm.downLoadType = 4; // (mescroll,)
vm.downHight = 0; // (mescroll,)
},
//
callback: function(mescroll) {
vm.mescroll.resetUpScroll()
}
},
//
up: {
//
showLoading() {
vm.upLoadType = 1;
},
//
showNoMore() {
vm.upLoadType = 2;
},
//
hideUpScroll() {
vm.upLoadType = 0;
},
//
empty: {
onShow(isShow) {
//
vm.isShowEmpty = isShow;
}
},
//
toTop: {
onShow(isShow) {
//
vm.isShowToTop = isShow;
}
},
//
callback: function(mescroll) {
vm.$emit('refresh', mescroll);
}
}
};
MeScroll.extend(diyOption, GlobalOption); //
let myOption = JSON.parse(
JSON.stringify({
down: vm.down,
up: vm.up
})
); // ,props
MeScroll.extend(myOption, diyOption); //
// MeScroll
vm.mescroll = new MeScroll(myOption, true); // true,body
// initmescroll
vm.$emit('init', vm.mescroll);
//
const sys = uni.getSystemInfoSync();
if (sys.windowHeight) vm.windowHeight = sys.windowHeight;
if (sys.statusBarHeight) vm.statusBarH = sys.statusBarHeight;
// 使downbottomOffset
vm.mescroll.setBodyHeight(sys.windowHeight);
// mescroll-bodyAndroid,mescroll-uni"disableScroll":true,
// #ifdef MP
if (sys.platform == "android") vm.downTransition = 'transform 200ms'
// #endif
// 使pagescroll,scrollTo
vm.mescroll.resetScrollTo((y, t) => {
uni.pageScrollTo({
scrollTop: y,
duration: t
})
});
// up.toTop.safearea,vuesafearea
if (sys.platform == "ios") {
vm.isSafearea = vm.safearea;
if (vm.up && vm.up.toTop && vm.up.toTop.safearea != null) {} else {
vm.mescroll.optUp.toTop.safearea = vm.safearea;
}
} else {
vm.isSafearea = false
vm.mescroll.optUp.toTop.safearea = false
}
}
};
</script>
<style lang="scss">
@import "@/components/mescroll-uni/mescroll-body.css";
@import "@/components/mescroll-uni/components/mescroll-down.css";
@import "@/components/mescroll-uni/components/mescroll-up.css";
@import "@/components/RefreshView/mescroll-down.css";
.titleStyle {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
overflow: hidden;
}
</style>

65
components/RefreshView/mescroll-down.css

@ -0,0 +1,65 @@
/*下拉刷新--标语*/
.mescroll-downwarp .downwarp-slogan {
display: block;
width: 420rpx;
height: 168rpx;
margin: auto;
}
/*下拉刷新--向下进度动画*/
.mescroll-downwarp .downwarp-progress {
display: inline-block;
width: 40rpx;
height: 40rpx;
border: none;
margin: auto;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
background-image: url(../../static/img/public/mescroll-progress.png);
transition: all 300ms;
}
/*下拉刷新--进度条*/
.mescroll-downwarp .downwarp-loading {
display: inline-block;
width: 32rpx;
height: 32rpx;
border-radius: 50%;
border: 2rpx solid #FF8095;
border-bottom-color: transparent;
}
/*下拉刷新--吉祥物*/
.mescroll-downwarp .downwarp-mascot {
position: absolute;
right: 16rpx;
bottom: 0;
width: 100rpx;
height: 100rpx;
background-size: contain;
background-repeat: no-repeat;
animation: animMascot .6s steps(1, end) infinite;
}
@keyframes animMascot {
0% {
background-image: url(http://www.mescroll.com/img/beibei/mescroll-bb1.png)
}
25% {
background-image: url(http://www.mescroll.com/img/beibei/mescroll-bb2.png)
}
50% {
background-image: url(http://www.mescroll.com/img/beibei/mescroll-bb3.png)
}
75% {
background-image: url(http://www.mescroll.com/img/beibei/mescroll-bb4.png)
}
100% {
background-image: url(http://www.mescroll.com/img/beibei/mescroll-bb1.png)
}
}

79
components/RefreshView/readme.md

@ -0,0 +1,79 @@
***********************************下拉框*************************************
1.dropLeftList: type: Array, default: []
// 左边下拉内容名称list
2.dropRightList: type: Array, default: []
// 右边下拉内容名称list
3.@drop :drop(id,isLeft,selectData)
// 监听用户选择的条目id,是否是左侧按钮的下拉,选中的数据
4.closeDrop()
// 关闭下拉框
***********************************导航栏*************************************
1.hasBack: type: Boolean,default: true
// 是否有返回键
2.useDownScroll: type: Boolean,default: true
// 是否支持下拉刷新
3.useUpScroll: ype: Boolean,default: true
// 是否支持上拉加载
// 使用上拉加载时 需引入
// import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";
// mixins: [MescrollMixin]
4.text: type: String,default: ""
// 导航栏标题
5.useTitleLeftBtn: type: String,default: "0"
// 导航栏最右边的两个按钮其中左边的按钮 0不使用 1使用文字 2使用图片
6.useTitleRightBtn: type: String,default: "0"
// 导航栏最右边的两个按钮其中右边的按钮 0不使用 1使用文字 2使用图片
7.titleLeftBtnSource: type: String,default: ""
// 导航栏最右边的两个按钮其中左边的按钮文字或图片资源
8.titleRightBtnSource: type: String,default: ""
// 导航栏最右边的两个按钮其中右边的按钮文字或图片资源
9.@refresh: refresh(page)
// 下拉或上拉的监听 参数为当前请求的页数
10.@leftBtn:
// 导航栏最右边的两个按钮其中左边的按钮点击的监听
11.@rightBtn:
// 导航栏最右边的两个按钮其中右边的按钮点击的监听
12.方法: refreshFinished()
// 网络请求成功后关闭下拉刷新动画与上拉加载动画
// 如果为分页请求 参数为当前接口返回list数据的长度(必须)
// 否则参数可不传
13.方法: refreshError()
// 网络请求失败后关闭下拉刷新动画与上拉加载动画
14.方法: resetPageOne()
// 调用此方法可重置为第一页(比如说退赛需要重新刷新列表,调用此方法会响应 @refresh 监听)
15.pageBg: type: String,default: ""
// 页面背景色
16.barColor: type: String,default: ""
// 顶部导航栏颜色
17.isShareIn: type: Boolean,default: true
// 是否是分享进入(分享进入返回键会显示主页)
// h5分享时需加入 isShareIn=true 小程序分享时onShareAppMessage() 也需加入isShareIn=true
18.isInterceptBack: type: Boolean,default: false
// 是否拦截返回键
// :isInterceptBack="true" @backClick = "方法名"
// 点击返回时会回调到上面设置方法名的那个方法里

19
components/RefreshView/refresh-option.js

@ -0,0 +1,19 @@
// 全局配置
const RefreshOption = {
// 背景色
pageBg: '#FFFFFF',
// 导航栏颜色
barColor: '#2fa1f0',
// 是否有返回键(会自动判断是否可返回)
hasBack: true,
// 首页
homePagePath: '../../pages/home/FindFragment',
// 首页是否是切换的页面
homePageIsTabBar: true,
// 返回键
backIcon: '../../static/img/public/back.png',
// 主页
homeIcon: '../../static/img/public/home.png'
}
export default RefreshOption

275
components/RichTextEditor/RichTextEditor.vue

@ -0,0 +1,275 @@
<template>
<view class="page-body" :style="{height:'calc(100vh - '+removeHeight+')'}">
<view class='wrapper'>
<view class="editor-wrapper">
<editor id="editor" class="ql-container" :placeholder="placeholder" showImgSize showImgToolbar
showImgResize @statuschange="onStatusChange" :read-only="readOnly" @ready="onEditorReady"
@input="getEditorContent">
</editor>
</view>
<view class='toolbar' @tap="format" style="height: 120px;overflow-y: auto;">
<view class="iconfont icon-undo" @tap="undo"></view>
<view class="iconfont icon-redo" @tap="redo"></view>
<view :class="formats.bold ? 'ql-active' : ''" class="iconfont icon-zitijiacu" data-name="bold">
</view>
<view :class="formats.italic ? 'ql-active' : ''" class="iconfont icon-zitixieti" data-name="italic">
</view>
<view :class="formats.underline ? 'ql-active' : ''" class="iconfont icon-zitixiahuaxian"
data-name="underline"></view>
<view :class="formats.strike ? 'ql-active' : ''" class="iconfont icon-zitishanchuxian"
data-name="strike"></view>
<view :class="formats.color === '#0000ff' ? 'ql-active' : ''" class="iconfont icon-text_color"
data-name="color" data-value="#0000ff"></view>
<view :class="formats.backgroundColor === '#00ff00' ? 'ql-active' : ''"
class="iconfont icon-fontbgcolor" data-name="backgroundColor" data-value="#00ff00"></view>
<view class="iconfont icon-charutupian" @tap="insertImage"></view>
<view class="iconfont icon-shanchu" @tap="clear"></view>
<view :class="formats.align === 'left' ? 'ql-active' : ''" class="iconfont icon-zuoduiqi"
data-name="align" data-value="left"></view>
<view :class="formats.align === 'center' ? 'ql-active' : ''" class="iconfont icon-juzhongduiqi"
data-name="align" data-value="center"></view>
<view :class="formats.align === 'right' ? 'ql-active' : ''" class="iconfont icon-youduiqi"
data-name="align" data-value="right"></view>
<view :class="formats.align === 'justify' ? 'ql-active' : ''" class="iconfont icon-zuoyouduiqi"
data-name="align" data-value="justify"></view>
<view :class="formats.lineHeight ? 'ql-active' : ''" class="iconfont icon-line-height"
data-name="lineHeight" data-value="2"></view>
<view :class="formats.letterSpacing ? 'ql-active' : ''" class="iconfont icon-Character-Spacing"
data-name="letterSpacing" data-value="2em"></view>
<view :class="formats.marginTop ? 'ql-active' : ''" class="iconfont icon-722bianjiqi_duanqianju"
data-name="marginTop" data-value="20px"></view>
<view :class="formats.previewarginBottom ? 'ql-active' : ''" class="iconfont icon-723bianjiqi_duanhouju"
data-name="marginBottom" data-value="20px"></view>
<view :class="formats.fontSize === '24px' ? 'ql-active' : ''" class="iconfont icon-fontsize"
data-name="fontSize" data-value="24px"></view>
<view :class="formats.list === 'ordered' ? 'ql-active' : ''" class="iconfont icon-youxupailie"
data-name="list" data-value="ordered"></view>
<view :class="formats.list === 'bullet' ? 'ql-active' : ''" class="iconfont icon-wuxupailie"
data-name="list" data-value="bullet"></view>
<view class="iconfont icon-outdent" data-name="indent" data-value="-1"></view>
<view class="iconfont icon-indent" data-name="indent" data-value="+1"></view>
<view class="iconfont icon-fengexian" @tap="insertDivider"></view>
<view :class="formats.script === 'sub' ? 'ql-active' : ''" class="iconfont icon-zitixiabiao"
data-name="script" data-value="sub"></view>
<view :class="formats.script === 'super' ? 'ql-active' : ''" class="iconfont icon-zitishangbiao"
data-name="script" data-value="super"></view>
<view :class="formats.direction === 'rtl' ? 'ql-active' : ''" class="iconfont icon-direction-rtl"
data-name="direction" data-value="rtl"></view>
</view>
</view>
</view>
</template>
<script>
export default {
props: {
html: {
type: String,
default: ''
},
placeholder: {
type: String,
default: ''
},
removeHeight: {
type: String,
default: '0px'
}
},
data() {
return {
readOnly: false,
formats: {},
result: {
html: "",
text: "",
delta: {}
}
}
},
methods: {
readOnlyChange() {
this.readOnly = !this.readOnly
},
onEditorReady() {
// #ifdef APP-PLUS
uni.createSelectorQuery().select('#editor').context((res) => {
this.editorCtx = res.context
let _this = this
let EContent = {
html: this.html
}
//
this.editorCtx.setContents(EContent);
this.editorCtx.getContents({
complete: res => {
_this.result = res
}
})
}).exec()
// #endif
// #ifdef H5 || MP-WEIXIN
this.createSelectorQuery().select('#editor').context((res) => {
this.editorCtx = res.context
let _this = this
let EContent = {
html: this.html
}
//
this.editorCtx.setContents(EContent);
this.editorCtx.getContents({
complete: res => {
_this.result = res
}
})
}).exec()
// #endif
},
undo() {
this.editorCtx.undo()
},
redo() {
this.editorCtx.redo()
},
format(e) {
let {
name,
value
} = e.target.dataset
if (!name) return
this.editorCtx.format(name, value)
},
onStatusChange(e) {
const formats = e.detail
this.formats = formats
},
insertDivider() {
this.editorCtx.insertDivider({
success: function() {
console.log('insert divider success')
}
})
},
clear() {
this.editorCtx.clear({
success: function(res) {
console.log("clear success")
}
})
},
removeFormat() {
this.editorCtx.removeFormat()
},
insertDate() {
const date = new Date()
const formatDate = `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`
this.editorCtx.insertText({
text: formatDate
})
},
insertImage() {
let that = this
console.log('00000>>')
uni.chooseImage({
count: 1,
success: (res) => {
console.log('1111>>'+res.tempFilePaths[0])
that.$emit("insertPic", res.tempFilePaths[0])
}
})
},
insertUrlImage(imageUrl) {
console.log('22222>>'+imageUrl)
this.editorCtx.insertImage({
src: imageUrl,
alt: '图像',
success: function() {
console.log('insert image success')
}
})
},
getEditorContent(e) {
this.result = e.detail;
},
getHtml() {
return this.result.html
},
getText() {
return this.result.text
}
}
}
</script>
<style lang="scss">
@import "./editor-icon.css";
.page-body {
background-color: #4CD964;
.wrapper {
height: 100%;
background-color: #FFFFFF;
box-sizing: border-box;
}
.editor-wrapper {
height: calc(100% - 120px);
background: #fff;
}
.toolbar {
box-sizing: border-box;
border-bottom: 0;
background-color: #F1F1F1;
}
}
.iconfont {
display: inline-block;
padding: 8px 8px;
width: 24px;
height: 24px;
cursor: pointer;
font-size: 20px;
}
.ql-container {
box-sizing: border-box;
padding: 12px 15px;
width: 100%;
min-height: 30vh;
height: 100%;
font-size: 16px;
line-height: 1.5;
}
.ql-active {
color: #06c;
}
</style>

239
components/RichTextEditor/editor-icon.css

File diff suppressed because one or more lines are too long

30
components/RichTextEditor/readme.md

@ -0,0 +1,30 @@
## 参数
| 参数 | 类型 | 解释 | 默认 | 必填 |
| ------------ | ------------ | ------------ | ------------ | ------------ |
| html | String | 默认html(模板内容) | "" | 否 |
| placeholder | String | 提示信息 | "" | 否 |
| removeHeight | String | 移除高度(默认占满屏幕高度) | "0px" | 否 |
## 方法
| 方法名 | 解释 | 参数 |
| ------------ | ------------ | ------------ |
| getHtml | 获取富文本html | 无 |
| getText | 获取富文本text | 无 |
| insertUrlImage | 插入网络图片 | 图片地址 |
## 事件
| 事件名 | 解释 | 参数 |
| ------------ | ------------ | ------------ |
| insertPic | 用户选取本地图片成功后回调 | 选择的本地图片地址 |
## 支持平台
| app | 微信小程序 |
| ------------ | ------------ |
| 未测试 | 支持 |
| h5-Safari | Android Browser | 微信浏览器(Android) |QQ浏览器(Android) |
| ------------ | ------------ | ------------ | |
| 未测试 | 支持 | 支持 | 支持 |

108
components/SendCodeItem/SendCodeItem.vue

@ -0,0 +1,108 @@
<template>
<view :style="{'border-radius': '5rpx','height': '65rpx','width': '160rpx','text-align': 'center','line-height': '65rpx','font-size': '26rpx',
'background-color': background,'color': 'white'}"
@click="click">
{{textDetail}}
</view>
</template>
<script>
export default {
data() {
return {
'background': '#2fa1f0',
"isCanClick": true,
"textDetail": "获取验证码",
"countdown": 60,
'cookie': ''
};
},
methods: {
getCookie() {
return this.cookie;
},
click() {
if (!this.isCanClick) {
return
}
if (this.phoneNum.length != 11 || !this.phoneNum.startsWith("1")) {
this.Toast("请输入合法的手机号")
} else {
//
this.background = "gray"
this.isCanClick = false
let timeOut = setInterval(() => {
if (this.countdown == 1) {
this.textDetail = "获取验证码"
this.countdown = 60
clearInterval(timeOut)
this.background = "#2fa1f0"
this.isCanClick = true
} else {
this.countdown = this.countdown - 1;
this.textDetail = this.countdown + "s"
}
}, 1000)
let _this = this
// if (this.sendByCookie != null) {
// // cookie
// this.HttpCookie({
// 'url': this.url,
// 'data': {
// "mobile": this.phoneNum
// },
// cookie: this.sendByCookie,
// loading: true
// }).then((res) => {
// _this.cookie = res.data
// }, (err) => {
// clearInterval(timeOut)
// this.countdown = 60
// //
// this.background = "#2fa1f0"
// this.isCanClick = true
// this.textDetail = ""
// })
// } else {
_this.HTTP({
url: _this.url+"?mobile="+_this.phoneNum,
data: {},
method: 'GET',
paramsType: "FORM",
loading: true
}).then((res) => {
_this.cookie = res.data
console.log('=======', res)
}, (err) => {
clearInterval(timeOut)
this.countdown = 60
//
this.background = "#2fa1f0"
this.isCanClick = true
this.textDetail = "获取验证码"
})
// }
}
}
},
props: ['phoneNum', 'url', 'data', 'sendByCookie'],
}
</script>
<style>
</style>

245
components/TabLayout/TabLayout.vue

@ -0,0 +1,245 @@
<template>
<view class="container999" @touchstart="refreshStart" @touchmove="refreshMove" @touchend="refreshEnd">
<!-- 刷新组件需搭配scroll-view使用并在pages.json中添加 "disableScroll":true-->
<tabRefresh ref="tabRefresh" @isRefresh='isRefresh'></tabRefresh>
<view class='nav'>
<!-- 状态栏 -->
<view :style="{'height':statusBarHeightPx}"></view>
<!-- 导航栏 -->
<view
:style="{'height':navHeightPx,'width':navWidthPx,'display': 'flex','flex-direction':'row','box-sizing': 'border-box','justify-content': 'space-between'}">
<view
:style="{'height':navHeightPx,'display': 'flex','flex-direction':'row','box-sizing': 'border-box'}">
<image :style="{'height':navHeightPx,'width':iconPx}" mode="aspectFit" :src="backIcon"
@click="clickBack"></image>
<text
:style="{'height':navHeightPx,'line-height':navHeightPx,'font-size':'33rpx','color':'#FFFFFF'}">{{text}}</text>
</view>
<view
:style="{'height':navHeightPx,'display': 'flex','flex-direction':'row','box-sizing': 'border-box'}">
<view v-if="useTitleLeftBtn!=0"
:style="{'height':navHeightPx,'display': 'flex','flex-direction':'row','box-sizing': 'border-box'}"
@click="clickLeftBtn">
<image v-if="useTitleLeftBtn == 2" :style="{'height':navHeightPx,'width':navHeightPx}"
mode="aspectFit" :src="titleLeftBtnSource"></image>
<text v-if="useTitleLeftBtn == 1"
:style="{'height':navHeightPx,'line-height':navHeightPx,'font-size':'28rpx','color':'#FFFFFF','padding-left':'20rpx','padding-right':'20rpx'}">{{titleLeftBtnSource}}</text>
</view>
<view v-if="useTitleRightBtn!=0"
:style="{'height':navHeightPx,'display': 'flex','flex-direction':'row','box-sizing': 'border-box'}"
@click="clickRightBtn">
<image v-if="useTitleRightBtn==2" :style="{'height':navHeightPx,'width':navHeightPx}"
mode="aspectFit" :src="titleRightBtnSource"></image>
<text v-if="useTitleRightBtn==1"
:style="{'height':navHeightPx,'line-height':navHeightPx,'font-size':'28rpx','color':'#FFFFFF','padding-left':'20rpx','padding-right':'20rpx'}">{{titleRightBtnSource}}</text>
</view>
</view>
</view>
<!-- Tab切换 -->
<tabTop ref="navTab" :tabTitle="tabTitleData" :tabNum="tabNumData" @onTabClick='clickTab'></tabTop>
</view>
<slot></slot>
</view>
</template>
<script>
const util = require('@/util/util.js');
export default {
props: {
text: {
type: String,
default: ""
},
isShareIn: {
type: Boolean,
default: false
},
hasBack: {
type: Boolean,
default: true
},
useTitleLeftBtn: {
type: String,
// 0使 1使 2使
default: "0"
},
useTitleRightBtn: {
type: String,
// 0使 1使 2使
default: "0"
},
titleLeftBtnSource: {
type: String,
default: ""
},
titleRightBtnSource: {
type: String,
default: ""
},
tabTitleData: {
type: Array,
default () {
return []
}
},
tabNumData: {
type: Array,
default () {
return []
}
}
},
data() {
return {
currentTab: 0, //sweiper
swiperPaddindTop: 0,
windowHeight: 0, // 使
statusBarH: 0, //
statusBarHeight: "",
navHeight: "",
navWidth: "",
totalHeight: "",
statusBarHeightPx: "",
navHeightPx: "",
navWidthPx: "",
totalHeightPx: "",
bg: "#2fa1f0",
totalHeightUpx: "",
backIcon: "",
iconPx: ""
};
},
methods: {
getViewPagerTop() {
return this.swiperPaddindTop
},
clickLeftBtn() {
this.$emit("leftBtn", "左侧按钮")
},
clickRightBtn() {
this.$emit("rightBtn", "右侧按钮")
},
clickBack() {
if (this.hasBack) {
if (this.$props.isShareIn) {
uni.switchTab({
url: "../../pages/home/FindFragment"
})
} else {
this.Back()
}
}
},
// touch
refreshStart(e) {
this.$refs.tabRefresh.refreshStart(e);
},
refreshMove(e) {
this.$refs.tabRefresh.refreshMove(e);
},
refreshEnd(e) {
this.$refs.tabRefresh.refreshEnd(e);
},
isRefresh() {
this.$emit("downRefresh", this.currentTab)
},
//
downRefresh() {
this.$refs.tabRefresh.endAfter()
},
// tab
clickTab(index) {
// index(使datacurrentTab)
//
this.currentTab = index
this.$emit("tabClickItem", index)
}, // swiper
changeTab(e) {
var index = e.detail.current //
// tab
this.$refs.navTab.longClick(index);
}
},
watch: {
isShareIn(newValue) {
if (this.$props.hasBack) {
this.backIcon = '../../static/custom-icon/back.png'
this.iconPx = this.navHeightPx
if (this.$props.isShareIn) {
this.backIcon = '../../static/custom-icon/home.png'
}
} else {
this.backIcon = '../../static/img/public/no-back.png'
this.iconPx = this.navHeight / 8 * 3 + 'px'
}
}
},
created() {
this.navWidth = getApp().globalData.navWidth
this.navHeight = getApp().globalData.navHeight
this.totalHeight = getApp().globalData.totalHeight
this.statusBarHeight = getApp().globalData.statusBarHeight
this.navWidthPx = getApp().globalData.navWidthPx
this.navHeightPx = getApp().globalData.navHeightPx
this.totalHeightPx = getApp().globalData.totalHeightPx
this.statusBarHeightPx = getApp().globalData.statusBarHeightPx
this.totalHeightUpx = getApp().globalData.totalHeightUpx
if (this.$props.hasBack) {
this.backIcon = '../../static/img/public/back.png'
this.iconPx = this.navHeightPx
if (this.$props.isShareIn) {
this.backIcon = '../../static/img/public/home.png'
}
} else {
this.backIcon = '../../static/custom-icon/no-back.png'
this.iconPx = this.navHeight / 8 * 3 + 'px'
}
this.swiperPaddindTop = this.totalHeight + 45 + 'px'
}
}
</script>
<style>
.container999 {
width: 100vw;
font-size: 28upx;
min-height: 100vh;
overflow: hidden;
color: #6B8082;
position: relative;
}
.nav {
position: fixed;
left: 0;
top: 0;
color: white;
width: 100%;
display: flex;
flex-direction: column;
font-size: 24upx;
background-color: #2fa1f0;
z-index: 996;
}
</style>

93
components/UserItem/UserItem.vue

@ -0,0 +1,93 @@
<template>
<view class="user-item-content" @click="click">
<view class="user-item-left">
<image :src="src" style="width: 70rpx;height: 70rpx;" mode="aspectFit"></image>
<text class="user-item-textBlack">{{text}}</text>
</view>
<view class="user-item-right" style="display: flex;align-items: center;">
<text v-if="showRightText"
style="font-size:30rpx ;color: #999999;margin-top: -7rpx;">{{rightText}}</text>
<image v-if="showRightImg" style="height: 30rpx;width: 30rpx;" src="../../static/img/public/more.png"></image>
</view>
</view>
</template>
<script>
export default {
props: {
src: {
type: String,
default: ""
},
text: {
type: String,
default: ""
},
clickId: {
type: String,
default: "0"
},
rightText: {
type: String,
default: ""
},
showRightText: {
type: Boolean,
default: false
},
showRightImg: {
type: Boolean,
default: true
},
},
data() {
return {
};
},
methods: {
click() {
var clickId = this.$props.clickId;
this.$emit("click", clickId)
}
}
}
</script>
<style lang="scss">
.user-item-content {
width: 100%;
height: 90rpx;
display: flex;
flex-direction: row;
align-items: center;
padding-left: 40rpx;
justify-content: space-between;
border-bottom: 0.1px solid #F1F1F1;
box-sizing: border-box;
.user-item-left {
display: flex;
flex-direction: row;
height: 89rpx;
align-items: center;
flex: 1;
box-sizing: border-box;
.user-item-textBlack {
color: #333333;
font-size: 30rpx;
height: 89rpx;
line-height: 89rpx;
}
}
.user-item-right {
margin-right: 35rpx;
flex-shrink: 0;
}
}
</style>

516
components/biaofun-datetime-picker/biaofun-datetime-picker.vue

@ -0,0 +1,516 @@
<!--
* @插件日期时间选择器
* @作者陈万照
* @公司山东标梵互动信息技术有限公司
* @官网http://biaofun.com/
* @微信C207668802
* @QQ207668802
* @邮箱cwz@biaofun.com || 207668802@qq.com
* @版本v1.0.8
-->
<template>
<view>
<picker :mode="mode" :range="range" range-key="text" @change="change" @columnchange="columnchange"
:value="value" :disa:bled="disabled">
<slot></slot>
</picker>
</view>
</template>
<script>
import utils from '@/common/utils.js'; //
export default {
/**
* 数据
*/
props: {
//
mode: {
type: String,
default: "multiSelector"
},
//
disabled: {
type: Boolean,
default: false
},
//
placeholder: {
type: String,
default: '请选择日期时间'
},
//
// "YYYY-MM-DD hh:mm"
start: {
type: String,
default: '1970-1-1 00:00'
},
//
// "YYYY-MM-DD hh:mm"
end: {
type: String,
default: '2300-1-1 00:00'
},
// year | month | day | hour | minute
fields: {
type: String,
default: 'minute'
},
//
// "YYYY-MM-DD hh:mm"
defaultValue: {
type: String,
default: ''
}
},
/**
* 数据
*/
data() {
return {
range: [],
value: [],
dateStr: '', //
dtStart: null, //
dtEnd: null, //
};
},
/**
* 监听数据
*/
watch: {
//
defaultValue() {
//
this.setDefaultValue();
}
},
/**
* 组件初次加载完成
*/
mounted() {
//
let start = this.start;
let end = this.end;
//
if (!utils.isString(this.start)) {
console.log('开始日期需为String类型,格式为 "YYYY-MM-DD hh:mm"');
start = '1970-1-1 00:00';
}
if (!utils.isString(this.start)) {
console.log('结束日期需为String类型,格式为 "YYYY-MM-DD hh:mm"');
start = '2300-1-1 00:00';
}
// Date
let dtStart = utils.formatDate(start).dt;
let dtEnd = utils.formatDate(end).dt;
// 300
if (dtEnd <= dtStart) {
dtEnd = utils.formatDate(start).dt;
dtEnd.setFullYear(dtStart.getFullYear() + 300);
dtEnd.setDate(dtEnd.getDate() - 1);
}
//
this.dtStart = dtStart;
this.dtEnd = dtEnd;
//
this.setDefaultValue();
},
/**
* 方法
*/
methods: {
/**
* 确认选择
*/
change(event) {
let year, month, day, hour, minute;
if (this.fields == 'year') {
year = this.range[0][this.value[0]].number; //
let dtStr = `${year}`;
this.setDateStr(dtStr);
this.$emit('change', utils.formatDate(dtStr));
return;
} else if (this.fields == 'month') {
year = this.range[0][this.value[0]].number; //
month = this.range[1][this.value[1]].number; //
let dtStr = `${year}-${month}`;
this.setDateStr(dtStr);
this.$emit('change', utils.formatDate(dtStr));
return;
} else if (this.fields == 'day') {
year = this.range[0][this.value[0]].number; //
month = this.range[1][this.value[1]].number; //
day = this.range[2][this.value[2]].number; //
let dtStr = `${year}-${month}-${day}`;
this.setDateStr(dtStr);
this.$emit('change', utils.formatDate(dtStr));
return;
} else if (this.fields == 'hour') {
year = this.range[0][this.value[0]].number; //
month = this.range[1][this.value[1]].number; //
day = this.range[2][this.value[2]].number; //
hour = this.range[3][this.value[3]].number; //
day = this.range[2][this.value[2]].number; //
let dtStr = `${year}-${month}-${day} ${hour}`;
this.setDateStr(dtStr);
this.$emit('change', utils.formatDate(dtStr));
return;
} else if (this.fields == 'minute') {
year = this.range[0][this.value[0]].number; //
month = this.range[1][this.value[1]].number; //
day = this.range[2][this.value[2]].number; //
hour = this.range[3][this.value[3]].number; //
minute = this.range[4][this.value[4]].number; //
let dtStr = `${year}-${month}-${day} ${hour}:${minute}`;
this.setDateStr(dtStr);
this.$emit('change', utils.formatDate(dtStr));
return;
}
},
/**
* 设置显示的值
* @param {Date|String} date 日期字符串或日期对象
*/
setDateStr(date) {
let dt = utils.formatDate(date);
if (this.fields == 'year') {
this.dateStr = `${dt.YYYY}`;
return;
}
if (this.fields == 'month') {
this.dateStr = `${dt.YYYY}${dt.M}`;
return;
}
if (this.fields == 'day') {
this.dateStr = `${dt.YYYY}${dt.M}${dt.D}`;
return;
}
if (this.fields == 'hour') {
this.dateStr = `${dt.YYYY}${dt.M}${dt.D}${dt.h}`;
return;
}
this.dateStr = `${dt.YYYY}${dt.M}${dt.D}${dt.h}${dt.m}`;
},
/**
* 设置年数据
*/
setYearData() {
//
let yearStart = this.dtStart.getFullYear();
let yearEnd = this.dtEnd.getFullYear();
//
let years = [];
for (let year = yearStart; year <= yearEnd; year++) {
let item = {
number: year,
text: `${year}`,
};
years.push(item);
}
this.range.splice(0, 1, years);
},
/**
* 设置月数据
* @param {Number} year
*/
setMonthData(year) {
//
let yearStart = this.dtStart.getFullYear();
let monthStart = this.dtStart.getMonth() + 1;
let yearEnd = this.dtEnd.getFullYear();
let monthEnd = this.dtEnd.getMonth() + 1;
//
let months = [];
let monthStartIndex = year == yearStart ? monthStart : 1;
let monthEndIndex = year == yearEnd ? monthEnd : 12;
for (let month = monthStartIndex; month <= monthEndIndex; month++) {
let item = {
number: month,
text: `${month}`,
};
months.push(item);
}
this.range.splice(1, 1, months);
},
/**
* 设置日数据
* @param {Number} year
* @param {Number} month
*/
setDayData(year, month) {
//
let yearStart = this.dtStart.getFullYear();
let monthStart = this.dtStart.getMonth() + 1;
let dayStart = this.dtStart.getDate();
let yearEnd = this.dtEnd.getFullYear();
let monthEnd = this.dtEnd.getMonth() + 1;
let dayEnd = this.dtEnd.getDate();
//
let days = [];
let dayStartIndex = year == yearStart && month == monthStart ? dayStart : 1;
let dayEndIndex;
if (year == yearEnd && month == monthEnd) {
dayEndIndex = dayEnd;
} else {
dayEndIndex = (new Date(year, month, 0)).getDate();
}
for (let day = dayStartIndex; day <= dayEndIndex; day++) {
let item = {
number: day,
text: `${day}`,
};
days.push(item);
}
this.range.splice(2, 1, days);
},
/**
* 设置时数据
* @param {Number} year
* @param {Number} month
* @param {Number} day
*/
setHourData(year, month, day) {
//
let yearStart = this.dtStart.getFullYear();
let monthStart = this.dtStart.getMonth() + 1;
let dayStart = this.dtStart.getDate();
let hourStart = this.dtStart.getHours();
let yearEnd = this.dtEnd.getFullYear();
let monthEnd = this.dtEnd.getMonth() + 1;
let dayEnd = this.dtEnd.getDate();
let hourEnd = this.dtEnd.getHours();
//
let hours = [];
let hourStartIndex = year == yearStart && month == monthStart && day == dayStart ? hourStart : 0;
let hourEndIndex = year == yearEnd && month == monthEnd && day == dayEnd ? hourEnd : 23;
for (let hour = hourStartIndex; hour <= hourEndIndex; hour++) {
let item = {
number: hour,
text: `${hour}`,
};
hours.push(item);
}
this.range.splice(3, 1, hours);
},
/**
* 设置分数据
* @param {Number} year
* @param {Number} month
* @param {Number} day
* @param {Number} hour
*/
setMinuteData(year, month, day, hour) {
//
let yearStart = this.dtStart.getFullYear();
let monthStart = this.dtStart.getMonth() + 1;
let dayStart = this.dtStart.getDate();
let hourStart = this.dtStart.getHours();
let minuteStart = this.dtStart.getMinutes();
let yearEnd = this.dtEnd.getFullYear();
let monthEnd = this.dtEnd.getMonth() + 1;
let dayEnd = this.dtEnd.getDate();
let hourEnd = this.dtEnd.getHours();
let minuteEnd = this.dtEnd.getMinutes();
//
let minutes = [];
let minuteStartIndex = year == yearStart && month == monthStart && day == dayStart && hour == hourStart ?
minuteStart : 0;
let minuteEndIndex = year == yearEnd && month == monthEnd && day == dayEnd && hour == hourEnd ? minuteEnd :
59;
for (let minute = minuteStartIndex; minute <= minuteEndIndex; minute++) {
let item = {
number: minute,
text: `${minute}`,
}
minutes.push(item);
}
this.range.splice(4, 1, minutes);
},
/**
* 设置默认值
*/
setDefaultValue() {
//
let dtDefault;
//
let dtStart = this.dtStart;
let dtEnd = this.dtEnd;
//
//
if (this.defaultValue) {
dtDefault = utils.formatDate(this.defaultValue).dt;
}
//
else {
dtDefault = new Date();
}
//
if (dtDefault < dtStart || dtDefault > dtEnd) {
dtDefault = dtStart;
}
// dateStr
if (this.defaultValue) this.setDateStr(dtDefault);
//
let dfYear = dtDefault.getFullYear();
let dfMonth = dtDefault.getMonth() + 1;
let dfDay = dtDefault.getDate();
let dfHour = dtDefault.getHours();
let dfMinute = dtDefault.getMinutes();
//
this.setYearData();
// Year value
let yearIndex = this.range[0].findIndex(year => {
return dfYear == year.number;
});
this.value.splice(0, 1, yearIndex >= 0 ? yearIndex : 0);
//
if (this.fields == 'year') return;
this.setMonthData(dfYear);
// Month value
let monthIndex = this.range[1].findIndex(month => {
return dfMonth == month.number;
});
this.value.splice(1, 1, monthIndex >= 0 ? monthIndex : 0);
//
if (this.fields == 'month') return;
this.setDayData(dfYear, dfMonth);
// Day value
let dayIndex = this.range[2].findIndex(day => {
return dfDay == day.number;
});
this.value.splice(2, 1, dayIndex >= 0 ? dayIndex : 0);
//
if (this.fields == 'day') return;
this.setHourData(dfYear, dfMonth, dfDay);
// Hour value
let hourIndex = this.range[3].findIndex(hour => {
return dfHour == hour.number;
});
this.value.splice(3, 1, hourIndex >= 0 ? hourIndex : 0);
//
if (this.fields == 'hour') return;
this.setMinuteData(dfYear, dfMonth, dfDay, dfHour);
// Minute value
let minuteIndex = this.range[4].findIndex(minute => {
return dfMinute == minute.number;
});
this.value.splice(4, 1, minuteIndex >= 0 ? minuteIndex : 0);
},
/**
* 某一列的值改变时触发
* @param {Number} event.detail.column 表示改变了第几列下标从0开始
* @param {Number} event.detail.value 表示变更值的下标
*/
columnchange(event) {
let columnIndex = event.detail.column; //
let valueIndex = event.detail.value; //
// value
this.value.splice(columnIndex, 1, valueIndex);
//
if (this.fields == 'year') return;
if (columnIndex == 0) {
//
let monthBeforeUpdate = this.range[1][this.value[1]];
//
this.setMonthData(this.range[0][this.value[0]].number);
// Month Value
let monthIndex = this.range[1].findIndex(month => {
return month.number == monthBeforeUpdate.number;
});
this.value.splice(1, 1, monthIndex >= 0 ? monthIndex : 0);
}
//
if (this.fields == 'month') return;
if (columnIndex == 0 || columnIndex == 1) {
//
let dayBeforeUpdate = this.range[2][this.value[2]];
//
this.setDayData(this.range[0][this.value[0]].number, this.range[1][this.value[1]].number);
// Day Value
let dayIndex = this.range[2].findIndex(day => {
return day.number == dayBeforeUpdate.number;
});
this.value.splice(2, 1, dayIndex >= 0 ? dayIndex : 0);
}
//
if (this.fields == 'day') return;
if (columnIndex == 0 || columnIndex == 1 || columnIndex == 2) {
//
let hourBeforeUpdate = this.range[3][this.value[3]];
//
this.setHourData(this.range[0][this.value[0]].number, this.range[1][this.value[1]].number, this.range[
2][this.value[2]].number);
// Hour Value
let hourIndex = this.range[3].findIndex(hour => {
return hour.number == hourBeforeUpdate.number;
});
this.value.splice(3, 1, hourIndex >= 0 ? hourIndex : 0);
}
//
if (this.fields == 'hour') return;
let minuteBeforeUpdate = this.range[4][this.value[4]];
//
this.setMinuteData(this.range[0][this.value[0]].number, this.range[1][this.value[1]].number, this.range[2][
this.value[2]
].number, this.range[3][this.value[3]].number);
// Minute Value
let minuteIndex = this.range[4].findIndex(minute => {
return minute.number == minuteBeforeUpdate.number;
});
this.value.splice(4, 1, minuteIndex >= 0 ? minuteIndex : 0);
},
}
};
</script>
<style lang="scss" scoped>
.content {
text-align: right;
}
.placeholder {
color: #949596;
}
</style>

30
components/biaofun-datetime-picker/使用说明.md

@ -0,0 +1,30 @@
### 组件说明
* 日期时间选择器
* 组件的默认日期有效期范围为:"1970-01-01 00:00" - "2300-01-01 00:00"。
* 注意:如果您传递的日期有效范围的结束日期小于开始日期,则日期有效范围的结束日期会自动修正为开始日期+300年,比如,您传递的日期有效范围的开始日期为 "2020-11-11 18:30",
* 结束日期为 "2018-11-11 18:30",则此时将会自动修正结束日期为 "2320-11-11 18:30"。
* 注意:如果您传递的默认值不在日期有效范围内,则会自动修正默认值为当前日期时间,如果当前日期时间也不在日期有效范围内,则会再次修正为日期有效范围的开始日期。
* 注意:该组件用到了我自己封装的 utils.js,位置在 '@/common/js/utils.js'。
### 插件 props 属性
* disabled: 是否禁用该组件?Boolean类型;
* placeholder: 组件没有选中值时显示的内容,String类型;
* start: 表示有效日期时间范围的开始,String类型,格式为 "YYYY-MM-DD hh:mm";
* end: 表示有效日期时间范围的结束,String类型,格式必为 "YYYY-MM-DD hh:mm";
* fields: 选择器的粒度,String类型,有效值为 year、month、day、hour、minute;
* defaultValue: 默认值,String类型,格式为 "YYYY-MM-DD hh:mm";
### 插件事件
- change(date):选择日期时间后的回调事件。
* date.YYYY: 年;
* date.M: 月;
* date.MM: 月(补0);
* date.D: 日;
* date.DD: 日(补0);
* date.h: 时;
* date.hh: 时(补0);
* date.m: 分;
* date.mm: 分(补0);
* date.dt: Date对象;
* ... 还有一些其他的字段,具体看返回值吧!

422
components/city-select/city-select.vue

@ -0,0 +1,422 @@
<template>
<!-- 城市选择-->
<view class="city-select">
<StatusBar></StatusBar>
<!-- 预留搜索-->
<view style="background-color: #FFFFFF;">
<view :style="{'height':navHeight,'display': 'flex','box-sizing': 'border-box','flex-direction':'row',
'align-items':'center','width':width}">
<view class="city-serach" v-if="isSearch">
<input @input="keyInput" placeholder="请输入城市名称">
</view>
<text style="font-size: 27rpx;margin-right: 20rpx;color: #999999;border-left: 1px solid #f1f1f1;padding-left: 20rpx;"
@click="close">取消</text>
</view>
</view>
<scroll-view :scroll-top="scrollTop" scroll-y="true" class="city-select-main" id="city-select-main">
<!-- 当前定位城市 -->
<view class="hot-title" v-if="activeCity && !serachCity">当前定位城市</view>
<view class="hot-city" v-if="activeCity && !serachCity">
<view class="hot-item" @click="cityTrigger(activeCity)">{{ activeCity[formatName] }}</view>
</view>
<!-- 热门城市 -->
<view class="hot-title" v-if="hotCity.length > 0 && !serachCity">热门城市</view>
<view class="hot-city" v-if="hotCity.length > 0 && !serachCity">
<template v-for="(item, index) in hotCity">
<view :key="index" @click="cityTrigger(item, 'hot')" class="hot-item">{{ item[formatName] }}</view>
</template>
</view>
<!-- 城市列表(搜索前) -->
<view class="citys" v-if="!serachCity">
<view v-for="(city, index) in sortItems" :key="index" v-if="city.isCity">
<view class="citys-item-letter" :id="'city-letter-' + (city.name === '#' ? '0' : city.name)">{{ city.name }}</view>
<view class="citys-item" v-for="(item, inx) in city.citys" :key="inx" @click="cityTrigger(item)">{{ item.cityName }}</view>
</view>
</view>
<!-- 城市列表(搜索后) -->
<view class="citys" v-if="serachCity">
<view v-for="(item, index) in searchDatas" :key="index">
<view class="citys-item" :key="index" @click="cityTrigger(item)">{{ item.name }}</view>
</view>
</view>
</scroll-view>
<!-- 城市选择索引-->
<view class="city-indexs-view" v-if="!serachCity">
<view class="city-indexs">
<view v-for="(cityIns, index) in handleCity" v-if="cityIns.isCity" :key="index" @click="cityindex(cityIns.name)">{{ cityIns.name }}</view>
</view>
</view>
</view>
</template>
<script>
import citySelect from './citySelect.js'
export default {
props: {
//
formatName: {
type: String,
default: 'cityName'
},
//
activeCity: {
type: Object,
default: () => null
},
//
hotCity: {
type: Array,
default: () => []
},
//
obtainCitys: {
type: Array,
default: () => []
},
//
isSearch: {
type: Boolean,
default: true
}
},
data() {
return {
navHeight: "",
width: "",
totalHeight: "",
scrollTop: 0, //scroll-view
cityindexs: [], //
activeCityIndex: '', //
handleCity: [], //
serachCity: '', //
cityData: []
}
},
computed: {
/**
* @desc 城市列表排序
* @return Array
*/
sortItems() {
for (let index = 0; index < this.handleCity.length; index++) {
if (this.handleCity[index].isCity) {
var cityArr = this.handleCity[index].citys
cityArr = cityArr.sort(function(a, b) {
var value1 = a.unicode
var value2 = b.unicode
return value1 - value2
})
}
}
return this.handleCity
},
/**
* @desc 搜索后的城市列表
* @return Array
*/
searchDatas() {
var searchData = []
for (let i = 0; i < this.cityData.length; i++) {
if (this.cityData[i][this.formatName].indexOf(this.serachCity) !== -1) {
searchData.push({
oldData: this.cityData[i],
name: this.cityData[i][this.formatName]
})
}
}
return searchData
}
},
created() {
//
this.cityData = this.obtainCitys
this.initializationCity()
this.buildCityindexs()
//
let info = uni.getSystemInfoSync()
let statusBarHeight = info.statusBarHeight
this.statusBarHeight = statusBarHeight + 'px'
this.navHeight = '45px'
this.width = '100%'
this.totalHeight = 45 + statusBarHeight + 'px'
//
// #ifdef MP-WEIXIN||MP-BAID||MP-QQ||MP-TOUTIAO
let menuButton = uni.getMenuButtonBoundingClientRect()
let top = menuButton.top
let bottom = menuButton.bottom
let navHeight = bottom - top + (top - statusBarHeight) * 2 + 4
this.navHeight = navHeight + 'px'
this.width = menuButton.left + 'px'
this.totalHeight = navHeight + statusBarHeight + 'px'
// #endif
},
watch: {
obtainCitys(newData) {
this.updateCitys(newData)
}
},
methods: {
/**
* @desc 初始化
*/
updateCitys(data) {
if (data && data.length) {
this.cityData = data
this.initializationCity()
this.buildCityindexs()
}
},
/**
* @desc 监听输入框的值
*/
keyInput(event) {
this.serachCity = event.detail.value
},
/**
* @desc 初始化城市数据
* @return undefind
*/
initializationCity() {
this.handleCity = []
const cityLetterArr = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '#'
]
for (let index = 0; index < cityLetterArr.length; index++) {
this.handleCity.push({
name: cityLetterArr[index],
isCity: false, //
citys: [] //
})
}
},
/**
* @desc 得到城市的首字母
* @param str String
*/
getLetter(str) {
return citySelect.getFirstLetter(str[0])
},
/**
* @desc 构建城市索引
* @return undefind
*/
buildCityindexs() {
this.cityindexs = []
for (let i = 0; i < this.cityData.length; i++) {
//
let cityLetter = this.getLetter(this.cityData[i][this.formatName]).firstletter
// unicode
let unicode = this.getLetter(this.cityData[i][this.formatName]).unicode
let index = this.cityIndexPosition(cityLetter)
if (this.cityindexs.indexOf(cityLetter) === -1) {
this.handleCity[index].isCity = true
this.cityindexs.push(cityLetter)
}
this.handleCity[index].citys.push({
cityName: this.cityData[i][this.formatName],
unicode: unicode,
oldData: this.cityData[i]
})
}
},
/**
* @desc 滑动到城市索引所在的地方
* @param id String 城市索引
*/
cityindex(id) {
//
const query = uni.createSelectorQuery().in(this)
var that = this
that.scrollTop = 0
//(::)
setTimeout(() => {
query
.select('#city-letter-' + (id === '#' ? '0' : id))
.boundingClientRect(data => {
// console.log("" + JSON.stringify(data));
// console.log("" + data.top);
data ? (that.scrollTop = data.top) : void 0
})
.exec()
}, 0)
},
/**
* @desc 获取城市首字母的unicode
* @param letter String 城市索引
*/
cityIndexPosition(letter) {
if (!letter) {
return ''
}
const ACode = 65
return letter === '#' ? 26 : letter.charCodeAt(0) - ACode
},
/** @desc
* @param Object
*/
cityTrigger(item, isHot) {
//
this.$emit('cityClick', item.oldData ? item.oldData : item)
},
close() {
this.Back()
}
}
}
</script>
<style lang="scss">
//vw
@function vww($number) {
@return ($number / 375) * 750+rpx;
}
view {
box-sizing: border-box;
}
.city-serach {
flex: 1;
color: #4a4a4a;
padding: 0 vww(10);
&-input {
margin: vww(13) 0;
height: vww(40);
line-height: vww(40);
font-size: vww(14);
padding: 0 vww(5);
border: 1px solid #4d8cfd;
border-radius: 3px;
}
}
.city-select-main {
position: relative;
// overflow: scroll;
// -webkit-overflow-scrolling: touch;
width: 100%;
height: 100%;
background: #FFFFFF;
// overflow-y: auto;
}
.city-select {
position: relative;
width: 100vw;
height: 100vh;
background: #f6f5fa;
//
.hot-title {
padding-left: vww(13);
width: 100vw;
font-size: 14px;
line-height: vww(30);
color: #9b9b9b;
background-color: #ededed;
}
.hot-city {
padding-left: vww(23);
padding-right: vww(20);
padding-top: vww(12);
overflow: hidden;
width: 100vw;
.hot-item {
float: left;
padding: 0 vww(5);
margin-right: vww(16);
margin-bottom: vww(12);
overflow: hidden;
width: vww(100);
height: vww(31);
font-size: 14px;
text-align: center;
border-radius: vww(5);
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
line-height: vww(31);
color: #4a4a4a;
background: #f5f5f5;
&:nth-child(3n) {
margin-right: 0;
}
}
.hot-hidden {
display: none;
margin-right: 0;
}
}
.citys {
>view {
padding-left: vww(18);
width: 100%;
font-size: 14px;
background: #fff;
.citys-item-letter {
margin-left: vww(-18);
padding-left: vww(18);
margin-top: -1px;
width: 100vw;
line-height: vww(30);
color: #9b9b9b;
background: #ededed;
border-top: none;
}
.citys-item {
width: 100%;
line-height: vww(50);
color: #4a4a4a;
border-bottom: 0.1px solid #ebebf0;
&:last-child {
border: none;
}
}
}
}
.city-indexs-view {
position: absolute;
right: 0;
top: 0;
z-index: 999;
display: flex;
width: vww(20);
height: 100%;
text-align: center;
.city-indexs {
width: vww(20);
text-align: center;
vertical-align: middle;
align-self: center;
>view {
margin-bottom: vww(10);
width: vww(20);
font-size: 12px;
color: #4d8cfd;
&:last-child {
margin-bottom: 0;
}
}
}
}
}
</style>

36
components/city-select/citySelect.js

File diff suppressed because one or more lines are too long

1024
components/city-select/citys.js

File diff suppressed because it is too large

697
components/cropper/cropper.vue

@ -0,0 +1,697 @@
<template>
<view class="cropper" id="cropper" :class="{ show: show }">
<view class="cropper-head">
<view class="cropper-btn cropper-reset" @tap="resetCrop">重做</view>
</view>
<view class="cropper-body">
<image id="image" class="cropper-image" :src="imagePath" mode="aspectFit"></image>
<view
:style="{ width: stageWidth + 'px', height: stageHeight + 'px', left: stageLeft + 'px', top: stageTop + 'px' }"
class="cropper-stage" @touchstart.stop.prevent="touchStart" @touchmove.stop.prevent="touchMove">
<view id="box" class="cropper-box"
:style="{ width: boxWidth + 'px', height: boxHeight + 'px', left: boxLeft + 'px', top: boxTop + 'px' }">
<view id="lt" class="lt"></view>
<view id="lb" class="lb"></view>
<view id="rt" class="rt"></view>
<view id="rb" class="rb"></view>
<view class="line-v" style="left:33.3%;"></view>
<view class="line-v" style="left:66.6%;"></view>
<view class="line-h" style="top:33.3%;"></view>
<view class="line-h" style="top:66.6%;"></view>
</view>
</view>
<canvas class="cropper-canvas" canvas-id="canvas"
:style="{ height: canvasHeight + 'px', width: canvasWidth + 'px' }"></canvas>
</view>
<view class="cropper-bottom">
<view class="cropper-btn cropper-cancel" @tap="cancelCrop">取消</view>
<view class="cropper-btn cropper-ok" @tap="completeCrop">裁剪</view>
</view>
</view>
</template>
<script>
//
let layoutLeft = 0;
let layoutTop = 0;
let layoutWidth = 0;
let layoutHeight = 0;
let stageLeft = 0;
let stageTop = 0;
let stageWidth = 0;
let stageHeight = 0;
let imageWidth = 0;
let imageHeight = 0;
let pixelRatio = 1; //todo//使//
let imageStageRatio = 1; //
let minBoxWidth = 0;
let minBoxHeight = 0;
let touchStartBoxLeft = 0;
let touchStartBoxTop = 0;
let touchStartBoxWidth = 0;
let touchStartBoxHeight = 0;
let touchStartX = 0;
let touchStartY = 0;
export default {
name: 'cropper',
props: {
quality: {
type: Number,
default: 1
},
//jpgjpgjpgpngpng
outputFileType: {
type: String,
default: 'jpg'
},
//nullaspectRatio0
aspectRatio: {
type: [Number, null],
default: null
},
//0.150.15
minBoxWidthRatio: {
type: Number,
default: 0.15
},
//minBoxWidthRatioaspectRatiominBoxHeightminBoxHeightminBoxWidth aspectRatio
minBoxHeightRatio: {
type: Number,
default: 0.15
},
//0.80.8
initialBoxWidthRatio: {
type: Number,
default: 0.8
},
//initialBoxWidthRatioaspectRatioinitialBoxHeightRatioinitialBoxHeightRatioinitialBoxWidthRatio aspectRatio
initialBoxHeightRatio: {
type: Number,
default: 0.8
},
// NoneScaleTo500( 500) Max500(500)
saveMode: {
type: String,
default: "None"
}
},
data() {
return {
//data
stageLeft: 0,
stageTop: 0,
stageWidth: 0,
stageHeight: 0,
boxWidth: 0,
boxHeight: 0,
boxLeft: 0,
boxTop: 0,
canvasWidth: 0,
canvasHeight: 0,
show: false,
imagePath: ''
};
},
methods: {
resetCrop() {
this.$emit('reset');
this.init(this.imagePath);
},
cancelCrop() {
this.$emit('cancel');
},
completeCrop() {
let imagePath = this.imagePath;
let canvasContext = uni.createCanvasContext('canvas', this);
let boxLeft = this.boxLeft;
let boxTop = this.boxTop;
let boxWidth = this.boxWidth;
let boxHeight = this.boxHeight;
let sx = Math.ceil(boxLeft * imageStageRatio);
let sy = Math.ceil(boxTop * imageStageRatio);
let sWidth = Math.ceil(boxWidth * imageStageRatio);
let sHeight = Math.ceil(boxHeight * imageStageRatio);
let dx = 0;
let dy = 0;
let dWidth = Math.ceil(sWidth * pixelRatio);
let dHeight = Math.ceil(sHeight * pixelRatio);
const param = {
x: sx,
y: sy,
width: dWidth,
height: dHeight,
rotate: 0,
scaleX: 1,
scaleY: 1
};
let _this = this
canvasContext.drawImage(imagePath, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
canvasContext.draw(false, () => {
let targetWidth = 0
let targetHeight = 0
let mode = _this.saveMode
if (_this.IsEmpty(mode) || mode == "None") {
targetWidth = sWidth
targetHeight = sHeight
} else {
if (mode.startsWith("ScaleTo")) {
let w = Number(mode.substring(7, mode.length))
if (isNaN(w)) {
targetWidth = sWidth
targetHeight = sHeight
} else {
//
targetWidth = w
targetHeight = sHeight * w / sWidth
}
} else if (mode.startsWith("Max")) {
let w = Number(mode.substring(3, mode.length))
if (isNaN(w)) {
targetWidth = sWidth
targetHeight = sHeight
} else {
if (sWidth > w) {
//
targetWidth = w
targetHeight = sHeight * w / sWidth
} else {
targetWidth = sWidth
targetHeight = sHeight
}
}
} else {
targetWidth = sWidth
targetHeight = sHeight
}
}
uni.canvasToTempFilePath({
x: dx,
y: dy,
width: dWidth,
height: dHeight,
destWidth: targetWidth,
destHeight: targetHeight,
canvasId: 'canvas',
fileType: this.outputFileType,
quality: this.quality,
success: res => {
this.$emit('complete', {
param,
path: res.tempFilePath,
source: this.imagePath
});
}
},
this
);
});
},
touchMove(e) {
let targetId = e.target.id;
let touch = e.touches[0];
let pageX = touch.pageX;
let pageY = touch.pageY;
let offsetX = pageX - touchStartX;
let offsetY = pageY - touchStartY;
if (targetId == 'box') {
let newBoxLeft = touchStartBoxLeft + offsetX;
let newBoxTop = touchStartBoxTop + offsetY;
if (newBoxLeft < 0) {
newBoxLeft = 0;
}
if (newBoxTop < 0) {
newBoxTop = 0;
}
if (newBoxLeft + touchStartBoxWidth > stageWidth) {
newBoxLeft = stageWidth - touchStartBoxWidth;
}
if (newBoxTop + touchStartBoxHeight > stageHeight) {
newBoxTop = stageHeight - touchStartBoxHeight;
}
this.boxLeft = newBoxLeft;
this.boxTop = newBoxTop;
} else if (targetId == 'lt') {
if (this.aspectRatio) {
offsetY = offsetX / this.aspectRatio;
}
let newBoxLeft = touchStartBoxLeft + offsetX;
let newBoxTop = touchStartBoxTop + offsetY;
if (newBoxLeft < 0) {
newBoxLeft = 0;
}
if (newBoxTop < 0) {
newBoxTop = 0;
}
if (touchStartBoxLeft + touchStartBoxWidth - newBoxLeft < minBoxWidth) {
newBoxLeft = touchStartBoxLeft + touchStartBoxWidth - minBoxWidth;
}
if (touchStartBoxTop + touchStartBoxHeight - newBoxTop < minBoxHeight) {
newBoxTop = touchStartBoxTop + touchStartBoxHeight - minBoxHeight;
}
let newBoxWidth = touchStartBoxWidth - (newBoxLeft - touchStartBoxLeft);
let newBoxHeight = touchStartBoxHeight - (newBoxTop - touchStartBoxTop);
//
if (newBoxTop == 0 && this.aspectRatio && newBoxLeft != 0) {
newBoxWidth = newBoxHeight * this.aspectRatio;
newBoxLeft = touchStartBoxWidth - newBoxWidth + touchStartBoxLeft;
}
if (newBoxLeft == 0 && this.aspectRatio) {
newBoxHeight = newBoxWidth / this.aspectRatio;
newBoxTop = touchStartBoxHeight - newBoxHeight + touchStartBoxTop;
}
if (newBoxWidth == minBoxWidth && this.aspectRatio) {
newBoxHeight = newBoxWidth / this.aspectRatio;
newBoxTop = touchStartBoxHeight - newBoxHeight + touchStartBoxTop;
}
this.boxTop = newBoxTop;
this.boxLeft = newBoxLeft;
this.boxWidth = newBoxWidth;
this.boxHeight = newBoxHeight;
} else if (targetId == 'rt') {
if (this.aspectRatio) {
offsetY = -offsetX / this.aspectRatio;
}
let newBoxWidth = touchStartBoxWidth + offsetX;
if (newBoxWidth < minBoxWidth) {
newBoxWidth = minBoxWidth;
}
if (touchStartBoxLeft + newBoxWidth > stageWidth) {
newBoxWidth = stageWidth - touchStartBoxLeft;
}
let newBoxTop = touchStartBoxTop + offsetY;
if (newBoxTop < 0) {
newBoxTop = 0;
}
if (touchStartBoxTop + touchStartBoxHeight - newBoxTop < minBoxHeight) {
newBoxTop = touchStartBoxTop + touchStartBoxHeight - minBoxHeight;
}
let newBoxHeight = touchStartBoxHeight - (newBoxTop - touchStartBoxTop);
//
if (newBoxTop == 0 && this.aspectRatio && newBoxWidth != stageWidth - touchStartBoxLeft) {
newBoxWidth = newBoxHeight * this.aspectRatio;
}
if (newBoxWidth == stageWidth - touchStartBoxLeft && this.aspectRatio) {
newBoxHeight = newBoxWidth / this.aspectRatio;
newBoxTop = touchStartBoxHeight - newBoxHeight + touchStartBoxTop;
}
if (newBoxWidth == minBoxWidth && this.aspectRatio) {
newBoxHeight = newBoxWidth / this.aspectRatio;
newBoxTop = touchStartBoxHeight - newBoxHeight + touchStartBoxTop;
}
this.boxTop = newBoxTop;
this.boxHeight = newBoxHeight;
this.boxWidth = newBoxWidth;
} else if (targetId == 'lb') {
if (this.aspectRatio) {
offsetY = -offsetX / this.aspectRatio;
}
let newBoxLeft = touchStartBoxLeft + offsetX;
if (newBoxLeft < 0) {
newBoxLeft = 0;
}
if (touchStartBoxLeft + touchStartBoxWidth - newBoxLeft < minBoxWidth) {
newBoxLeft = touchStartBoxLeft + touchStartBoxWidth - minBoxWidth;
}
let newBoxWidth = touchStartBoxWidth - (newBoxLeft - touchStartBoxLeft);
let newBoxHeight = touchStartBoxHeight + offsetY;
if (newBoxHeight < minBoxHeight) {
newBoxHeight = minBoxHeight;
}
if (touchStartBoxTop + newBoxHeight > stageHeight) {
newBoxHeight = stageHeight - touchStartBoxTop;
}
//
if (newBoxHeight == stageHeight - touchStartBoxTop && this.aspectRatio && newBoxLeft != 0) {
newBoxWidth = newBoxHeight * this.aspectRatio;
newBoxLeft = touchStartBoxWidth - newBoxWidth + touchStartBoxLeft;
}
if (newBoxLeft == 0 && this.aspectRatio) {
newBoxHeight = newBoxWidth / this.aspectRatio;
}
if (newBoxWidth == minBoxWidth && this.aspectRatio) {
newBoxHeight = newBoxWidth / this.aspectRatio;
}
this.boxLeft = newBoxLeft;
this.boxWidth = newBoxWidth;
this.boxHeight = newBoxHeight;
} else if (targetId == 'rb') {
if (this.aspectRatio) {
offsetY = offsetX / this.aspectRatio;
}
let newBoxWidth = touchStartBoxWidth + offsetX;
if (newBoxWidth < minBoxWidth) {
newBoxWidth = minBoxWidth;
}
if (touchStartBoxLeft + newBoxWidth > stageWidth) {
newBoxWidth = stageWidth - touchStartBoxLeft;
}
let newBoxHeight = touchStartBoxHeight + offsetY;
if (newBoxHeight < minBoxHeight) {
newBoxHeight = minBoxHeight;
}
if (touchStartBoxTop + newBoxHeight > stageHeight) {
newBoxHeight = stageHeight - touchStartBoxTop;
}
//
if (newBoxHeight == stageHeight - touchStartBoxTop && this.aspectRatio && newBoxWidth != stageWidth -
touchStartBoxLeft) {
newBoxWidth = newBoxHeight * this.aspectRatio;
}
if (newBoxWidth == stageWidth - touchStartBoxLeft && this.aspectRatio) {
newBoxHeight = newBoxWidth / this.aspectRatio;
}
if (newBoxWidth == minBoxWidth && this.aspectRatio) {
newBoxHeight = newBoxWidth / this.aspectRatio;
}
this.boxWidth = newBoxWidth;
this.boxHeight = newBoxHeight;
}
},
touchStart(e) {
let touch = e.touches[0];
let pageX = touch.pageX;
let pageY = touch.pageY;
touchStartX = pageX;
touchStartY = pageY;
touchStartBoxLeft = this.boxLeft;
touchStartBoxTop = this.boxTop;
touchStartBoxWidth = this.boxWidth;
touchStartBoxHeight = this.boxHeight;
},
close(force = true) {
this.show = false;
if (force) {
this.imagePath = ''
}
},
init(src) {
let _this = this
if (!src) {
return '';
}
this.imagePath = src;
uni.showLoading({
mask: true,
title: '载入图片中'
});
uni.createSelectorQuery()
.in(this)
.select('.cropper-body')
.boundingClientRect(rect => {
layoutLeft = rect.left;
layoutTop = rect.top;
layoutWidth = rect.width;
layoutHeight = rect.height;
uni.getImageInfo({
src: _this.imagePath,
success: imageInfo => {
imageWidth = imageInfo.width;
imageHeight = imageInfo.height;
let imageWH = imageWidth / imageHeight;
let layoutWH = layoutWidth / layoutHeight;
if (imageWH >= layoutWH) {
stageWidth = layoutWidth;
stageHeight = stageWidth / imageWH;
imageStageRatio = imageHeight / stageHeight;
} else {
stageHeight = layoutHeight;
stageWidth = layoutHeight * imageWH;
imageStageRatio = imageWidth / stageWidth;
}
stageLeft = (layoutWidth - stageWidth) / 2;
stageTop = (layoutHeight - stageHeight) / 2;
minBoxWidth = stageWidth * _this.minBoxWidthRatio;
minBoxHeight = stageHeight * _this.minBoxHeightRatio;
let boxWidth = stageWidth * _this.initialBoxWidthRatio;
let boxHeight = stageHeight * _this.initialBoxHeightRatio;
if (_this.aspectRatio) {
boxHeight = boxWidth / _this.aspectRatio;
}
if (boxHeight > stageHeight) {
boxHeight = stageHeight;
boxWidth = boxHeight * _this.aspectRatio;
}
let boxLeft = (stageWidth - boxWidth) / 2;
let boxTop = (stageHeight - boxHeight) / 2;
_this.canvasWidth = imageWidth * pixelRatio;
_this.canvasHeight = imageHeight * pixelRatio;
_this.stageLeft = stageLeft;
_this.stageTop = stageTop;
_this.stageWidth = stageWidth;
_this.stageHeight = stageHeight;
_this.boxWidth = boxWidth;
_this.boxHeight = boxHeight;
_this.boxLeft = boxLeft;
_this.boxTop = boxTop;
setTimeout(() => {
uni.hideLoading();
_this.show = true;
}, 100);
},
fail: () => {
uni.showToast({
icon: 'none',
title: '图片载入失败'
});
}
});
})
.exec();
}
}
};
</script>
<style lang="scss">
.cropper {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
background-color: #000;
z-index: -1000000;
opacity: 0;
&.show {
z-index: 9901;
opacity: 1;
}
.cropper-head {
position: fixed;
top: 0;
width: 750rpx;
z-index: 6;
height: calc(var(--status-bar-height) + 88rpx);
padding-top: var(--status-bar-height);
display: flex;
justify-content: flex-start;
align-items: center;
}
.cropper-btn {
height: 64rpx;
margin: 0 20rpx;
padding: 0 30rpx;
line-height: 64rpx;
color: #fff;
font-size: 26rpx;
}
.cropper-body {
margin: calc(var(--status-bar-height) + 88rpx) 30rpx 0 30rpx;
height: calc(100vh - var(--status-bar-height) - 88rpx - 100rpx - var(--safe-area-inset-bottom));
position: relative;
}
.cropper-bottom {
height: calc(var(--safe-area-inset-bottom) + 100rpx);
padding-top: var(--safe-area-inset-bottom);
display: flex;
align-items: center;
justify-content: space-between;
position: fixed;
z-index: 6;
width: 750rpx;
bottom: 0;
}
.cropper-ok {
color: #39f;
}
// Android
.cropper-image {
position: absolute;
width: 100%;
height: 100%;
}
.cropper-stage {
position: absolute;
.cropper-box {
position: absolute;
border: 4rpx solid #ddd;
box-sizing: border-box;
box-shadow: 0 0 0 2000rpx rgba(0, 0, 0, 0.5);
.lt {
position: absolute;
height: 48rpx;
width: 48rpx;
left: -6rpx;
top: -6rpx;
border-left: 12rpx solid #ffffff;
border-top: 12rpx solid #ffffff;
}
.lb {
position: absolute;
height: 48rpx;
width: 48rpx;
left: -6rpx;
bottom: -6rpx;
border-left: 12rpx solid #ffffff;
border-bottom: 12rpx solid #ffffff;
}
.rt {
position: absolute;
height: 48rpx;
width: 48rpx;
right: -6rpx;
top: -6rpx;
border-right: 12rpx solid #ffffff;
border-top: 12rpx solid #ffffff;
}
.rb {
position: absolute;
height: 48rpx;
width: 48rpx;
right: -6rpx;
bottom: -6rpx;
border-right: 12rpx solid #ffffff;
border-bottom: 12rpx solid #ffffff;
}
.line-v,
.line-h {
position: absolute;
opacity: 0.5;
}
.line-v {
width: 2rpx;
border-left: 2rpx dashed #fff;
height: 100%;
}
.line-h {
height: 2rpx;
border-bottom: 2rpx dashed #fff;
width: 100%;
}
}
}
.cropper-canvas {
position: fixed;
background-color: red;
left: 5000rpx;
}
}
//
page {
--safe-area-inset-top: 0px;
--safe-area-inset-right: 0px;
--safe-area-inset-bottom: 0px;
--safe-area-inset-left: 0px;
@supports (top: constant(safe-area-inset-top)) {
--safe-area-inset-top: constant(safe-area-inset-top);
--safe-area-inset-right: constant(safe-area-inset-right);
--safe-area-inset-bottom: constant(safe-area-inset-bottom);
--safe-area-inset-left: constant(safe-area-inset-left);
}
@supports (top: env(safe-area-inset-top)) {
--safe-area-inset-top: env(safe-area-inset-top);
--safe-area-inset-right: env(safe-area-inset-right);
//--safe-area-inset-bottom: 12px;
--safe-area-inset-bottom: env(safe-area-inset-bottom);
--safe-area-inset-left: env(safe-area-inset-left);
}
}
</style>

79
components/cropper/readme.md

@ -0,0 +1,79 @@
#### 【注意】由于赶时间,只做了功能性测试,兼容性测试暂时无法进行,更不用说优化测试了。欢迎有遇到问题的留言,或者自己动手修改也可以。
#### 【注意】因为属于业余时间开发的一个项目所依赖的插件,所以更新较缓慢。
### 使用方式
```javascript
import Cropper from '@/components/cropper/cropper.vue';
```
```html
<cropper :imagePath="originalFaceSrc" :aspectRatio="1" @complete="complete" @cancel="cancel" v-if="handleFaceStatus"></cropper>
```
使用v-if控制显示,mounted内执行init方法,使用简单,操作便利。
## 参数
| 参数 | 类型 | 解释 | 默认 | 必填 | 备注 |
| ------------ | ------------ | ------------ | ------------ | ------------ | ------------ |
| quality | Number | 返回图片质量 | 1 | 否 | 如返回的图片仅需预览不用上传,请适当调低 |
| imagePath | String | 原始图片路径 |"" | 是 | blob、静态图片资源地址或者base64(未测试) |
| outputFileType | String | 目标文件的类型 | jpg | 否 | jpg:输出jpg格式图片;png:输出png格式图片 |
| aspectRatio | Number, null | 目标图片的宽高比 | null | 否 | null,即不限制剪裁宽高比。aspectRatio需大于0 |
| minBoxWidthRatio | Number | 最小剪裁尺寸与原图尺寸的比率(宽)| 0.15 | 否 | 宽度最小剪裁到原图的0.15宽 |
| minBoxHeightRatio | Number | 最小剪裁尺寸与原图尺寸的比率(高)| 0.15 | 否 | 当设置aspectRatio时,minBoxHeight值设置无效。minBoxHeight值由minBoxWidth 和 aspectRatio自动计算得到 |
| initialBoxWidthRatio | Number | 剪裁框初始大小比率(宽) | 0.8 | 否 | 裁框默认宽度为图片宽度的0.8倍 |
| initialBoxHeightRatio| Number | 剪裁框初始大小比率(高) | 0.8 | 否 | 当设置aspectRatio时,initialBoxHeightRatio值设置无效。initialBoxHeightRatio值由initialBoxWidthRatio 和 aspectRatio自动计算得到|
| saveMode | String | 保存模式 | "None" | 否 | None(无限制)ScaleTo500(缩放到指定的尺寸 如 500宽度) Max500(不超过500宽度) |
## 事件
| 事件名 | 解释 | 参数 | 备注 |
| ------------ | ------------ | ------------ | ------------ |
| reset | 用户点击重做后触发 | 无 | 可能无用,但留一个给开发者备用 |
| cancel | 用户点击取消后触发 | 无 | 开发者可以将组件置为消失v-if(尽量不要用v-show) |
| complete | 用户点击裁剪后触发 | path:路径,h5为base64,app与小程序为临时路径;param:裁切参数,见【备注1】 | 核心功能 |
### 备注1
param:
```json
x:Number, //裁切左上角X坐标
y:Number, //裁切左上角Y坐标
width:Number, //裁切宽度
height:Number, //裁切高度
rotate:Number, //旋转(未实现,预留)
scaleX:Number, //X轴缩放(未实现,预留)
scaleY:Number, //Y轴缩放(未实现,预留)
```
### 备注2
本组件样式支持iphone安全范围但需要手动开启
请在插件被样式中最后备注的地方解除备注即可。
控制台会报错,无视即可。
或者复制如下样式样式代码
```css
// 安全域兼容样式
page {
--safe-area-inset-top: 0px;
--safe-area-inset-right: 0px;
--safe-area-inset-bottom: 0px;
--safe-area-inset-left: 0px;
@supports (top: constant(safe-area-inset-top)) {
--safe-area-inset-top: constant(safe-area-inset-top);
--safe-area-inset-right: constant(safe-area-inset-right);
--safe-area-inset-bottom: constant(safe-area-inset-bottom);
--safe-area-inset-left: constant(safe-area-inset-left);
}
@supports (top: env(safe-area-inset-top)) {
--safe-area-inset-top: env(safe-area-inset-top);
--safe-area-inset-right: env(safe-area-inset-right);
//--safe-area-inset-bottom: 12px;
--safe-area-inset-bottom: env(safe-area-inset-bottom);
--safe-area-inset-left: env(safe-area-inset-left);
}
}
```

163
components/drop-item/drop-item.vue

@ -0,0 +1,163 @@
<template>
<view :style="{'height':navHeightPx,'display':'flex','flex-direction':'row','align-items':'center'}"
v-if="btnType!=0">
<view class="uni-combox__input-box">
<image v-if="btnType==2 && filterCandidatesLength > 0" :style="{'height':navHeightPx,'width':navHeightPx}"
mode="aspectFit" :src="btnSource" @click.stop="toggleSelector">
</image>
<text v-if="btnType==1 && filterCandidatesLength > 0"
:style="{'height':navHeightPx,'line-height':navHeightPx,'font-size':'28rpx','color':'#FFFFFF','padding-left':'20rpx','padding-right':'20rpx'}"
@click.stop="toggleSelector">{{btnSource}}</text>
<view>
<view class="uni-combox__selector" v-if="showSelector" :style="{'margin-left': offsetPx}">
<scroll-view scroll-y="true" class="uni-combox__selector-scroll">
<view class="uni-combox__selector-empty" v-if="filterCandidatesLength === 0">
<text>{{emptyTips}}</text>
</view>
<view class="uni-combox__selector-item" v-for="(item,index) in candidates" :key="index"
@click="onSelectorClick(index)">
<image :src="item.src" class="modal-img"></image>
<text
style="display:-webkit-box;-webkit-box-orient:vertical; -webkit-line-clamp:1; overflow:hidden;">{{item.name}}</text>
</view>
</scroll-view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'uni-combox',
props: {
btnType: {
type: String,
// 0使 1使 2使
default: "0"
},
btnSource: {
type: String,
default: ""
},
emptyTips: {
type: String,
default: '无'
},
candidates: {
type: Array,
default () {
return []
}
}
},
data() {
return {
showSelector: false,
navHeightPx: "",
navWidth: "",
windowWidth: "",
offsetPx: ""
}
},
computed: {
filterCandidatesLength() {
return this.candidates.length;
}
},
methods: {
toggleSelector() {
this.showSelector = !this.showSelector
},
onSelectorClick(index) {
this.showSelector = false
this.$emit('onselect', index, this.candidates)
},
closeDropItem() {
this.showSelector = false
}
},
created() {
this.navHeightPx = getApp().globalData.navHeightPx
this.navWidth = getApp().globalData.navWidth
this.windowWidth = getApp().globalData.windowWidth
let offset = this.windowWidth - this.navWidth;
this.offsetPx = "-160rpx"
//
// #ifdef MP-WEIXIN||MP-BAID||MP-QQ||MP-TOUTIAO
this.offsetPx = "-" + (this.windowWidth - this.navWidth - 20) + "px"
// #endif
}
}
</script>
<style lang="scss" scoped>
.uni-combox__input-box {
position: relative;
display: flex;
flex-direction: row;
align-items: center;
}
.uni-combox__selector {
box-sizing: border-box;
position: absolute;
top: 42px;
left: 0;
width: 100%;
background-color: #FFFFFF;
border-radius: 6px;
box-shadow: #DDDDDD 4px 4px 8px, #DDDDDD -4px -4px 8px;
z-index: 2;
width: 240rpx;
}
.uni-combox__selector-scroll {
max-height: 200px;
box-sizing: border-box;
}
.uni-combox__selector::before {
content: '';
position: absolute;
width: 0;
height: 0;
border-bottom: solid 6px #FFFFFF;
border-right: solid 6px transparent;
border-left: solid 6px transparent;
left: 85%;
top: -6px;
margin-left: -6px;
}
.uni-combox__selector-empty,
.uni-combox__selector-item {
display: flex;
line-height: 80rpx;
font-size: 14px;
text-align: center;
border-bottom: solid 1px #DDDDDD;
margin: 0px 10px;
flex-direction: row;
align-items: center;
justify-content: center;
}
.uni-combox__selector-empty:last-child,
.uni-combox__selector-item:last-child {
border-bottom: none;
}
.modal-img {
width: 40rpx;
height: 40rpx;
margin-right: 5rpx;
}
</style>

BIN
components/firstui/.DS_Store

Binary file not shown.

379
components/firstui/fui-button/fui-button.vue

@ -0,0 +1,379 @@
<template>
<view class="fui-button__wrap"
:style="{width: width,height: height,marginTop:margin[0] || 0, marginRight:margin[1]||0,marginBottom:margin[2] || margin[0]||0,marginLeft:margin[3] || margin[1]||0,borderRadius: radius}"
@touchstart="handleStart" @touchend="handleClick" @touchcancel="handleEnd" @tap="handleTap">
<button class="fui-button" :class="[
bold ? 'fui-text__bold' : '',
time && (plain || type==='link') ? 'fui-button__opacity' : '',
disabled && !disabledBackground ? 'fui-button__opacity' : '',
!background && !disabledBackground && !plain?('fui-button__'+type):'',
!width || width==='100%' || width==='true'?'fui-button__flex-1':'',
time && !plain && type!=='link' ? 'fui-button__active' : ''
]" :style="{
width: width,
height: height,
lineHeight: height,
background: disabled && disabledBackground ? disabledBackground : (plain ? 'transparent' : background),
borderWidth:borderWidth,
borderColor: borderColor ? borderColor : disabled && disabledBackground ? disabledBackground : (background || 'transparent'),
borderRadius: radius,
fontSize: size + 'rpx',
color: disabled && disabledBackground ? disabledColor : color
}" :loading="loading" :form-type="formType" :open-type="openType" @getuserinfo="bindgetuserinfo"
@getphonenumber="bindgetphonenumber" @contact="bindcontact" @error="binderror"
@opensetting="bindopensetting" :disabled="disabled" :scope="scope">
<text class="fui-button__text"
:class="{'fui-btn__gray-color':!background && !disabledBackground && !plain && type==='gray' && color==='#fff','fui-text__bold':bold}"
v-if="text"
:style="{fontSize: size + 'rpx',lineHeight:size + 'rpx',color: disabled && disabledBackground ? disabledColor : color}">{{text}}</text>
<slot></slot>
</button>
</view>
</template>
<script>
export default {
name: 'fui-button',
emits: ['click', 'getuserinfo', 'contact', 'getphonenumber', 'error', 'opensetting'],
// #ifndef VUE3
// #ifdef MP-WEIXIN
behaviors: ['wx://form-field-button'],
// #endif
// #endif
props: {
//primarysuccess warningdangerlinkpurplegray
type: {
type: String,
default: 'primary'
},
//type
background: {
type: String,
default: ''
},
//
text: {
type: String,
default: ''
},
//
color: {
type: String,
default: '#fff'
},
//
disabledBackground: {
type: String,
default: ''
},
//
disabledColor: {
type: String,
default: ''
},
borderWidth: {
type: String,
// #ifdef APP-NVUE
default: '0.5px'
// #endif
// #ifndef APP-NVUE
default: '1rpx'
// #endif
},
borderColor: {
type: String,
default: ''
},
//
width: {
type: String,
default: '100%'
},
//
height: {
type: String,
default: '96rpx'
},
//rpx
size: {
type: [Number, String],
default: 32
},
bold: {
type: Boolean,
default: false
},
//['20rpx','30rpx','20rpx','30rpx']->[]
margin: {
type: Array,
default () {
return ['0', '0']
}
},
//
radius: {
type: String,
default: '16rpx'
},
plain: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
},
loading: {
type: Boolean,
default: false
},
formType: {
type: String,
default: ''
},
openType: {
type: String,
default: ''
},
//
// open-type getAuthorize scope phoneNumberuserInfo
scope: {
type: String,
default: ''
},
index: {
type: [Number, String],
default: 0
}
},
data() {
return {
time: 0,
trigger: false,
tap: false
};
},
methods: {
handleStart() {
if (this.disabled) return;
this.trigger = false;
this.tap = true;
if (new Date().getTime() - this.time <= 150) return;
this.trigger = true;
this.time = new Date().getTime();
},
handleClick() {
if (this.disabled || !this.trigger) return;
//
this.time = 0;
this.$emit('click', {
index: Number(this.index)
});
},
handleTap() {
// #ifdef H5
if (this.disabled || this.tap) return;
this.$emit('click', {
index: Number(this.index)
});
// #endif
},
handleEnd() {
if (this.disabled) return;
setTimeout(() => {
this.time = 0;
}, 150);
},
bindgetuserinfo({
detail = {}
} = {}) {
this.$emit('getuserinfo', detail);
},
bindcontact({
detail = {}
} = {}) {
this.$emit('contact', detail);
},
bindgetphonenumber({
detail = {}
} = {}) {
this.$emit('getphonenumber', detail);
},
binderror({
detail = {}
} = {}) {
this.$emit('error', detail);
},
bindopensetting({
detail = {}
} = {}) {
this.$emit('opensetting', detail);
}
}
};
</script>
<style scoped>
.fui-button__wrap {
position: relative;
}
.fui-button {
/* #ifdef APP-NVUE */
border-width: 0.5px;
/* #endif */
/* #ifndef APP-NVUE */
border-width: 1rpx;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
/* #endif */
border-style: solid;
position: relative;
padding-left: 0;
padding-right: 0;
overflow: hidden;
/* #ifndef APP-NVUE */
transform: translateZ(0);
-webkit-touch-callout: none;
-webkit-user-select: none;
user-select: none;
/* #endif */
}
.fui-button__flex-1 {
flex: 1;
/* #ifndef APP-NVUE */
width: 100%;
/* #endif */
}
.fui-button::after {
border: 0;
}
/* #ifndef APP-NVUE */
.fui-button__active {
overflow: hidden !important;
}
.fui-button__active::after {
content: ' ';
background-color: var(--fui-bg-color-hover, rgba(0, 0, 0, 0.2));
position: absolute;
width: 100%;
height: 100%;
left: 0;
right: 0;
top: 0;
transform: none;
z-index: 1;
border-radius: 0;
}
/* #endif */
.fui-button__text {
text-align: center;
flex-direction: row;
align-items: center;
justify-content: center !important;
padding-left: 0 !important;
}
.fui-button__opacity {
opacity: 0.5;
}
.fui-text__bold {
font-weight: bold;
}
.fui-button__link {
border-color: transparent !important;
background: transparent !important;
}
.fui-button__primary {
/* #ifdef APP-NVUE */
border-color: #465CFF !important;
background: #465CFF !important;
/* #endif */
/* #ifndef APP-NVUE */
border-color: var(--fui-color-primary, #465CFF) !important;
background: var(--fui-color-primary, #465CFF) !important;
/* #endif */
}
.fui-button__success {
/* #ifdef APP-NVUE */
border-color: #09BE4F !important;
background: #09BE4F !important;
/* #endif */
/* #ifndef APP-NVUE */
border-color: var(--fui-color-success, #09BE4F) !important;
background: var(--fui-color-success, #09BE4F) !important;
/* #endif */
}
.fui-button__warning {
/* #ifdef APP-NVUE */
border-color: #FFB703 !important;
background: #FFB703 !important;
/* #endif */
/* #ifndef APP-NVUE */
border-color: var(--fui-color-warning, #FFB703) !important;
background: var(--fui-color-warning, #FFB703) !important;
/* #endif */
}
.fui-button__danger {
/* #ifdef APP-NVUE */
border-color: #FF2B2B !important;
background: #FF2B2B !important;
/* #endif */
/* #ifndef APP-NVUE */
border-color: var(--fui-color-danger, #FF2B2B) !important;
background: var(--fui-color-danger, #FF2B2B) !important;
/* #endif */
}
.fui-button__purple {
/* #ifdef APP-NVUE */
border-color: #6831FF !important;
background: #6831FF !important;
/* #endif */
/* #ifndef APP-NVUE */
border-color: var(--fui-color-purple, #6831FF) !important;
background: var(--fui-color-purple, #6831FF) !important;
/* #endif */
}
.fui-button__gray {
/* #ifdef APP-NVUE */
border-color: #F8F8F8 !important;
background: #F8F8F8 !important;
/* #endif */
/* #ifndef APP-NVUE */
border-color: var(--fui-bg-color-content, #F8F8F8) !important;
background: var(--fui-bg-color-content, #F8F8F8) !important;
color: var(--fui-color-primary, #465CFF) !important;
/* #endif */
}
.fui-btn__gray-color {
/* #ifdef APP-NVUE */
color: #465CFF !important;
/* #endif */
/* #ifndef APP-NVUE */
color: var(--fui-color-primary, #465CFF) !important;
/* #endif */
}
</style>

445
components/firstui/fui-dropdown-menu/fui-dropdown-menu.vue

@ -0,0 +1,445 @@
<template>
<view class="fui-dropdown__menu">
<slot></slot>
<view class="fui-dropdown__menu-list"
:class="{'fui-ddm__down':direction!=='up','fui-ddm__up':direction==='up','fui-ddm__down-show':isShow && direction!=='up','fui-ddm__up-show':isShow && direction==='up'}"
:style="getStyles">
<scroll-view class="fui-ddm__scroll" scroll-y :style="{maxHeight:maxHeight+'rpx',minWidth:minWidth+'rpx'}">
<view class="fui-dropdown__menu-item" :style="{background:background,padding:padding}"
:class="{'fui-ddm__reverse':isReverse,'fui-ddm__item-line':splitLine && itemList.length-1!==index}"
v-for="(model,index) in itemList" :key="index" @tap.stop="itemClick(index)">
<view class="fui-ddm__checkbox"
:class="{'fui-is__checkmark':isCheckMark,'fui-ddm__checkbox-color':(!checkboxColor || checkboxColor=='true') && model.checked && !isCheckMark}"
:style="{background:model.checked && !isCheckMark ?checkboxColor:'transparent',borderColor:model.checked && !isCheckMark ?checkboxColor:borderColor}"
v-if="isCheckbox">
<view class="fui-ddm__checkmark"
:style="{borderBottomColor:checkmarkColor,borderRightColor:checkmarkColor}"
v-if="model.checked"></view>
</view>
<view class="fui-ddm__flex">
<view class="fui-ddm__icon-box"
:class="{'fui-ddm__icon-ml':!isReverse && isCheckbox,'fui-ddm__icon-mr':isReverse}"
:style="{width:iconWidth+'rpx',height:iconWidth+'rpx'}" v-if="model.src">
<image src="/static/images/common/logo.png"
:style="{width:iconWidth+'rpx',height:iconWidth+'rpx'}"></image>
</view>
<text class="fui-ddm__item-text"
:class="{'fui-ddm__text-pl':!isReverse && (isCheckbox || model.src),'fui-ddm__text-pr':isReverse && (isCheckbox || model.src)}"
:style="{fontSize:size+'rpx',color:selectedColor && model.checked?selectedColor:color}">{{model.text}}</text>
</view>
</view>
</scroll-view>
</view>
<view class="fui-ddm__mask" :style="{backgrround:maskBackground}" v-if="isShow && isMask" @tap="close(1)">
</view>
</view>
</template>
<script>
/*!
* 下拉菜单
* 由于weex android上overflow仅支持hidden所以该组件不支持Nvue端
* Nvue端使用组件fui-dropdown-list或fui-select组件替代该组件
*/
export default {
name: "fui-dropdown-menu",
emits: ['click', 'close'],
props: {
options: {
type: Array,
default () {
return []
}
},
maxHeight: {
type: [Number, String],
default: 400
},
minWidth: {
type: [Number, String],
default: 0
},
left: {
type: [Number, String],
default: 0
},
//right0left
right: {
type: [Number, String],
default: -1
},
background: {
type: String,
default: '#fff'
},
radius: {
type: [Number, String],
default: 0
},
padding: {
type: String,
default: '32rpx'
},
isCheckbox: {
type: Boolean,
default: true
},
//
checkboxColor: {
type: String,
// #ifdef APP-NVUE
default: '#465CFF'
// #endif
// #ifndef APP-NVUE
default: ''
// #endif
},
//
borderColor: {
type: String,
default: '#ccc'
},
//
isCheckMark: {
type: Boolean,
default: false
},
//
checkmarkColor: {
type: String,
default: '#fff'
},
//
isReverse: {
type: Boolean,
default: false
},
//线
splitLine: {
type: Boolean,
default: false
},
iconWidth: {
type: [Number, String],
default: 48
},
size: {
type: [Number, String],
default: 32
},
color: {
type: String,
default: '#181818'
},
selectedColor: {
type: String,
default: ''
},
isMask: {
type: Boolean,
default: true
},
maskBackground: {
type: String,
default: 'transparent'
},
//down/up
direction: {
type: String,
default: 'down'
}
},
watch: {
options(newVal) {
this.initData(newVal)
}
},
data() {
return {
itemList: [],
isShow: false
};
},
computed: {
getStyles() {
let styles = `border-radius:${this.radius}rpx;background:${this.background};`
let right = Number(this.right || 0)
if (right >= 0) {
styles += 'right:0;'
} else {
styles += 'left:0;'
}
return styles
}
},
created() {
this.initData(this.options)
},
methods: {
initData(vals) {
if (vals && vals.length > 0) {
if (typeof vals[0] !== 'object') {
vals = vals.map(item => {
return {
text: item,
checked: false
}
})
} else {
vals.map(item => {
item.checked = item.checked || false
})
}
this.itemList = vals;
}
},
itemClick(index) {
let item = this.itemList[index]
let vals = [...this.itemList]
vals.forEach((item, idx) => {
if (index === idx) {
item.checked = true
} else {
item.checked = false
}
})
this.itemList = vals;
this.$emit('click', {
index: index,
...item
})
this.close(2)
},
close(type) {
this.isShow = false;
if (type === 1) {
this.$emit('close', {})
}
},
show() {
this.isShow = true;
}
}
}
</script>
<style scoped>
.fui-dropdown__menu {
flex: 1;
position: relative;
}
.fui-ddm__scroll {
/* #ifndef APP-NVUE */
width: auto;
/* #endif */
}
.fui-dropdown__menu-list {
position: absolute;
box-shadow: 0 0 10rpx rgba(2, 4, 38, 0.05);
overflow: hidden;
z-index: 992;
opacity: 0;
/* #ifndef APP-NVUE */
visibility: hidden;
transition: all 0.3s ease-in-out;
/* #endif */
}
.fui-ddm__down {
transform-origin: 0 0;
bottom: 0;
/* #ifndef APP-NVUE */
transform: translate3d(0, 100%, 0) scaleY(0);
/* #endif */
}
.fui-ddm__down-show {
/* #ifndef APP-NVUE */
transform: translate3d(0, 100%, 0) scaleY(1);
visibility: visible;
/* #endif */
opacity: 1;
}
.fui-ddm__up {
transform-origin: 0 100%;
top: 0;
/* #ifndef APP-NVUE */
transform: translate3d(0, -100%, 0) scaleY(0);
/* #endif */
}
.fui-ddm__up-show {
/* #ifndef APP-NVUE */
transform: translate3d(0, -100%, 0) scaleY(1);
visibility: visible;
/* #endif */
opacity: 1;
}
.fui-ddm__mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 990;
}
.fui-dropdown__menu-item {
/* #ifndef APP-NVUE */
width: 100%;
display: flex;
box-sizing: border-box;
transform: scale(1) translateZ(0);
/* #endif */
flex: 1;
flex-direction: row;
align-items: center;
background-color: #FFFFFF;
position: relative;
/* #ifdef H5 */
cursor: pointer;
/* #endif */
}
.fui-ddm__flex {
/* #ifndef APP-NVUE */
width: 100%;
display: flex;
box-sizing: border-box;
/* #endif */
flex: 1;
flex-direction: row;
align-items: center;
}
/* #ifndef APP-NVUE */
.fui-ddm__item-line {
position: relative;
}
.fui-ddm__item-line::after {
content: '';
position: absolute;
border-bottom: 1px solid var(--fui-color-border, #EEEEEE);
/* #ifdef H5 */
transform: scaleY(0.5);
/* #endif */
/* #ifndef H5 */
transform: scaleY(0.5) translateZ(0);
/* #endif */
transform-origin: 0 100%;
bottom: 0;
right: 0;
left: 32rpx;
pointer-events: none;
}
/* #endif */
.fui-dropdown__menu-item:active {
/* #ifdef APP-NVUE */
background-color: rgba(0, 0, 0, .2) !important;
/* #endif */
/* #ifndef APP-NVUE */
background-color: var(--fui-bg-color-hover, rgba(0, 0, 0, .2)) !important;
/* #endif */
}
.fui-ddm__reverse {
justify-content: space-between;
flex-direction: row-reverse;
}
.fui-ddm__checkbox {
font-size: 0;
color: rgba(0, 0, 0, 0);
width: 40rpx;
height: 40rpx;
border-width: 1px;
border-style: solid;
/* #ifdef APP-NVUE */
border-radius: 40rpx;
/* #endif */
/* #ifndef APP-NVUE */
display: inline-flex;
box-sizing: border-box;
border-radius: 50%;
vertical-align: top;
flex-shrink: 0;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: center;
overflow: hidden;
position: relative;
}
/* #ifndef APP-NVUE */
.fui-ddm__checkbox-color {
background: var(--fui-color-primary, #465CFF) !important;
border-color: var(--fui-color-primary, #465CFF) !important;
}
/* #endif */
.fui-is__checkmark {
border-width: 0 !important;
background: transparent !important;
}
.fui-ddm__checkmark {
width: 20rpx;
height: 40rpx;
border-bottom-style: solid;
border-bottom-width: 3px;
border-bottom-color: #FFFFFF;
border-right-style: solid;
border-right-width: 3px;
border-right-color: #FFFFFF;
/* #ifndef APP-NVUE */
box-sizing: border-box;
transform: rotate(45deg) scale(0.5) translateZ(0);
/* #endif */
/* #ifdef APP-NVUE */
transform: rotate(45deg) scale(0.5);
/* #endif */
transform-origin: 54% 48%;
}
.fui-ddm__item-text {
/* #ifndef APP-NVUE */
word-break: break-all;
/* #endif */
font-weight: normal;
}
.fui-ddm__text-pl {
padding-left: 24rpx;
}
.fui-ddm__text-pr {
padding-right: 24rpx;
}
.fui-ddm__icon-box {
overflow: hidden;
background-color: #F1F4FA;
/* #ifndef APP-NVUE */
flex-shrink: 0;
/* #endif */
}
.fui-ddm__icon-ml {
margin-left: 24rpx;
}
.fui-ddm__icon-mr {
margin-right: 24rpx;
}
</style>

154
components/firstui/fui-icon/fui-icon.js

@ -0,0 +1,154 @@
export default {
"addressbook":"\ue80c",
"addfriends-fill": "\ue80a",
"addfriends": "\ue80b",
"backspace-fill": "\ue808",
"backspace": "\ue809",
"bankcard-fill": "\ue806",
"bankcard": "\ue807",
"camera-fill": "\ue804",
"camera": "\ue805",
"captcha-fill": "\ue802",
"captcha": "\ue803",
"cart-fill": "\ue800",
"cart": "\ue801",
"classify": "\ue7fe",
"classify-fill": "\ue7ff",
"comment-fill": "\ue7fc",
"comment": "\ue7fd",
"community-fill": "\ue7fa",
"community": "\ue7fb",
"coupon-fill": "\ue7f8",
"coupon": "\ue7f9",
"delete": "\ue7f6",
"delete-fill": "\ue7f7",
"edit": "\ue7f4",
"edit-fill": "\ue7f5",
"fabulous-fill": "\ue7f2",
"fabulous": "\ue7f3",
"find": "\ue7f0",
"find-fill": "\ue7f1",
"help-fill": "\ue7ee",
"help": "\ue7ef",
"home-fill": "\ue7ec",
"home": "\ue7ed",
"idcard-fill": "\ue7ea",
"idcard": "\ue7eb",
"info": "\ue7e8",
"info-fill": "\ue7e9",
"invite-fill": "\ue7e6",
"invite": "\ue7e7",
"kefu-fill": "\ue7e4",
"kefu": "\ue7e5",
"like-fill": "\ue7e2",
"like": "\ue7e3",
"location": "\ue7e0",
"location-fill": "\ue7e1",
"lock": "\ue7de",
"lock-fill": "\ue7df",
"mail-fill": "\ue7dc",
"mail": "\ue7dd",
"message": "\ue7da",
"message-fill": "\ue7db",
"mobile-fill": "\ue7d8",
"mobile": "\ue7d9",
"more": "\ue7d6",
"more-fill": "\ue7d7",
"my-fill": "\ue7d4",
"my": "\ue7d5",
"principal":"\ue80d",
"notice-fill": "\ue7d2",
"notice": "\ue7d3",
"order": "\ue7d0",
"order-fill": "\ue7d1",
"picture": "\ue7ce",
"picture-fill": "\ue7cf",
"setup-fill": "\ue7cc",
"setup": "\ue7cd",
"share": "\ue7ca",
"share-fill": "\ue7cb",
"shop": "\ue7c8",
"shop-fill": "\ue7c9",
"star-fill": "\ue7c5",
"star": "\ue7c6",
"starhalf": "\ue7c7",
"stepon-fill": "\ue7c3",
"stepon": "\ue7c4",
"wait-fill": "\ue7c1",
"wait": "\ue7c2",
"warning": "\ue7bf",
"warning-fill": "\ue7c0",
"plus": "\ue7bc",
"plussign-fill": "\ue7bd",
"plussign": "\ue7be",
"minus": "\ue7b9",
"minussign": "\ue7ba",
"minussign-fill": "\ue7bb",
"close": "\ue7b8",
"clear": "\ue7b6",
"clear-fill": "\ue7b7",
"checkbox-fill": "\ue7b5",
"checkround": "\ue7b4",
"checkbox": "\ue7b3",
"check": "\ue7b2",
"pulldown-fill": "\ue7ae",
"pullup": "\ue7af",
"pullup-fill": "\ue7b0",
"pulldown": "\ue7b1",
"roundright-fill": "\ue7ac",
"roundright": "\ue7ad",
"arrowright": "\ue7a9",
"arrowleft": "\ue7aa",
"arrowdown": "\ue7ab",
"left": "\ue7a6",
"up": "\ue7a7",
"right": "\ue7a8",
"back": "\ue7a3",
"top": "\ue7a4",
"dropdown": "\ue7a5",
"turningleft": "\ue79f",
"turningup": "\ue7a0",
"turningright": "\ue7a1",
"turningdown": "\ue7a2",
"refresh": "\ue79c",
"loading": "\ue79d",
"search": "\ue79e",
"rotate": "\ue79b",
"screen": "\ue79a",
"signin": "\ue799",
"calendar": "\ue798",
"scan": "\ue797",
"qrcode": "\ue796",
"wallet": "\ue795",
"telephone": "\ue794",
"visible": "\ue793",
"invisible": "\ue792",
"menu": "\ue78e",
"operate": "\ue78f",
"slide": "\ue790",
"list": "\ue791",
"nonetwork": "\ue78d",
"partake": "\ue78c",
"qa": "\ue78b",
"barchart": "\ue788",
"piechart": "\ue789",
"linechart": "\ue78a",
"at": "\ue787",
"face": "\ue77f",
"redpacket": "\ue780",
"suspend": "\ue781",
"link": "\ue782",
"keyboard": "\ue783",
"play": "\ue784",
"video": "\ue785",
"voice": "\ue786",
"sina": "\ue77a",
"browser": "\ue77b",
"moments": "\ue77c",
"qq": "\ue77d",
"wechat": "\ue77e",
"balance": "\ue779",
"bankcardpay": "\ue778",
"wxpay": "\ue777",
"alipay": "\ue776"
}

BIN
components/firstui/fui-icon/fui-icon.ttf

Binary file not shown.

93
components/firstui/fui-icon/fui-icon.vue

File diff suppressed because one or more lines are too long

217
components/firstui/fui-list-cell/fui-list-cell.vue

@ -0,0 +1,217 @@
<template>
<view class="fui-list__cell" :class="{'fui-highlight':highlight,'fui-list__cell-background':!background}"
:style="{paddingTop:padding[0] || 0,paddingRight:padding[1] || 0,paddingBottom:padding[2] || padding[0] || 0,paddingLeft:padding[3] || padding[1] || 0,background:background,marginTop:marginTop+'rpx',marginBottom:marginBottom+'rpx',borderRadius:radius}"
@tap="handleClick">
<view v-if="topBorder" :style="{background:borderColor,left:topLeft+'rpx',right:topRight+'rpx'}"
class="fui-cell__border-top" :class="{'fui-cell__border-color':!borderColor}"></view>
<slot></slot>
<view v-if="bottomBorder" :style="{background:borderColor,left:bottomLeft+'rpx',right:bottomRight+'rpx'}"
class="fui-cell__border-bottom" :class="{'fui-cell__border-color':!borderColor}"></view>
<view class="fui-cell__arrow" v-if="arrow" :style="{'border-color':arrowColor}">
</view>
</view>
</template>
<script>
export default {
name: "fui-list-cell",
emits: ['click'],
props: {
//padding,nvuepadding-right()
padding: {
type: Array,
default () {
return ['32rpx', '32rpx']
}
},
//margin-top rpx
marginTop: {
type: [Number, String],
default: 0
},
//margin-bottom rpx
marginBottom: {
type: [Number, String],
default: 0
},
//
// #ifdef APP-NVUE
background: {
type: String,
default: '#fff'
},
// #endif
// #ifndef APP-NVUE
background: {
type: String,
default: ''
},
// #endif
//
highlight: {
type: Boolean,
default: true
},
//
arrow: {
type: Boolean,
default: false
},
arrowColor: {
type: String,
default: '#B2B2B2'
},
//
topBorder: {
type: Boolean,
default: false
},
//
bottomBorder: {
type: Boolean,
default: true
},
//nvue
// #ifdef APP-NVUE
borderColor: {
type: String,
default: '#EEEEEE'
},
// #endif
// #ifndef APP-NVUE
borderColor: {
type: String,
default: ''
},
// #endif
//leftrpx
topLeft: {
type: [Number, String],
default: 0
},
//rightrpx
topRight: {
type: [Number, String],
default: 0
},
//leftrpx
bottomLeft: {
type: [Number, String],
default: 32
},
//rightrpx
bottomRight: {
type: [Number, String],
default: 0
},
//border-radius
radius: {
type: String,
default: '0'
},
index: {
type: Number,
default: 0
}
},
methods: {
handleClick() {
this.$emit('click', {
index: this.index
});
}
}
}
</script>
<style scoped>
.fui-list__cell {
position: relative;
flex: 1;
/* #ifndef APP-NVUE */
width: 100%;
display: flex;
box-sizing: border-box;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: space-between;
}
/* #ifdef APP-NVUE */
.fui-list__item {
flex: 1;
}
/* #endif */
.fui-cell__arrow {
height: 40rpx;
width: 40rpx;
border-width: 3px 3px 0 0;
border-style: solid;
transform: rotate(45deg) scale(0.5);
/* #ifndef APP-NVUE */
border-radius: 4rpx;
flex-shrink: 0;
margin-left: auto;
box-sizing: border-box;
/* #endif */
/* #ifdef APP-NVUE */
border-top-right-radius: 3rpx;
/* #endif */
transform-origin: center center;
margin-right: -5.8579rpx;
}
.fui-cell__border-top {
position: absolute;
top: 0;
/* #ifdef APP-NVUE */
height: 0.5px;
z-index: -1;
/* #endif */
/* #ifndef APP-NVUE */
height: 1px;
-webkit-transform: scaleY(0.5);
transform: scaleY(0.5);
transform-origin: 0 0;
z-index: 1;
/* #endif */
}
.fui-cell__border-bottom {
position: absolute;
bottom: 0;
/* #ifdef APP-NVUE */
height: 0.5px;
z-index: -1;
/* #endif */
/* #ifndef APP-NVUE */
height: 1px;
-webkit-transform: scaleY(0.5);
transform: scaleY(0.5);
transform-origin: 0 100%;
z-index: 1;
/* #endif */
}
/* #ifndef APP-NVUE */
.fui-cell__border-color {
background-color: var(--fui-color-border, #EEEEEE) !important;
}
.fui-list__cell-background {
background-color: var(--fui-bg-color, #fff);
}
/* #endif */
.fui-highlight:active {
/* #ifdef APP-NVUE */
background-color: rgba(0, 0, 0, 0.2) !important;
/* #endif */
/* #ifndef APP-NVUE */
background-color: var(--fui-bg-color-hover, rgba(0, 0, 0, 0.2)) !important;
/* #endif */
}
</style>

85
components/firstui/fui-theme/fui-theme.css

@ -0,0 +1,85 @@
/*
FirstUI组件内置的基础变量
1.如果你是组件使用者你可以通过修改这些变量的值来定制自己的组件主题实现自定义主题功能
2.如果全局修改需要在项目根目录下App.vue文件中引入此css文件
3.如果组件中有props属性是针对颜色设置默认为空值则优先级props变量如果有传值> 全局主题色
*/
/* #ifndef APP-NVUE */
page {
/* 行为相关颜色 */
--fui-color-primary: #465CFF;
--fui-color-success: #09BE4F;
--fui-color-warning: #FFB703;
--fui-color-danger: #FF2B2B;
--fui-color-purple: #6831FF;
/* 文字基本颜色、其他辅助色 */
/* 用于重量级文字信息、标题 */
--fui-color-title: #181818;
/* 用于普通级段落信息、引导词 */
--fui-color-section: #333333;
/* 用于次要标题内容 */
--fui-color-subtitle: #7F7F7F;
/* 用于底部标签、描述、次要文字信息 */
--fui-color-label: #B2B2B2;
/* 用于辅助、次要信息、禁用文字等。如:待输入状态描述文字,已点击按钮文字 */
--fui-color-minor: #CCCCCC;
--fui-color-white: #FFFFFF;
/* 链接颜色 */
--fui-color-link: #465CFF;
/* 背景颜色 */
--fui-bg-color: #ffffff;
/* 页面背景底色 */
--fui-bg-color-grey: #F1F4FA;
/* 内容模块底色 */
--fui-bg-color-content: #F8F8F8;
--fui-bg-color-red: rgba(255, 43, 43, .05);
--fui-bg-color-yellow: rgba(255, 183, 3, .1);
--fui-bg-color-purple: rgba(104, 49, 255, .05);
--fui-bg-color-green: rgba(9, 190, 79, .05);
/* 点击背景色 */
--fui-bg-color-hover: rgba(0, 0, 0, 0.2);
/* 遮罩颜色 */
--fui-bg-color-mask: rgba(0, 0, 0, 0.6);
/* 边框颜色 */
--fui-color-border: #EEEEEE;
/* 阴影颜色 */
--fui-color-shadow: rgba(2, 4, 38, 0.05);
/*禁用态的透明度 */
--fui-opacity-disabled: 0.5;
/* 图标尺寸 */
--fui-img-size-sm: 48rpx;
--fui-img-size-base: 56rpx;
--fui-img-size-middle: 64rpx;
--fui-img-size-lg: 96rpx;
/* 图片尺寸 */
--fui-img-sm: 60rpx;
--fui-img-base: 120rpx;
--fui-img-lg: 240rpx;
/* Border Radius */
--fui-border-radius-sm: 16rpx;
--fui-border-radius-base: 24rpx;
--fui-border-radius-lg: 48rpx;
--fui-border-radius-circle: 50%;
/* 水平间距 */
--fui-spacing-row-sm: 16rpx;
--fui-spacing-row-base: 24rpx;
--fui-spacing-row-lg: 32rpx;
/* 垂直间距 */
--fui-spacing-col-sm: 8rpx;
--fui-spacing-col-base: 16rpx;
--fui-spacing-col-lg: 24rpx;
}
/* #endif */

21
components/grid/grid.vue

@ -0,0 +1,21 @@
<template>
<view class="grid-out">
<slot></slot>
</view>
</template>
<script>
export default {
methods: {
}
}
</script>
<style lang="scss">
.grid-out {
display: flex;
align-content: flex-start;
flex-flow: row wrap;
}
</style>

68
components/helang-asyncSwitch/helang-asyncSwitch.vue

@ -0,0 +1,68 @@
<template>
<view class="async-switch">
<switch :checked="checked" :disabled="disabled" :color="changeColor" />
<button @click="onChange"></button>
</view>
</template>
<script>
export default {
name:'async-switch',
props:{
checked: {
type: Boolean,
default: false
},
disabled:{
type: Boolean,
default: false
},
color:{
type:String,
default:'#007aff'
},
disabledColor:{
type:String,
default:'#333'
}
},
data() {
return {
};
},
computed:{
changeColor(){
return (this.disabled ? this.disabledColor : this.color)
}
},
methods:{
onChange(){
/* 禁用状态不返回事件 */
if(this.disabled){
return;
}
this.$emit("change");
}
}
}
</script>
<style scoped lang="scss">
.async-switch{
display: inline-block;
position: relative;
&>switch{
margin: 0;
}
&>button{
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
}
}
</style>

186
components/item-drop/item-drop.vue

@ -0,0 +1,186 @@
<template>
<view style="display: flex;flex-direction: column;box-sizing: border-box;">
<view v-if="border == 1 || border ==3" class="border"></view>
<view style="width: 100%;height: 30rpx;"></view>
<view style="display: flex;flex-direction: row;align-items: center;margin-left: 16rpx;margin-right: 15rpx;">
<text style="font-size: 26rpx;color: #101010;" :class="{ 'enabled': !enabled }">{{title}}</text>
<image v-if="required" src="../../static/asterisk.png"
style="width: 14rpx;height: 16rpx;margin-left: 12rpx;flex-shrink: 0;"></image>
</view>
<view style="width: 100%;height: 30rpx;"></view>
<view style="margin-left: 16rpx;margin-right: 15rpx;" :class="{ 'enabled': !enabled }">
<!--自定义类型的选择器-->
<!--自定义数据 需提供数据源 以及 取值key-->
<picker v-if="type==0" :disabled="!enabled" :range-key="dataDetailKey" :range="data" @change="custom">
<view style="display: flex;flex-direction: row;align-items: center;width: 100%;">
<view v-if="select == undefined || select == null || select === ''"
style="font-size: 28rpx;color: #BCBCBC;">{{hint}}</view>
<view v-if="select != undefined && select != null || select != ''"
style="font-size: 28rpx;color: #2E2D2D;">{{select}}</view>
<view style="flex: 1;"></view>
<image src="../../static/drop.png"
style="width: 45rpx;height: 45rpx;margin-left: 12rpx;flex-shrink: 0;">
</image>
</view>
</picker>
<!--内置选择器 支持 男女 以及 是否-->
<picker v-if="type==1||type==2" :disabled="!enabled" range-key="detail" :range="type==1?sex:yes"
@change="preset">
<view style="display: flex;flex-direction: row;align-items: center;width: 100%;">
<view v-if="select == undefined || select == null || select === ''"
style="font-size: 28rpx;color: #BCBCBC;">{{hint}}</view>
<view v-if="select != undefined && select != null || select != ''"
style="font-size: 28rpx;color: #2E2D2D;">{{select}}</view>
<view style="flex: 1;"></view>
<image src="../../static/drop.png"
style="width: 45rpx;height: 45rpx;margin-left: 12rpx;flex-shrink: 0;">
</image>
</view>
</picker>
<!--用户自行实现 选择的方式-->
<view v-if="type!=0&&type!=1&&type!=2"
style="display: flex;flex-direction: row;align-items: center;width: 100%;" @click="impl">
<view v-if="select == undefined || select == null || select === ''"
style="font-size: 28rpx;color: #BCBCBC;">{{hint}}</view>
<view v-if="select != undefined && select != null || select != ''"
style="font-size: 28rpx;color: #2E2D2D;">{{select}}</view>
<view style="flex: 1;"></view>
<image src="../../static/drop.png"
style="width: 45rpx;height: 45rpx;margin-left: 12rpx;flex-shrink: 0;">
</image>
</view>
</view>
<view style="width: 100%;height: 25rpx;"></view>
<view v-if="border == 2 || border ==3" class="border"></view>
</view>
</template>
<script>
export default {
name: "item-drop",
props: {
//
required: {
type: Boolean,
default: false
},
//
title: {
type: String,
default: ""
},
//
hint: {
type: String,
default: ""
},
//
content: {
type: String,
default: ""
},
// 线
// 0 线线
// 1 线
// 2 线
// 3 线
border: {
type: Number,
default: 2
},
// 使
enabled: {
type: Boolean,
default: true
},
//
// 0
// 1
// 2
// 3
type: {
type: Number,
default: 0
},
//
data: {
type: Array,
default: []
},
// key
dataDetailKey: {
type: String,
default: ""
}
},
mounted() {
this.select = this.content
},
data() {
return {
sex: [{
"detail": "男",
"value": "0001"
},
{
"detail": "女",
"value": "0002"
}
],
yes: [{
"detail": "是",
"value": "0001"
},
{
"detail": "否",
"value": "0002"
}
],
select: "",
selector: {}
};
},
methods: {
custom(e) {
let index = e.detail.value
//
this.selector = this.data[index]
//
this.select = this.selector[this.dataDetailKey]
console.log("选择+")
console.log(this.selector)
},
preset(e) {
let index = e.detail.value
//
this.selector = this.type == 1 ? this.sex[index] : this.yes[index]
//
console.log("选择+")
console.log(this.selector)
},
impl() {
if (this.enabled)
//
this.$emit("select")
}
}
}
</script>
<style lang="scss">
.border {
height: 1rpx;
background-color: #DFDFDF;
}
.enabled {
opacity: 0.5;
}
</style>

149
components/item-scan/item-scan.vue

@ -0,0 +1,149 @@
<template>
<view style="display: flex;flex-direction: column;box-sizing: border-box;">
<view v-if="border == 1 || border == 3" class="border"></view>
<view style="width: 100%;height: 30rpx;"></view>
<view style="display: flex;flex-direction: row;align-items: center;margin-left: 16rpx;margin-right: 15rpx;">
<text style="font-size: 26rpx;color: #101010;" :class="{ 'enabled': !enabled }">{{title}}</text>
<image v-if="required" src="../../static/asterisk.png"
style="width: 14rpx;height: 16rpx;margin-left: 12rpx;flex-shrink: 0;"></image>
</view>
<view style="width: 100%;height: 30rpx;"></view>
<!--选择类型ocr识别-->
<view v-if="data.length > 0"
style="display: flex;flex-direction: row;align-items: center;margin-left: 16rpx;margin-right: 15rpx;"
:class="{ 'enabled': !enabled }">
<input style="font-size: 28rpx;color: #2E2D2D;flex: 1;" placeholder-style="font-size: 28rpx;color: #BCBCBC;"
:value="input" @input="inputValue" :placeholder="hint" />
<picker :disabled="!enabled" :range-key="dataDetailKey" :range="data" @change="custom">
<image src="../../static/scan.png"
style="width: 45rpx;height: 45rpx;margin-left: 12rpx;flex-shrink: 0;">
</picker>
</image>
</view>
<!--默认身份证类型-->
<view v-if="data.length == 0"
style="display: flex;flex-direction: row;align-items: center;margin-left: 16rpx;margin-right: 15rpx;"
:class="{ 'enabled': !enabled }">
<input style="font-size: 28rpx;color: #2E2D2D;flex: 1;" placeholder-style="font-size: 28rpx;color: #BCBCBC;"
:value="input" @input="inputValue" :placeholder="hint" />
<image src="../../static/scan.png" style="width: 45rpx;height: 45rpx;margin-left: 12rpx;flex-shrink: 0;"
@click="scan">
</image>
</view>
<view style="width: 100%;height: 25rpx;"></view>
<view v-if="border == 2 || border ==3" class="border"></view>
</view>
</template>
<script>
//
import B64 from '../../commons/LocalFile2Base64.js';
export default {
name: "item-scan",
props: {
//
required: {
type: Boolean,
default: false
},
//
title: {
type: String,
default: ""
},
//
hint: {
type: String,
default: ""
},
//
content: {
type: String,
default: ""
},
// 线
// 0 线线
// 1 线
// 2 线
// 3 线
border: {
type: Number,
default: 2
},
// 使
enabled: {
type: Boolean,
default: true
},
//
data: {
type: Array,
default () {
return []
}
},
// key
dataDetailKey: {
type: String,
default: ""
}
},
created() {
this.input = this.content
},
data() {
return {
input: "",
select: ""
};
},
methods: {
scan() {
if (this.enabled) {
let _this = this;
uni.chooseImage({
count: 1,
sizeType: ['original'],
success: function(res) {
B64(res.tempFilePaths[0])
.then((res) => {
//
console.log(res)
}, (err) => {
//
_this.Toast(err)
})
}
})
}
},
inputValue(e) {
console.log(e.detail.value)
},
custom(e) {
let index = e.detail.value
// ocr
this.select = this.data[index][this.dataDetailKey]
this.scan()
}
}
}
</script>
<style lang="scss">
.border {
height: 1rpx;
background-color: #DFDFDF;
}
.enabled {
opacity: 0.5;
}
</style>

41
components/loading/loading.vue

@ -0,0 +1,41 @@
<template>
<view>
<view style="width: 100%;margin-top: 10rpx;display: flex;justify-content: center;">
<image src="../../static/custom-icon/jiazai.png" mode="aspectFill" class="loading spin"></image>
</view>
</view>
</template>
<script>
export default {
data() {
return {};
},
methods: {
}
}
</script>
<style>
.loading {
margin-top: 10rpx;
width: 20px;
height: 20px;
margin-bottom: 20rpx;
}
.spin {
animation: spin 1s steps(8) infinite;
}
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
</style>

56
components/mescroll-uni/components/mescroll-down.css

@ -0,0 +1,56 @@
/* 下拉刷新区域 */
.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);
}
}

54
components/mescroll-uni/components/mescroll-down.vue

@ -0,0 +1,54 @@
<!-- 下拉刷新区域 -->
<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, // inOffset1 outOffset2 showLoading3 endDownScroll4
rate: Number // (inOffset: rate<1; outOffset: rate>=1)
},
computed: {
// ,propdefault
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>

92
components/mescroll-uni/components/mescroll-empty.vue

@ -0,0 +1,92 @@
<!--空布局
可作为独立的组件, 不使用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 }">
<image v-if="icon" class="empty-icon" :src="icon" mode="widthFix" />
<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>
/* 无任何数据的空布局 */
.mescroll-empty {
box-sizing: border-box;
width: 100%;
padding: 100rpx 50rpx;
text-align: 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>

80
components/mescroll-uni/components/mescroll-top.vue

@ -0,0 +1,80 @@
<!-- 回到顶部的按钮 -->
<template>
<image class="mescroll-totop"
:class="[value ? 'mescroll-totop-in' : 'mescroll-totop-out', {'mescroll-safe-bottom': 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="/static/img//uniapp/mescrollTotop.png" mode="widthFix" @click="toTopClick" />
</template>
<script>
export default {
props: {
// up.toTop
option: Object,
//
value: false
},
computed: {
// ,propdefault
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 */
.mescroll-safe-bottom {
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>

47
components/mescroll-uni/components/mescroll-up.css

@ -0,0 +1,47 @@
/* 上拉加载区域 */
.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);
}
}

39
components/mescroll-uni/components/mescroll-up.vue

@ -0,0 +1,39 @@
<!-- 上拉加载区域 -->
<template>
<view class="mescroll-upwarp" :style="{'background-color':mescroll.optUp.bgColor,'color':mescroll.optUp.textColor}">
<!-- 加载中 (此处不能用v-if,否则android小程序快速上拉可能会不断触发上拉回调) -->
<view v-if="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 // 0loading1loading2
},
computed: {
// ,propdefault
mOption() {
return this.option || {};
},
//
isUpLoading() {
return this.type === 1;
},
//
isUpNoMore() {
return this.type === 2;
}
}
};
</script>
<style>
@import './mescroll-up.css';
</style>

15
components/mescroll-uni/mescroll-body.css

@ -0,0 +1,15 @@
page {
-webkit-overflow-scrolling: touch;
/* 使iOS滚动流畅 */
}
.mescroll-body {
position: relative;
/* 下拉刷新区域相对自身定位 */
height: auto;
/* 不可固定高度,否则overflow: hidden, 可通过设置最小高度使列表不满屏仍可下拉*/
overflow: hidden;
/* 遮住顶部下拉刷新区域 */
box-sizing: border-box;
/* 避免设置padding出现双滚动条的问题 */
}

309
components/mescroll-uni/mescroll-body.vue

@ -0,0 +1,309 @@
<template>
<view class="mescroll-body"
:style="{'minHeight':minHeight, 'padding-top': padTop, 'padding-bottom': padBottom, 'padding-bottom': padBottomConstant, 'padding-bottom': padBottomEnv }"
@touchstart="touchstartEvent" @touchmove="touchmoveEvent" @touchend="touchendEvent"
@touchcancel="touchendEvent">
<view class="mescroll-body-content" :style="{ transform: translateY, transition: transition }">
<!-- 下拉加载区域 (支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-down组件实现)-->
<!-- <mescroll-down :option="mescroll.optDown" :type="downLoadType" :rate="downRate"></mescroll-down> -->
<view v-if="mescroll.optDown.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>
<!-- 列表内容 -->
<slot></slot>
<!-- 空布局 -->
<mescroll-empty v-if="isShowEmpty" :option="mescroll.optUp.empty" @emptyclick="emptyClick"></mescroll-empty>
<!-- 上拉加载区域 (下拉刷新时不显示, 支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-up组件实现)-->
<!-- <mescroll-up v-if="mescroll.optUp.use && !isDownLoading" :option="mescroll.optUp" :type="upLoadType"></mescroll-up> -->
<view v-if="mescroll.optUp.use && !isDownLoading" class="mescroll-upwarp"
:style="{'background-color':mescroll.optUp.bgColor,'color':mescroll.optUp.textColor}">
<!-- 加载中 (此处不能用v-if,否则android小程序快速上拉可能会不断触发上拉回调) -->
<view v-if="upLoadType===1">
<view class="upwarp-progress mescroll-rotate" :style="{'border-color':mescroll.optUp.textColor}">
</view>
<view class="upwarp-tip">{{ mescroll.optUp.textLoading }}</view>
</view>
<!-- 无数据 -->
<view v-if="upLoadType===2" class="upwarp-nodata">{{ mescroll.optUp.textNoMore }}</view>
</view>
</view>
<!-- 回到顶部按钮 (fixed元素需写在transform外面,防止降级为absolute)-->
<mescroll-top v-model="isShowToTop" :option="mescroll.optUp.toTop" @click="toTopClick"></mescroll-top>
</view>
</template>
<script>
// mescroll-uni.js,
import MeScroll from './mescroll-uni.js';
//
import GlobalOption from './mescroll-uni-option.js';
//
import MescrollEmpty from './components/mescroll-empty.vue';
//
import MescrollTop from './components/mescroll-top.vue';
export default {
components: {
MescrollEmpty,
MescrollTop
},
data() {
return {
mescroll: {
optDown: {},
optUp: {}
}, // mescroll
downHight: 0, //:
downRate: 0, // (inOffset: rate<1; outOffset: rate>=1)
downLoadType: 4, // inOffset1 outOffset2 showLoading3 endDownScroll4
upLoadType: 0, // 0loading1loading2
isShowEmpty: false, //
isShowToTop: false, //
windowHeight: 0, // 使
statusBarHeight: 0, //
isSafearea: false //
};
},
props: {
down: Object, //
up: Object, //
top: [String, Number], // (20, "20rpx", "20px", "20%", rpx, windowHeight)
topbar: Boolean, // top, false (使:,)
bottom: [String, Number], // (20, "20rpx", "20px", "20%", rpx, windowHeight)
safearea: Boolean, // bottom, false (iPhoneX使)
height: [String, Number] // mescroll,windowHeight,使
},
computed: {
// mescroll,windowHeight,使
minHeight() {
return this.toPx(this.height || '100%') + 'px'
},
// (px)
numTop() {
return this.toPx(this.top) + (this.topbar ? this.statusBarHeight : 0);
},
padTop() {
return this.numTop + 'px';
},
// (px)
numBottom() {
return this.toPx(this.bottom);
},
padBottom() {
return this.numBottom + 'px';
},
padBottomConstant() {
return this.isSafearea ? 'calc(' + this.padBottom + ' + constant(safe-area-inset-bottom))' : this
.padBottom;
},
padBottomEnv() {
return this.isSafearea ? 'calc(' + this.padBottom + ' + env(safe-area-inset-bottom))' : this.padBottom;
},
//
isDownReset() {
return this.downLoadType === 3 || this.downLoadType === 4;
},
//
transition() {
return this.isDownReset ? 'transform 300ms' : this.downTransition;
},
translateY() {
return this.downHight > 0 ? 'translateY(' + this.downHight + 'px)' :
''; // transform使fixed,fixedmescroll
},
//
isDownLoading() {
return this.downLoadType === 3
},
//
downRotate() {
return 'rotate(' + 360 * this.downRate + 'deg)'
},
//
downText() {
switch (this.downLoadType) {
case 1:
return this.mescroll.optDown.textInOffset;
case 2:
return this.mescroll.optDown.textOutOffset;
case 3:
return this.mescroll.optDown.textLoading;
case 4:
return this.mescroll.optDown.textLoading;
default:
return this.mescroll.optDown.textInOffset;
}
}
},
methods: {
//number,rpx,upx,px,% --> px
toPx(num) {
if (typeof num === 'string') {
if (num.indexOf('px') !== -1) {
if (num.indexOf('rpx') !== -1) {
// "10rpx"
num = num.replace('rpx', '');
} else if (num.indexOf('upx') !== -1) {
// "10upx"
num = num.replace('upx', '');
} else {
// "10px"
return Number(num.replace('px', ''));
}
} else if (num.indexOf('%') !== -1) {
// ,windowHeight,"10%"windowHeight10%
let rate = Number(num.replace('%', '')) / 100;
return this.windowHeight * rate;
}
}
return num ? uni.upx2px(Number(num)) : 0;
},
//touchstart,
touchstartEvent(e) {
this.mescroll.touchstartEvent(e);
},
//touchmove,
touchmoveEvent(e) {
this.mescroll.touchmoveEvent(e);
},
//touchend,
touchendEvent(e) {
this.mescroll.touchendEvent(e);
},
//
emptyClick() {
this.$emit('emptyclick', this.mescroll);
},
//
toTopClick() {
this.mescroll.scrollTo(0, this.mescroll.optUp.toTop.duration); //
this.$emit('topclick', this.mescroll); //
}
},
// 使createdmescroll; mountedcssH5
created() {
let vm = this;
let diyOption = {
//
down: {
inOffset(mescroll) {
vm.downLoadType = 1; // offset (mescroll,)
},
outOffset(mescroll) {
vm.downLoadType = 2; // offset (mescroll,)
},
onMoving(mescroll, rate, downHight) {
// ,;
vm.downHight = downHight; // (mescroll,)
vm.downRate = rate; // (inOffset: rate<1; outOffset: rate>=1)
},
showLoading(mescroll, downHight) {
vm.downLoadType = 3; // (mescroll,)
vm.downHight = downHight; // (mescroll,)
},
endDownScroll(mescroll) {
vm.downLoadType = 4; // (mescroll,)
vm.downHight = 0; // (mescroll,)
},
//
callback: function(mescroll) {
vm.$emit('down', mescroll);
}
},
//
up: {
//
showLoading() {
vm.upLoadType = 1;
},
//
showNoMore() {
vm.upLoadType = 2;
},
//
hideUpScroll() {
vm.upLoadType = 0;
},
//
empty: {
onShow(isShow) {
//
vm.isShowEmpty = isShow;
}
},
//
toTop: {
onShow(isShow) {
//
vm.isShowToTop = isShow;
}
},
//
callback: function(mescroll) {
vm.$emit('up', mescroll);
}
}
};
MeScroll.extend(diyOption, GlobalOption); //
let myOption = JSON.parse(
JSON.stringify({
down: vm.down,
up: vm.up
})
); // ,props
MeScroll.extend(myOption, diyOption); //
// MeScroll
vm.mescroll = new MeScroll(myOption, true); // true,body
// initmescroll
vm.$emit('init', vm.mescroll);
//
const sys = uni.getSystemInfoSync();
if (sys.windowHeight) vm.windowHeight = sys.windowHeight;
if (sys.statusBarHeight) vm.statusBarHeight = sys.statusBarHeight;
// 使downbottomOffset
vm.mescroll.setBodyHeight(sys.windowHeight);
// mescroll-bodyAndroid,mescroll-uni"disableScroll":true,
// #ifdef MP
if (sys.platform == "android") vm.downTransition = 'transform 200ms'
// #endif
// 使pagescroll,scrollTo
vm.mescroll.resetScrollTo((y, t) => {
uni.pageScrollTo({
scrollTop: y,
duration: t
})
});
// up.toTop.safearea,vuesafearea
if (sys.platform == "ios") {
vm.isSafearea = vm.safearea;
if (vm.up && vm.up.toTop && vm.up.toTop.safearea != null) {} else {
vm.mescroll.optUp.toTop.safearea = vm.safearea;
}
} else {
vm.isSafearea = false
vm.mescroll.optUp.toTop.safearea = false
}
}
};
</script>
<style>
@import "./mescroll-body.css";
@import "./components/mescroll-down.css";
@import './components/mescroll-up.css';
</style>

60
components/mescroll-uni/mescroll-mixins.js

@ -0,0 +1,60 @@
// 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
}
},
// 下拉刷新的回调
downCallback() {
// mixin默认resetUpScroll
this.mescroll.resetUpScroll()
},
// 上拉加载的回调
upCallback() {
// mixin默认延时500自动结束加载
setTimeout(() => {
this.mescroll.endErr();
}, 500)
}
},
mounted() {
this.mescrollInitByRef(); // 兼容字节跳动小程序, 避免未设置@init或@init此时未能取到ref的情况
}
}
export default MescrollMixin;

34
components/mescroll-uni/mescroll-uni-option.js

@ -0,0 +1,34 @@
// 全局配置
// 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: '-- END --', // 没有更多数据的提示文本
offset: 80, // 距底部多远时,触发upCallback
isBounce: false, // 默认禁止橡皮筋的回弹效果, 必读事项: http://www.mescroll.com/qa.html?v=190725#q25
toTop: {
// 回到顶部按钮,需配置src才显示
src: "/static/img/uniapp/mescrollTotop.png", // 图片路径 (建议放入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/img/uniapp/zanwushuju.png", // 图标路径 (建议放入static目录, 如 /static/img/mescroll-empty.png )
tip: '' // 提示
}
}
}
export default GlobalOption

33
components/mescroll-uni/mescroll-uni.css

@ -0,0 +1,33 @@
page {
height: 100%;
box-sizing: border-box;
/* 避免设置padding出现双滚动条的问题 */
}
.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生效 */
}

869
components/mescroll-uni/mescroll-uni.js

@ -0,0 +1,869 @@
/* mescroll
* version 1.2.5
* 2020-03-15 wenju
* http://www.mescroll.com
*/
export default function MeScroll(options, isScrollBody) {
let me = this;
me.version = '1.2.5'; // 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.auto && hasDownCallback) {
if (me.optDown.autoShowLoading) {
me.triggerDownScroll(); // 显示下拉进度,执行下拉回调
} else {
me.optDown.callback && me.optDown.callback(me); // 不显示下拉进度,直接执行下拉回调
}
}
// 自动触发上拉加载
setTimeout(function() { // 延时确保先执行down的callback,再执行up的callback,因为部分小程序emit是异步,会导致isUpAutoLoad判断有误
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, 此值用于控制最大的误差
fps: 80, // 下拉节流 (值越大每秒刷新频率越高)
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, // 准备结束下拉的回调. 返回结束下拉的延时执行时间,默认0ms; 常用于结束下拉之前再显示另外一小段动画,才去隐藏下拉刷新的场景, 参考案例【dotJump】
endDownScroll: null, // 结束下拉刷新的回调
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,两者可同时触发;
isBounce: false, // 默认禁止橡皮筋的回弹效果, 必读事项: http://www.mescroll.com/qa.html?v=190725#q25
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: '-- END --', // 没有更多数据的提示文本
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.lastPoint = this.startPoint; // 重置上次move的点
this.maxTouchmoveY = this.getBodyHeight() - this.optDown.bottomOffset; // 手指触摸的最大范围(写在touchstart避免body获取高度为0的情况)
this.inTouchend = false; // 标记不是touchend
}
/* 列表touchmove事件 */
MeScroll.prototype.touchmoveEvent = function(e) {
// #ifdef H5
window.isPreventDefault = false // 标记不需要阻止window事件
// #endif
if (!this.optDown.use) return;
if (!this.startPoint) return;
let me = this;
// 节流
let t = new Date().getTime();
if (me.moveTime && t - me.moveTime < me.moveTimeDiff) { // 小于节流时间,则不处理
return;
} else {
me.moveTime = t
if (!me.moveTimeDiff) me.moveTimeDiff = 1000 / me.optDown.fps
}
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; 在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))) {
// 下拉的角度是否在配置的范围内
let angle = me.getAngle(me.lastPoint, curPoint); // 两点之间的角度,区间 [0,90]
if (angle < me.optDown.minAngle) return; // 如果小于配置的角度,则不往下执行下拉刷新
// 如果手指的位置超过配置的距离,则提前结束下拉,避免Webview嵌套导致touchend无法触发
if (me.maxTouchmoveY > 0 && curPoint.y >= me.maxTouchmoveY) {
me.inTouchend = true; // 标记执行touchend
me.touchendEvent(); // 提前触发touchend
return;
}
// #ifdef H5
window.isPreventDefault = true // 标记阻止window事件
// #endif
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 += Math.round(diff * me.optDown.outOffsetRate); // 越往下,高度变化越小
} else { // 向上收
me.downHight += diff; // 向上收回高度,则向上滑多少收多少高度
}
}
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.optDown.endDownScroll && this.optDown.endDownScroll(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.callback && this.optDown.callback(this); // 执行回调,联网加载数据
}
}
/* 显示下拉进度布局 */
MeScroll.prototype.showDownScroll = function() {
this.isDownScrolling = true; // 标记下拉中
if (this.optDown.native) {
uni.startPullDownRefresh(); // 系统自带的下拉刷新
this.optDown.showLoading && this.optDown.showLoading(this, 0); // 仍触发showLoading,因为上拉加载用到
} else {
this.downHight = this.optDown.offset; // 更新下拉区域高度
this.optDown.showLoading && this.optDown.showLoading(this, this.downHight); // 下拉刷新中...
}
}
/* 显示系统自带的下拉刷新时需要处理的业务 */
MeScroll.prototype.onPullDownRefresh = function() {
this.isDownScrolling = true; // 标记下拉中
this.optDown.showLoading && this.optDown.showLoading(this, 0); // 仍触发showLoading,因为上拉加载用到
this.optDown.callback && this.optDown.callback(this); // 执行回调,联网加载数据
}
/* 结束下拉刷新 */
MeScroll.prototype.endDownScroll = function() {
if (this.optDown.native) { // 结束原生下拉刷新
this.isDownScrolling = false;
this.optDown.endDownScroll && this.optDown.endDownScroll(this);
uni.stopPullDownRefresh();
return
}
let me = this;
// 结束下拉刷新的方法
let endScroll = function() {
me.downHight = 0;
me.isDownScrolling = false;
me.optDown.endDownScroll && me.optDown.endDownScroll(me);
!me.isScrollBody && me.setScrollHeight(0) // scroll-view重置滚动区域,使数据不满屏时仍可检查触发翻页
}
// 结束下拉刷新时的回调
let delay = 0;
if (me.optDown.afterLoading) delay = me.optDown.afterLoading(me); // 结束下拉刷新的延时,单位ms
if (typeof delay === 'number' && delay > 0) {
setTimeout(endScroll, delay);
} else {
endScroll();
}
}
/* 锁定下拉刷新: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.isBounce) me.setBounce(false); // 不允许bounce时,需禁止window的touchmove事件
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
// app的bounce只能通过配置pages.json的style.app-plus.bounce为"none"来禁止
// cancelable:是否可以被禁用; defaultPrevented:是否已经被禁用
if (e && e.cancelable && !e.defaultPrevented) e.preventDefault()
}
/* 是否允许下拉回弹(橡皮筋效果); true或null为允许; false禁止bounce */
MeScroll.prototype.setBounce = function(isBounce) {
// #ifdef H5
if (isBounce === false) {
this.optUp.isBounce = false; // 禁止
// 标记当前页使用了mescroll (需延时,确保page已切换)
setTimeout(function() {
let uniPageDom = document.getElementsByTagName('uni-page')[0];
uniPageDom && uniPageDom.setAttribute('use_mescroll', true)
}, 30);
// 避免重复添加事件
if (window.isSetBounce) return;
window.isSetBounce = true;
// 需禁止window的touchmove事件才能有效的阻止bounce
window.bounceTouchmove = function(e) {
if (!window.isPreventDefault) return; // 根据标记判断是否阻止
let el = e.target;
// 当前touch的元素及父元素是否要拦截touchmove事件
let isPrevent = true;
while (el !== document.body && el !== document) {
if (el.tagName === 'UNI-PAGE') { // 只扫描当前页
if (!el.getAttribute('use_mescroll')) {
isPrevent = false; // 如果当前页没有使用mescroll,则不阻止
}
break;
}
let cls = el.classList;
if (cls) {
if (cls.contains('mescroll-touch')) { // 采用scroll-view 此处不能过滤mescroll-uni,否则下拉仍然有回弹
isPrevent = false; // mescroll-touch无需拦截touchmove事件
break;
} else if (cls.contains('mescroll-touch-x') || cls.contains('mescroll-touch-y')) {
// 如果配置了水平或者垂直滑动
let curX = e.touches ? e.touches[0].pageX : e.clientX; // 当前第一个手指距离列表顶部的距离x
let curY = e.touches ? e.touches[0].pageY : e.clientY; // 当前第一个手指距离列表顶部的距离y
if (!this.preWinX) this.preWinX = curX; // 设置上次移动的距离x
if (!this.preWinY) this.preWinY = curY; // 设置上次移动的距离y
// 计算两点之间的角度
let x = Math.abs(this.preWinX - curX);
let y = Math.abs(this.preWinY - curY);
let z = Math.sqrt(x * x + y * y);
this.preWinX = curX; // 记录本次curX的值
this.preWinY = curY; // 记录本次curY的值
if (z !== 0) {
let angle = Math.asin(y / z) / Math.PI * 180; // 角度区间 [0,90]
if ((angle <= 45 && cls.contains('mescroll-touch-x')) || (angle > 45 && cls
.contains('mescroll-touch-y'))) {
isPrevent = false; // 水平滑动或者垂直滑动,不拦截touchmove事件
break;
}
}
}
}
el = el.parentNode; // 继续检查其父元素
}
// 拦截touchmove事件:是否可以被禁用&&是否已经被禁用 (这里不使用me.preventDefault(e)的方法,因为某些情况下会报找不到方法的异常)
if (isPrevent && e.cancelable && !e.defaultPrevented && typeof e.preventDefault === "function") e
.preventDefault();
}
window.addEventListener('touchmove', window.bounceTouchmove, {
passive: false
});
} else {
this.optUp.isBounce = true; // 允许
if (window.bounceTouchmove) {
window.removeEventListener('touchmove', window.bounceTouchmove);
window.bounceTouchmove = null;
window.isSetBounce = false;
}
}
// #endif
}

386
components/mescroll-uni/mescroll-uni.vue

@ -0,0 +1,386 @@
<template>
<view class="mescroll-uni-warp">
<scroll-view :id="viewId" class="mescroll-uni" :class="{'mescroll-uni-fixed':isFixed}"
:style="{'height':scrollHeight,'padding-top':padTop,'padding-bottom':padBottom,'padding-bottom':padBottomConstant,'padding-bottom':padBottomEnv,'top':fixedTop,'bottom':fixedBottom,'bottom':fixedBottomConstant,'bottom':fixedBottomEnv}"
:scroll-top="scrollTop" :scroll-into-view="scrollToViewId" :scroll-with-animation="scrollAnim"
@scroll="scroll" @touchstart="touchstartEvent" @touchmove="touchmoveEvent" @touchend="touchendEvent"
@touchcancel="touchendEvent" :scroll-y='isDownReset' :enable-back-to-top="true">
<view class="mescroll-uni-content" :style="{'transform': translateY, 'transition': transition}">
<!-- 下拉加载区域 (支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-down组件实现)-->
<!-- <mescroll-down :option="mescroll.optDown" :type="downLoadType" :rate="downRate"></mescroll-down> -->
<view v-if="mescroll.optDown.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>
<!-- 列表内容 -->
<slot></slot>
<!-- 空布局 -->
<mescroll-empty v-if="isShowEmpty" :option="mescroll.optUp.empty" @emptyclick="emptyClick">
</mescroll-empty>
<!-- 上拉加载区域 (下拉刷新时不显示, 支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-up组件实现)-->
<!-- <mescroll-up v-if="mescroll.optUp.use && !isDownLoading" :option="mescroll.optUp" :type="upLoadType"></mescroll-up> -->
<view v-if="mescroll.optUp.use && !isDownLoading" class="mescroll-upwarp"
:style="{'background-color':mescroll.optUp.bgColor,'color':mescroll.optUp.textColor}">
<!-- 加载中 (此处不能用v-if,否则android小程序快速上拉可能会不断触发上拉回调) -->
<view v-if="upLoadType===1">
<view class="upwarp-progress mescroll-rotate"
:style="{'border-color':mescroll.optUp.textColor}"></view>
<view class="upwarp-tip">{{ mescroll.optUp.textLoading }}</view>
</view>
<!-- 无数据 -->
<view v-if="upLoadType===2" class="upwarp-nodata">{{ mescroll.optUp.textNoMore }}</view>
</view>
</view>
</scroll-view>
<!-- 回到顶部按钮 (fixed元素,需写在scroll-view外面,防止滚动的时候抖动)-->
<mescroll-top v-model="isShowToTop" :option="mescroll.optUp.toTop" @click="toTopClick"></mescroll-top>
</view>
</template>
<script>
// mescroll-uni.js,
import MeScroll from './mescroll-uni.js';
//
import GlobalOption from './mescroll-uni-option.js';
//
import MescrollEmpty from './components/mescroll-empty.vue';
//
import MescrollTop from './components/mescroll-top.vue';
export default {
components: {
MescrollEmpty,
MescrollTop
},
data() {
return {
mescroll: {
optDown: {},
optUp: {}
}, // mescroll
viewId: 'id_' + Math.random().toString(36).substr(2), // mescrollid(,)
downHight: 0, //:
downRate: 0, // (inOffset: rate<1; outOffset: rate>=1)
downLoadType: 4, // inOffset1 outOffset2 showLoading3 endDownScroll4
upLoadType: 0, // 0loading1loading2
isShowEmpty: false, //
isShowToTop: false, //
scrollTop: 0, //
scrollAnim: false, //
windowTop: 0, // 使
windowBottom: 0, // 使
windowHeight: 0, // 使
statusBarHeight: 0, //
isSafearea: false, //
scrollToViewId: '' // viewid
}
},
props: {
down: Object, //
up: Object, //
top: [String, Number], // (20, "20rpx", "20px", "20%", rpx, windowHeight)
topbar: Boolean, // top, false (使:,)
bottom: [String, Number], // (20, "20rpx", "20px", "20%", rpx, windowHeight)
safearea: Boolean, // bottom, false (iPhoneX使)
fixed: { // fixedmescroll, true
type: Boolean,
default () {
return true
}
},
height: [String,
Number
] // mescroll, ,使fixed. (20, "20rpx", "20px", "20%", rpx, windowHeight)
},
computed: {
// 使fixed (height,使)
isFixed() {
return !this.height && this.fixed
},
// mescroll
scrollHeight() {
if (this.isFixed) {
return "auto"
} else if (this.height) {
return this.toPx(this.height) + 'px'
} else {
return "100%"
}
},
// (px)
numTop() {
return this.toPx(this.top) + (this.topbar ? this.statusBarHeight : 0)
},
fixedTop() {
return this.isFixed ? (this.numTop + this.windowTop) + 'px' : 0
},
padTop() {
return !this.isFixed ? this.numTop + 'px' : 0
},
// (px)
numBottom() {
return this.toPx(this.bottom)
},
fixedBottom() {
return this.isFixed ? (this.numBottom + this.windowBottom) + 'px' : 0
},
fixedBottomConstant() {
return this.isSafearea ? "calc(" + this.fixedBottom + " + constant(safe-area-inset-bottom))" : this
.fixedBottom
},
fixedBottomEnv() {
return this.isSafearea ? "calc(" + this.fixedBottom + " + env(safe-area-inset-bottom))" : this.fixedBottom
},
padBottom() {
return !this.isFixed ? this.numBottom + 'px' : 0
},
padBottomConstant() {
return this.isSafearea ? "calc(" + this.padBottom + " + constant(safe-area-inset-bottom))" : this.padBottom
},
padBottomEnv() {
return this.isSafearea ? "calc(" + this.padBottom + " + env(safe-area-inset-bottom))" : this.padBottom
},
//
isDownReset() {
return this.downLoadType === 3 || this.downLoadType === 4
},
//
transition() {
return this.isDownReset ? 'transform 300ms' : '';
},
translateY() {
return this.downHight > 0 ? 'translateY(' + this.downHight + 'px)' :
''; // transform使fixed,fixedmescroll
},
//
isDownLoading() {
return this.downLoadType === 3
},
//
downRotate() {
return 'rotate(' + 360 * this.downRate + 'deg)'
},
//
downText() {
switch (this.downLoadType) {
case 1:
return this.mescroll.optDown.textInOffset;
case 2:
return this.mescroll.optDown.textOutOffset;
case 3:
return this.mescroll.optDown.textLoading;
case 4:
return this.mescroll.optDown.textLoading;
default:
return this.mescroll.optDown.textInOffset;
}
}
},
methods: {
//number,rpx,upx,px,% --> px
toPx(num) {
if (typeof num === "string") {
if (num.indexOf('px') !== -1) {
if (num.indexOf('rpx') !== -1) { // "10rpx"
num = num.replace('rpx', '');
} else if (num.indexOf('upx') !== -1) { // "10upx"
num = num.replace('upx', '');
} else { // "10px"
return Number(num.replace('px', ''))
}
} else if (num.indexOf('%') !== -1) {
// ,windowHeight,"10%"windowHeight10%
let rate = Number(num.replace("%", "")) / 100
return this.windowHeight * rate
}
}
return num ? uni.upx2px(Number(num)) : 0
},
//,
scroll(e) {
this.mescroll.scroll(e.detail, () => {
this.$emit('scroll', this
.mescroll) // this.mescroll.scrollTop; this.mescroll.isScrollUp
})
},
//touchstart,
touchstartEvent(e) {
this.mescroll.touchstartEvent(e);
},
//touchmove,
touchmoveEvent(e) {
this.mescroll.touchmoveEvent(e);
},
//touchend,
touchendEvent(e) {
this.mescroll.touchendEvent(e);
},
//
emptyClick() {
this.$emit('emptyclick', this.mescroll)
},
//
toTopClick() {
this.mescroll.scrollTo(0, this.mescroll.optUp.toTop.duration); //
this.$emit('topclick', this.mescroll); //
},
// (使,)
setClientHeight() {
if (this.mescroll.getClientHeight(true) === 0 && !this.isExec) {
this.isExec = true; //
this.$nextTick(() => { // dom
let view = uni.createSelectorQuery().in(this).select('#' + this.viewId);
view.boundingClientRect(data => {
this.isExec = false;
if (data) {
this.mescroll.setClientHeight(data.height);
} else if (this.clientNum != 3) { // ,dom,,3
this.clientNum = this.clientNum == null ? 1 : this.clientNum + 1;
setTimeout(() => {
this.setClientHeight()
}, this.clientNum * 100)
}
}).exec();
})
}
}
},
// 使createdmescroll; mountedcssH5
created() {
let vm = this;
let diyOption = {
//
down: {
inOffset(mescroll) {
vm.downLoadType = 1; // offset (mescroll,)
},
outOffset(mescroll) {
vm.downLoadType = 2; // offset (mescroll,)
},
onMoving(mescroll, rate, downHight) {
// ,;
vm.downHight = downHight; // (mescroll,)
vm.downRate = rate; // (inOffset: rate<1; outOffset: rate>=1)
},
showLoading(mescroll, downHight) {
vm.downLoadType = 3; // (mescroll,)
vm.downHight = downHight; // (mescroll,)
},
endDownScroll(mescroll) {
vm.downLoadType = 4; // (mescroll,)
vm.downHight = 0; // (mescroll,)
},
//
callback: function(mescroll) {
vm.$emit('down', mescroll)
}
},
//
up: {
//
showLoading() {
vm.upLoadType = 1;
},
//
showNoMore() {
vm.upLoadType = 2;
},
//
hideUpScroll() {
vm.upLoadType = 0;
},
//
empty: {
onShow(isShow) { //
vm.isShowEmpty = isShow;
}
},
//
toTop: {
onShow(isShow) { //
vm.isShowToTop = isShow;
}
},
//
callback: function(mescroll) {
vm.$emit('up', mescroll);
// (mescroll)
vm.setClientHeight()
}
}
}
MeScroll.extend(diyOption, GlobalOption); //
let myOption = JSON.parse(JSON.stringify({
'down': vm.down,
'up': vm.up
})) // ,props
MeScroll.extend(myOption, diyOption); //
// MeScroll
vm.mescroll = new MeScroll(myOption);
vm.mescroll.viewId = vm.viewId; // id
// initmescroll
vm.$emit('init', vm.mescroll);
//
const sys = uni.getSystemInfoSync();
if (sys.windowTop) vm.windowTop = sys.windowTop;
if (sys.windowBottom) vm.windowBottom = sys.windowBottom;
if (sys.windowHeight) vm.windowHeight = sys.windowHeight;
if (sys.statusBarHeight) vm.statusBarHeight = sys.statusBarHeight;
// 使downbottomOffset
vm.mescroll.setBodyHeight(sys.windowHeight);
// 使scrollview,scrollTo
vm.mescroll.resetScrollTo((y, t) => {
vm.scrollAnim = (t !== 0); // t0,使
if (typeof y === 'string') { // ,使scroll-into-view
vm.scrollToViewId = y;
return;
}
let curY = vm.mescroll.getScrollTop()
if (t === 0 || t === 300) { // t使300,使
vm.scrollTop = curY;
vm.$nextTick(function() {
vm.scrollTop = y
})
} else {
vm.mescroll.getStep(curY, y, step => { // t
vm.scrollTop = step
}, t)
}
})
// up.toTop.safearea,vuesafearea
if (sys.platform == "ios") {
vm.isSafearea = vm.safearea;
if (vm.up && vm.up.toTop && vm.up.toTop.safearea != null) {} else {
vm.mescroll.optUp.toTop.safearea = vm.safearea;
}
} else {
vm.isSafearea = false
vm.mescroll.optUp.toTop.safearea = false
}
},
mounted() {
//
this.setClientHeight()
}
}
</script>
<style>
@import "./mescroll-uni.css";
@import "./components/mescroll-down.css";
@import './components/mescroll-up.css';
</style>

23
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;

48
components/mescroll-uni/mixins/mescroll-more-item.js

@ -0,0 +1,48 @@
/**
* mescroll-more-item的mixins, 仅在多个 mescroll-body 写在子组件时使用 (参考 mescroll-more 案例)
*/
const MescrollMoreItemMixin = {
props: {
i: Number, // 每个tab页的专属下标
index: { // 当前tab的下标
type: Number,
default () {
return 0
}
}
},
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;

56
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;

20
components/noData/noData.vue

@ -0,0 +1,20 @@
<template>
<view style="display: flex;justify-content: center;width: 100%;">
<image style="width: 280rpx;height: 280rpx; margin-top: 130rpx;margin-bottom: 110rpx;"
src="../../static/custom-icon/zanwushuju.png" mode="aspectFill"></image>
</view>
</template>
<script>
export default {
data() {
return {
};
}
}
</script>
<style>
</style>

135
components/pick-regions/pick-regions.vue

@ -0,0 +1,135 @@
<template>
<picker mode="multiSelector" :value="multiIndex" :range="multiArray" @change="handleValueChange"
@columnchange="handleColumnChange">
<slot></slot>
</picker>
</template>
<script>
const CHINA_REGIONS = require('./regions.json')
export default {
props: {
defaultRegions: {
type: Array,
default () {
return []
}
},
defaultRegionCode: {
type: String
},
defaultRegion: [String, Array]
},
data() {
return {
cityArr: CHINA_REGIONS[0].childs,
districtArr: CHINA_REGIONS[0].childs[0].childs,
multiIndex: [0, 0, 0],
isInitMultiArray: true,
}
},
watch: {
defaultRegion: {
handler(region, oldRegion) {
if (Array.isArray(region)) {
//
oldRegion = oldRegion || []
if (region.join('') !== oldRegion.join('')) {
this.handleDefaultRegion(region)
}
} else if (region && region.length == 6) {
this.handleDefaultRegion(region)
} else {
console.warn('defaultRegion非有效格式')
}
},
immediate: true,
}
},
computed: {
multiArray() {
return this.pickedArr.map(arr => arr.map(item => item.name))
},
pickedArr() {
//
if (this.isInitMultiArray) {
return [
CHINA_REGIONS,
CHINA_REGIONS[0].childs,
CHINA_REGIONS[0].childs[0].childs
]
}
return [CHINA_REGIONS, this.cityArr, this.districtArr];
}
},
methods: {
handleColumnChange(e) {
// console.log(e);
this.isInitMultiArray = false;
const that = this;
let col = e.detail.column;
let row = e.detail.value;
that.multiIndex[col] = row;
try {
switch (col) {
case 0:
if (CHINA_REGIONS[that.multiIndex[0]].childs.length == 0) {
that.cityArr = that.districtArr = [CHINA_REGIONS[that.multiIndex[0]]]
break;
}
that.cityArr = CHINA_REGIONS[that.multiIndex[0]].childs
that.districtArr = CHINA_REGIONS[that.multiIndex[0]].childs[that.multiIndex[1]].childs
break;
case 1:
that.districtArr = CHINA_REGIONS[that.multiIndex[0]].childs[that.multiIndex[1]].childs
break;
case 2:
break;
}
} catch (e) {
// console.log(e);
that.districtArr = CHINA_REGIONS[that.multiIndex[0]].childs[0].childs
}
},
handleValueChange(e) {
//
let [index0, index1, index2] = e.detail.value;
let [arr0, arr1, arr2] = this.pickedArr;
let address = [arr0[index0], arr1[index1], arr2[index2]];
// console.log(address);
this.$emit('getRegion', address)
},
handleDefaultRegion(region) {
const isCode = !Array.isArray(region)
this.isInitMultiArray = false;
let children = CHINA_REGIONS
for (let i = 0; i < 3; i++) {
for (let j = 0; j < children.length; j++) {
let condition = isCode ? children[j].code == region.slice(0, (i + 1) * 2) : children[j].name
.includes(region[i]);
if (condition) {
//
// console.log(i,j,children.length-1);
children = children[j].childs;
if (i == 0) {
this.cityArr = children
} else if (i == 1) {
this.districtArr = children
}
this.$set(this.multiIndex, i, j)
// console.log(this.multiIndex);
break;
} else {
//
// console.log(i,j,children.length-1);
if (i == 0 && j == (children.length - 1)) {
this.isInitMultiArray = true;
}
}
}
}
}
},
}
</script>

10330
components/pick-regions/regions.json

File diff suppressed because it is too large

459
components/sl-filter/filter-view.vue

@ -0,0 +1,459 @@
<template>
<view>
<view style="padding: 0px 0px;">
<view class="filter-content" v-for="(item, index) in menuList" :key="index" v-if="menuIndex == index">
<view class="filter-content-list">
<view v-for="(detailItem,idx) in selectDetailList" :key="idx"
:class="detailItem.isSelected?'filter-content-list-item-active':'filter-content-list-item-default'"
:style="{'color': detailItem.isSelected?themeColor:'#666666'}"
@tap="sortTap(idx,selectDetailList,item.key)">
<text>{{detailItem.title}}</text>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
selectArr: [],
result: {},
menuIndex: 0,
selectDetailList: [],
independenceObj: {},
selectedKey: '',
cacheSelectedObj: {},
defaultSelectedTitleObj: {}
};
},
props: {
themeColor: {
type: String,
default () {
return '#D1372C'
}
},
menuList: {
type: Array,
default () {
return []
}
},
independence: {
type: Boolean,
default: true
}
},
computed: {
selectedTitleObj() {
let obj = {}
for (let i = 0; i < this.menuList.length; i++) {
let item = this.menuList[i];
obj[item.key] = item.title;
}
return obj;
},
defaultSelectedObj() { //
return this.getSelectedObj()
},
selectedObj: {
get() {
return this.getSelectedObj()
},
set(newObj) {
return newObj;
}
}
},
methods: {
getSelectedObj() {
let obj = {}
for (let i = 0; i < this.menuList.length; i++) {
let item = this.menuList[i];
if (!this.independence && item.defaultSelectedIndex != null && item.defaultSelectedIndex.toString()
.length > 0) { //
if (item.isMutiple) {
obj[item.key] = [];
item.detailList[0].isSelected = false;
if (!Array.isArray(item.defaultSelectedIndex)) { //
item.defaultSelectedIndex = [item.defaultSelectedIndex];
}
for (let j = 0; j < item.defaultSelectedIndex.length; j++) { // selectedObj
item.detailList[item.defaultSelectedIndex[j]].isSelected = true;
obj[item.key].push(item.detailList[item.defaultSelectedIndex[j]].value)
}
} else {
obj[item.key] = item.detailList[item.defaultSelectedIndex].value;
this.selectedTitleObj[item.key] = item.detailList[item.defaultSelectedIndex].title;
this.defaultSelectedTitleObj[item.key] = item.detailList[item.defaultSelectedIndex].title;
item.detailList[0].isSelected = false;
item.detailList[item.defaultSelectedIndex].isSelected = true;
}
} else {
if (item.isMutiple) {
obj[item.key] = [];
} else {
obj[item.key] = '';
}
}
}
this.result = obj;
return obj;
},
// result
resetAllSelect(callback) {
let titles = [];
for (let i = 0; i < this.menuList.length; i++) {
this.resetSelected(this.menuList[i].detailList, this.menuList[i].key);
titles[this.menuList[i].key] = this.menuList[i].title;
}
let obj = {
'result': this.result,
'titles': titles,
'isReset': true
}
this.$emit("confirm", obj);
callback(this.result);
},
// result
resetSelectToDefault(callback) {
for (let i = 0; i < this.menuList.length; i++) {
this.selectDetailList = this.menuList[i].detailList;
if (this.menuList[i].defaultSelectedIndex) {
if (Array.isArray(this.menuList[i].defaultSelectedIndex)) { // falsetrue
for (let j = 0; j < this.menuList[i].defaultSelectedIndex.length; j++) {
if (this.selectDetailList[this.menuList[i].defaultSelectedIndex[j]].isSelected == false) {
this.itemTap(this.menuList[i].defaultSelectedIndex[j], this.selectDetailList, this
.menuList[i].isMutiple, this
.menuList[i].key)
}
}
} else {
this.itemTap(this.menuList[i].defaultSelectedIndex, this.selectDetailList, this.menuList[i]
.isMutiple, this.menuList[
i].key)
}
//
let unDefaultSelectedIndexArr = this.getUnDefaultSelectedIndex(this.menuList[i])
// truefalse
for (let j = 0; j < unDefaultSelectedIndexArr.length; j++) {
if (this.selectDetailList[unDefaultSelectedIndexArr[j]].isSelected == true) {
this.itemTap(unDefaultSelectedIndexArr[j], this.selectDetailList, this.menuList[i]
.isMutiple, this
.menuList[i].key)
}
}
}
}
this.selectedObj = this.defaultSelectedObj;
this.result = this.defaultSelectedObj;
let obj = {
'result': this.result,
'titles': this.defaultSelectedTitleObj,
'isReset': true
}
this.$emit("confirm", obj);
callback(this.result)
},
getUnDefaultSelectedIndex(menuListItem) { //
let tempDefault = menuListItem.defaultSelectedIndex;
if (!Array.isArray(tempDefault)) {
tempDefault = [tempDefault];
}
//
let all = [];
for (let i = 0; i < menuListItem.detailList.length; i++) {
all.push(i)
}
//
var unDefaultSelectedIndex = tempDefault.filter(function(v) {
return !(all.indexOf(v) > -1)
}).concat(all.filter(function(v) {
return !(tempDefault.indexOf(v) > -1)
}));
return unDefaultSelectedIndex;
},
resetMenuList(val) {
this.menuList = val;
this.$emit('update:menuList', val)
},
menuTabClick(index) {
this.menuIndex = index;
this.selectDetailList = this.menuList[index].detailList;
this.selectedKey = this.menuList[index].key;
//
if (this.independence && !this.menuList[index].isSort) {
if (JSON.stringify(this.independenceObj) == '{}') {
this.initIndependenceObj(index);
} else {
for (let key in this.independenceObj) {
if (key != this.selectedKey) {
this.initIndependenceObj(index);
this.resetSelected(this.menuList[index].detailList, this.selectedKey);
}
}
}
}
if (this.independence && this.menuList[index].isSort) {
this.independenceObj = {};
}
if (this.independence) {
let idx = this.menuList[index].defaultSelectedIndex;
if (idx != null && idx.toString().length > 0) { //
if (this.menuList[index].isMutiple) {
for (let i = 0; i < idx.length; i++) {
if (this.menuList[index].detailList[idx[i]].isSelected == false) {
this.itemTap(idx[i], this.menuList[index].detailList, true, this.selectedKey);
}
}
} else {
if (this.menuList[index].detailList[idx].isSelected == false) {
this.itemTap(idx, this.menuList[index].detailList, false, this.selectedKey);
}
}
}
}
this.selectedObj = this.selectedObj;
this.$forceUpdate();
},
initIndependenceObj(index) {
this.independenceObj = {};
if (this.menuList[index].isMutiple) {
this.independenceObj[this.selectedKey] = [];
} else {
this.independenceObj[this.selectedKey] = '';
}
},
itemTap(index, list, isMutiple, key) {
if (isMutiple == true) {
list[index].isSelected = !list[index].isSelected;
if (index == 0) {
this.resetSelected(list, key)
if (!this.independence) {
this.selectedTitleObj[key] = list[index].title;
}
} else {
list[0].isSelected = false
if (list[index].isSelected) {
if (this.independence) {
this.independenceObj[this.selectedKey].push(list[index].value);
} else {
this.selectedObj[key].push(list[index].value);
}
} else {
list[index].isSelected = false;
if (this.independence) {
var idx = this.independenceObj[this.selectedKey].indexOf(list[index].value);
this.independenceObj[this.selectedKey].splice(idx, 1);
} else {
var idx = this.selectedObj[key].indexOf(list[index].value);
this.selectedObj[key].splice(idx, 1);
}
}
if (this.independence) {
this.result = this.independenceObj;
} else {
this.result = this.selectedObj;
}
}
} else {
if (index == 0) {
this.resetSelected(list, key)
if (!this.independence) {
this.selectedTitleObj[key] = list[index].title;
}
} else {
list[0].isSelected = false
if (this.independence) {
this.independenceObj[this.selectedKey] = list[index].value;
this.result = this.independenceObj;
} else {
this.selectedObj[key] = list[index].value;
this.result = this.selectedObj;
this.selectedTitleObj[key] = list[index].title;
}
for (let i = 0; i < list.length; i++) {
if (index == i) {
list[i].isSelected = true
} else {
list[i].isSelected = false
}
}
}
}
// #ifdef H5
this.$forceUpdate();
// #endif
},
resetSelected(list, key) {
if (typeof this.result[key] == 'object') {
this.result[key] = [];
this.selectedTitleObj[key] = list[0].title;
} else {
this.result[key] = '';
this.selectedTitleObj[key] = list[0].title;
}
for (let i = 0; i < list.length; i++) {
if (i == 0) {
list[i].isSelected = true;
} else {
list[i].isSelected = false;
}
}
// #ifdef H5
this.$forceUpdate();
// #endif
},
sortTap(index, list, key) {
if (this.independence) {
this.independenceObj[this.selectedKey] = list[index].value;
this.result = this.independenceObj;
} else {
this.selectedObj[key] = list[index].value;
this.result = this.selectedObj;
this.selectedTitleObj[key] = list[index].title;
}
for (let i = 0; i < list.length; i++) {
if (index == i) {
list[i].isSelected = true;
} else {
list[i].isSelected = false;
}
}
let obj = {
'result': this.result,
'titles': this.selectedTitleObj,
'isReset': false
}
this.$emit("confirm", obj);
},
sureClick() {
let obj = {
'result': this.result,
'titles': this.selectedTitleObj,
'isReset': false
}
this.$emit("confirm", obj);
},
resetClick(list, key) {
this.resetSelected(list, key)
}
}
}
</script>
<style>
.filter-content {
background-color: #F6F7F8;
}
.filter-content-title {
border-bottom: #EEEEEE 1px solid;
padding: 10px 15px;
font-size: 13px;
color: #999999;
}
.filter-content-detail {
padding: 5px 15px;
}
.filter-content-detail-item-active {
background-color: #D1372C;
color: #FFFFFF;
padding: 5px 15px;
border-radius: 20px;
margin-right: 10px;
margin-top: 10px;
display: inline-block;
font-size: 14px;
}
.filter-content-detail-item-default {
background-color: #FFFFFF;
color: #666666;
padding: 5px 15px;
border-radius: 20px;
margin-right: 10px;
margin-top: 10px;
display: inline-block;
font-size: 14px;
}
.filter-content-footer {
display: flex;
justify-content: space-between;
width: 100%;
height: 45px;
margin-top: 10px;
}
.filter-content-footer-item {
width: 50%;
display: flex;
justify-content: center;
align-items: center;
font-size: 16px;
}
.filter-content-list {
padding: 5px 15px;
}
.filter-content-list-item-default {
color: #666666;
width: 100%;
padding: 10px 0px;
}
.filter-content-list-item-default text {
width: 90%;
font-size: 14px;
display: inline-block;
}
.filter-content-list-item-active {
color: #D1372C;
width: 100%;
padding: 10px 0px;
}
.filter-content-list-item-active text {
font-size: 14px;
width: 90%;
display: inline-block;
}
.filter-content-list-item-active:after {
content: '✓';
}
</style>

20
components/sl-filter/iconfont/iconfont.css

@ -0,0 +1,20 @@
@font-face {
font-family: 'sl-font';
src: url('data:font/truetype;charset=utf-8;base64,AAEAAAALAIAAAwAwR1NVQrD+s+0AAAE4AAAAQk9TLzI8kEgOAAABfAAAAFZjbWFwZO3RAgAAAeAAAAGGZ2x5Zh0ZI/EAAANwAAAAyGhlYWQVZkUXAAAA4AAAADZoaGVhB94DhAAAALwAAAAkaG10eAwAAAAAAAHUAAAADGxvY2EAMgBkAAADaAAAAAhtYXhwAREAKAAAARgAAAAgbmFtZT5U/n0AAAQ4AAACbXBvc3TohGjqAAAGqAAAADMAAQAAA4D/gABcBAAAAAAABAAAAQAAAAAAAAAAAAAAAAAAAAMAAQAAAAEAANxW6kVfDzz1AAsEAAAAAADZJADbAAAAANkkANsAAAAABAACZAAAAAgAAgAAAAAAAAABAAAAAwAcAAQAAAAAAAIAAAAKAAoAAAD/AAAAAAAAAAEAAAAKAB4ALAABREZMVAAIAAQAAAAAAAAAAQAAAAFsaWdhAAgAAAABAAAAAQAEAAQAAAABAAgAAQAGAAAAAQAAAAAAAQQAAZAABQAIAokCzAAAAI8CiQLMAAAB6wAyAQgAAAIABQMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUGZFZABA5hrmHAOA/4AAXAOAAIAAAAABAAAAAAAABAAAAAQAAAAEAAAAAAAABQAAAAMAAAAsAAAABAAAAV4AAQAAAAAAWAADAAEAAAAsAAMACgAAAV4ABAAsAAAABgAEAAEAAuYa5hz//wAA5hrmHP//AAAAAAABAAYABgAAAAEAAgAAAQYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAKAAAAAAAAAACAADmGgAA5hoAAAABAADmHAAA5hwAAAACAAAAAAAAADIAZAAEAAAAAAOlAmQAEwAWABkAGgAAEwEWMjcBNjIWFAcBBiInASY0NjIBMDEVMDEnmQFgAgoDAV8LHRUK/n8LHAv+fwoVHQFoAQJZ/qEDAwFfCxYcC/6ACwsBgAsdFf6bAgQAAAAABAAAAAADpAJkABMAFgAZABsAACUBJiIHAQYiJjQ3ATYyFwEWFAYiATAxNTAxFzEDZ/6hAwoD/qELHRUKAYELHAsBgQoVHf6YAacBXwMD/qELFhwLAYEKCv5/CxwWAWUCBAAAAAAAEgDeAAEAAAAAAAAAFQAAAAEAAAAAAAEACAAVAAEAAAAAAAIABwAdAAEAAAAAAAMACAAkAAEAAAAAAAQACAAsAAEAAAAAAAUACwA0AAEAAAAAAAYACAA/AAEAAAAAAAoAKwBHAAEAAAAAAAsAEwByAAMAAQQJAAAAKgCFAAMAAQQJAAEAEACvAAMAAQQJAAIADgC/AAMAAQQJAAMAEADNAAMAAQQJAAQAEADdAAMAAQQJAAUAFgDtAAMAAQQJAAYAEAEDAAMAAQQJAAoAVgETAAMAAQQJAAsAJgFpCkNyZWF0ZWQgYnkgaWNvbmZvbnQKaWNvbmZvbnRSZWd1bGFyaWNvbmZvbnRpY29uZm9udFZlcnNpb24gMS4waWNvbmZvbnRHZW5lcmF0ZWQgYnkgc3ZnMnR0ZiBmcm9tIEZvbnRlbGxvIHByb2plY3QuaHR0cDovL2ZvbnRlbGxvLmNvbQAKAEMAcgBlAGEAdABlAGQAIABiAHkAIABpAGMAbwBuAGYAbwBuAHQACgBpAGMAbwBuAGYAbwBuAHQAUgBlAGcAdQBsAGEAcgBpAGMAbwBuAGYAbwBuAHQAaQBjAG8AbgBmAG8AbgB0AFYAZQByAHMAaQBvAG4AIAAxAC4AMABpAGMAbwBuAGYAbwBuAHQARwBlAG4AZQByAGEAdABlAGQAIABiAHkAIABzAHYAZwAyAHQAdABmACAAZgByAG8AbQAgAEYAbwBuAHQAZQBsAGwAbwAgAHAAcgBvAGoAZQBjAHQALgBoAHQAdABwADoALwAvAGYAbwBuAHQAZQBsAGwAbwAuAGMAbwBtAAAAAAIAAAAAAAAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwECAQMBBAAEZG93bgJ1cAAAAA==') format('truetype');
}
.sl-font {
font-family: "sl-font" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.sl-down:before {
content: "\e61a";
}
.sl-up:before {
content: "\e61c";
}

122
components/sl-filter/popup-layer.vue

@ -0,0 +1,122 @@
<template>
<scroll-view scroll-y v-show="ifshow" @tap="ableClose" @touchmove.stop.prevent class="popup-layer">
<view ref="popRef" class="popup-content" @tap.stop="stopEvent" :style="_location">
<slot></slot>
</view>
</scroll-view>
</template>
<script>
export default {
name: 'popup-layer',
props: {
direction: {
type: String,
default: 'top', // topbottomleftright
},
autoClose: {
type: Boolean,
default: true,
},
isTransNav: {
type: Boolean,
default: false
},
navHeight: {
type: Number,
default: 0
}
},
data() {
return {
ifshow: false, // ,
translateValue: -100, //
timer: null,
iftoggle: false,
};
},
computed: {
_translate() {
if (this.isTransNav) {
const transformObj = {
'top': `transform:translateY(${-this.translateValue}%)`,
'bottom': `transform:translateY(calc(${this.translateValue}% + ${this.navHeight}px))`,
'left': `transform:translateX(${-this.translateValue}%)`,
'right': `transform:translateX(${this.translateValue}%)`
};
return transformObj[this.direction]
} else {
const transformObj = {
'top': `transform:translateY(${-this.translateValue}%)`,
'bottom': `transform:translateY(${this.translateValue}%)`,
'left': `transform:translateX(${-this.translateValue}%)`,
'right': `transform:translateX(${this.translateValue}%)`
};
return transformObj[this.direction]
}
},
_location() {
const positionValue = {
'top': 'bottom:0px;width:100%;',
'bottom': 'top:0px;width:100%;',
'left': 'right:0px;height:100%;',
'right': 'left:0px;height:100%;',
};
return positionValue[this.direction] + this._translate;
}
},
methods: {
show() {
let _this = this;
this.ifshow = true;
let _open = setTimeout(() => {
this.translateValue = 0;
_open = null;
}, 100)
let _toggle = setTimeout(() => {
this.iftoggle = true;
_toggle = null;
}, 300);
},
close() {
if (this.timer !== null || !this.iftoggle) {
return;
}
this.translateValue = -100 - this.navHeight;
this.timer = setTimeout(() => {
this.ifshow = false;
this.timer = null;
this.iftoggle = false;
}, 300);
this.$emit("close")
},
ableClose() {
if (this.autoClose) {
this.close();
}
},
stopEvent(event) {},
}
}
</script>
<style>
.popup-layer {
position: absolute;
z-index: 999999;
background: rgba(0, 0, 0, .3);
height: calc(100% - 50px);
width: 100%;
left: 0px;
overflow: hidden;
}
.popup-content {
position: absolute;
z-index: 1000000;
background: #FFFFFF;
transition: all .3s ease;
}
</style>

301
components/sl-filter/sl-filter.vue

@ -0,0 +1,301 @@
<template>
<view class="content">
<view :style="{height: tabHeight + 1 +'px'}">
<view :class="topFixed?'select-tab-fixed-top':'select-tab'" :style="{height: tabHeight+'px'}">
<view class="select-tab-item" :style="{width: itemWidth}" v-for="(item,index) in titleList" :key="index"
@tap="showMenuClick(index)">
<text style="display:-webkit-box;-webkit-line-clamp:1;
overflow:hidden;text-overflow:ellipsis;-webkit-box-orient:vertical;
word-break:break-all; " :style="{color:color}">{{item.title}}</text>
<text class="arrows sl-font" :class="statusList[index].isActive?up:down"></text>
</view>
</view>
</view>
<popup-layer ref="popupRef" :direction="'bottom'" @close="close" :isTransNav="isTransNav" :navHeight="navHeight"
:tabHeight="tabHeight">
<sl-filter-view :ref="'slFilterView'" :independence="independence" :themeColor="themeColor"
:menuList.sync="menuListTemp" ref="slFilterView" @confirm="filterResult"></sl-filter-view>
</popup-layer>
</view>
</template>
<script>
import popupLayer from '@/components/sl-filter/popup-layer.vue';
import slFilterView from '@/components/sl-filter/filter-view.vue';
export default {
components: {
popupLayer,
slFilterView
},
props: {
menuList: {
type: Array,
default () {
return []
}
},
themeColor: {
type: String,
default () {
return '#000000'
}
},
color: {
type: String,
default () {
return '#666666'
}
},
independence: {
type: Boolean,
default: true
},
isTransNav: {
type: Boolean,
default: false
},
navHeight: {
type: Number,
default: 0
},
topFixed: {
type: Boolean,
default: false
}
},
computed: {
itemWidth() {
return 'calc(100%/2)'
},
menuListTemp: {
get() {
return this.getMenuListTemp();
},
set(newObj) {
return newObj;
}
}
},
created: function() {
let arr = [];
let titleArr = [];
let r = {};
for (let i = 0; i < this.menuList.length; i++) {
arr.push({
'isActive': false
});
r[this.menuList[i].key] = this.menuList[i].title;
if (this.menuList[i].reflexTitle && this.menuList[i].defaultSelectedIndex > -1) {
titleArr.push({
'title': this.menuList[i].detailList[this.menuList[i].defaultSelectedIndex].title,
'key': this.menuList[i].key
})
} else {
titleArr.push({
'title': this.menuList[i].title,
'key': this.menuList[i].key
})
}
}
this.statusList = arr;
this.titleList = titleArr;
this.tempTitleObj = r;
},
data() {
return {
down: 'sl-down',
up: 'sl-up',
tabHeight: 50,
statusList: [],
selectedIndex: '',
titleList: [],
tempTitleObj: {}
};
},
methods: {
setTitle(obj) {
let list = obj
let titleArr = [];
for (let i = 0; i < list.length; i++) {
if (list[i].reflexTitle && list[i].defaultSelectedIndex > -1) {
titleArr.push({
'title': list[i].detailList[list[i].defaultSelectedIndex].title,
'key': list[i].key
})
} else {
titleArr.push({
'title': list[i].title,
'key': list[i].key
})
}
}
this.titleList = titleArr;
},
refresh() {
let arr = [];
let titleArr = [];
let r = {};
for (let i = 0; i < this.menuList.length; i++) {
arr.push({
'isActive': false
});
r[this.menuList[i].key] = this.menuList[i].title;
if (this.menuList[i].reflexTitle && this.menuList[i].defaultSelectedIndex > -1) {
titleArr.push({
'title': this.menuList[i].detailList[this.menuList[i].defaultSelectedIndex].title,
'key': this.menuList[i].key
})
} else {
titleArr.push({
'title': this.menuList[i].title,
'key': this.menuList[i].key
})
}
}
this.statusList = arr;
this.titleList = titleArr;
this.tempTitleObj = r;
},
getMenuListTemp() {
let arr = this.menuList;
for (let i = 0; i < arr.length; i++) {
let item = arr[i];
for (let j = 0; j < item.detailList.length; j++) {
let d_item = item.detailList[j];
if (j == 0) {
d_item.isSelected = true
} else {
d_item.isSelected = false
}
}
}
return arr;
},
// result
resetAllSelect(callback) {
this.$refs.slFilterView.resetAllSelect(function(e) {
callback(e);
});
},
// result
resetSelectToDefault(callback) {
this.$refs.slFilterView.resetSelectToDefault(function(e) {
callback(e);
});
},
resetSelect(val, index) {
this.$refs.slFilterView.reset1(val, index);
},
resetMenuList(val) {
this.menuList = val;
this.$emit('update:menuList', val)
this.$forceUpdate();
this.$refs.slFilterView.resetMenuList(val)
},
showMenuClick(index) {
this.selectedIndex = index;
if (this.statusList[index].isActive == true) {
this.$refs.popupRef.close();
this.statusList[index].isActive = false
} else {
this.menuTabClick(index);
this.$refs.popupRef.show()
}
},
menuTabClick(index) {
this.$refs.slFilterView.menuTabClick(index);
for (let i = 0; i < this.statusList.length; i++) {
if (index == i) {
this.statusList[i].isActive = true;
} else {
this.statusList[i].isActive = false;
}
}
},
filterResult(obj) {
let val = obj.result;
let titlesObj = obj.titles;
// title
let tempTitle = '';
let tempIndex = 0;
for (let i = 0; i < this.menuList[this.selectedIndex].detailList.length; i++) {
let item = this.menuList[this.selectedIndex].detailList[i];
if (item.value == val[this.menuList[this.selectedIndex].key]) {
tempTitle = item.title;
tempIndex = i;
}
}
if (this.menuList[this.selectedIndex].reflexTitle) {
this.menuList[this.selectedIndex].defaultSelectedIndex = tempIndex;
this.titleList[this.selectedIndex].title = tempTitle;
}
this.$refs.popupRef.close()
if (obj.isReset) {
} else {
this.$emit("result", val)
}
},
close() {
for (let i = 0; i < this.statusList.length; i++) {
this.statusList[i].isActive = false;
}
}
}
}
</script>
<style>
@import 'iconfont/iconfont.css';
.select-tab {
border-bottom: #F7F7F7 1px solid;
background-color: #FFFFFF;
display: flex;
width: 100%;
}
.select-tab-fixed-top {
border-bottom: #F7F7F7 1px solid;
background-color: #FFFFFF;
display: flex;
width: 100%;
position: fixed;
/* #ifdef H5 */
top: 44px;
/* #endif */
/* #ifndef H5 */
top: 0;
/* #endif */
}
.arrows {
margin-left: 5px;
}
.select-tab .select-tab-item,
.select-tab-fixed-top .select-tab-item {
display: flex;
padding-left: 20rpx;
padding-right: 20rpx;
align-items: center;
}
.select-tab .select-tab-item text,
.select-tab-fixed-top .select-tab-item text {
color: #666666;
font-size: 14px;
}
</style>

173
components/tabRefresh/tabRefresh.vue

@ -0,0 +1,173 @@
<template>
<view class='refreshBox' :style="isTranform">
<view class='refresh' :style="isZoom" :class="isEnd==2?'animationSmall':''">
<image class='refreshWord' src='../../static/img/public/shuaxin.png' v-if="isEnd == 0"></image>
<view class='refreshCirle animation' v-if="isEnd == 1"></view>
<image class='iconYes' src='../../static/img/public/icon-yes.png' v-if="isEnd==2"></image>
</view>
</view>
</template>
<script>
export default {
name: 'refresh',
props: {
isTop: {
type: Number,
default: 1
}
},
data() {
return {
isTranf: 0,
touchStart: '',
touchMove: '',
isEnd: 0
};
},
methods: {
refreshStart(e) {
if (this.isEnd == 0 && this.isTop == 1) {
this.touchStart = e.changedTouches[0].pageY
}
},
refreshMove(e) {
if (this.isEnd == 0 && this.isTop == 1) {
var touchStart = this.touchStart,
oldMove = this.touchMove,
newMove = e.changedTouches[0].pageY
if (touchStart <= newMove) {
var isTranf = touchStart > newMove ? 0 : newMove - touchStart
this.isTranf = isTranf
this.touchMove = e.changedTouches[0].pageY
}
} else {
this.isTranf = 0
this.isEnd = 0
this.touchStart = 9999
}
},
refreshEnd(e) {
var that = this
if (this.isEnd == 0 && this.isTop == 1) {
if (this.isTranf >= 90) {
this.isTranf = 125
this.isEnd = 1
this.$emit('isRefresh');
} else {
this.isTranf = 0
}
}
},
endAfter() {
this.isEnd = 2
setTimeout(() => {
this.isTranf = 0
this.isEnd = 0
}, 600)
}
},
computed: {
isTranform() {
var isTranf = this.isTranf > 150 ? 150 : this.isTranf
var isTemp = `transform: translateY(${isTranf-100}px);`
return isTemp
},
isZoom() {
var isTranf = this.isTranf > 125 ? 125 : this.isTranf
var isTemp = `zoom:${isTranf/125};`
return isTemp
}
}
}
</script>
<style lang="scss">
.refreshBox {
margin: 0 auto;
width: 100%;
height: 100upx;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
max-height: 300upx;
position: fixed;
z-index: 999;
top: 0;
left: 0;
transform: translateY(-100upx);
}
.animationSmall {
animation: small 1.1s both;
}
@keyframes small {
0% {
transform: scale(1)
}
20% {
transform: scale(1.4)
}
100% {
transform: scale(0)
}
}
.refreshWord {
width: 26upx;
height: 26upx;
border-radius: 26upx;
}
.refresh {
min-width: 50upx;
display: flex;
align-items: center;
justify-content: center;
height: 50upx;
background: #FFFFFF;
box-shadow: 0 0 16upx 0 rgba(0, 0, 0, 0.10);
border-radius: 50upx;
}
.refreshCirle {
width: 26upx;
height: 26upx;
border-radius: 50%;
display: inline-block;
position: relative;
border: 6upx solid black;
border-bottom-color: transparent;
border-top-color: transparent;
}
.animation {
animation: rotate 1.1s infinite;
animation-timing-function: cubic-bezier(0.3, 1.65, 0.7, -0.65);
}
@keyframes rotate {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.true {
color: black;
}
.iconYes {
width: 34upx;
height: 34upx;
}
</style>

112
components/tabTop/tabTop.vue

@ -0,0 +1,112 @@
<template>
<view class="navTabBox">
<view class="longTab">
<scroll-view scroll-x="true" style="white-space: nowrap; display: flex" scroll-with-animation
:scroll-left="tabLeft">
<view class="longItem" :style='"width:"+isWidth+"px"' :data-index="index"
:class="index===tabClick?'click':''" v-for="(item,index) in tabTitle" :key="index" :id="'id'+index"
@click="longClick(index)">{{item}}
<text v-if="tabNum[index]!=0 && tabNum.length == tabTitle.length && tabNum[index] != undefined"
style="color: #FFFFFF;font-size: 10rpx;background-color: red; width: 20rpx; height: 20rxp; border-radius: 20rpx;padding-left: 2rpx;padding-right: 2rpx;">{{tabNum[index]}}</text>
</view>
<view class="underlineBox" :style='"transform:translateX("+isLeft+"px);width:"+isWidth+"px"'>
<view class="underline"></view>
</view>
</scroll-view>
</view>
</view>
</template>
<script>
export default {
name: 'navTab',
props: {
tabTitle: {
type: Array,
default: []
},
tabNum: {
type: Array,
default: []
}
},
data() {
return {
tabClick: 0, //
isLeft: 0, //线
isWidth: 0, //
tabLeft: 0,
current: 0 //tab
};
},
created() {
var that = this
//
uni.getSystemInfo({
success(e) {
if (that.tabTitle.length <= 5) {
that.isWidth = e.windowWidth / that.tabTitle.length //=
} else {
that.isWidth = e.windowWidth / 5
}
}
})
},
methods: {
//
longClick(index) {
if (this.tabTitle.length > 5) {
var tempIndex = index - 2;
tempIndex = tempIndex <= 0 ? 0 : tempIndex;
this.tabLeft = (index - 2) * this.isWidth //线
}
this.tabClick = index //
this.isLeft = index * this.isWidth //线
if (this.current != index) {
this.$emit('onTabClick', index); //swiper
this.current = index
}
}
}
}
</script>
<style lang="scss">
.navTabBox {
width: 100vw;
color: rgba(255, 255, 255, 0.50);
.click {
color: white;
}
.longTab {
width: 100%;
.longItem {
height: 45px;
display: inline-block;
line-height: 45px;
text-align: center;
font-size: 28rpx;
}
.underlineBox {
height: 3px;
width: 20%;
display: flex;
align-content: center;
justify-content: center;
transition: .5s;
.underline {
width: 42upx;
height: 2px;
background-color: white;
}
}
}
}
</style>

45
components/uni-datetime-picker/keypress.js

@ -0,0 +1,45 @@
// #ifdef H5
export default {
name: 'Keypress',
props: {
disable: {
type: Boolean,
default: false
}
},
mounted () {
const keyNames = {
esc: ['Esc', 'Escape'],
tab: 'Tab',
enter: 'Enter',
space: [' ', 'Spacebar'],
up: ['Up', 'ArrowUp'],
left: ['Left', 'ArrowLeft'],
right: ['Right', 'ArrowRight'],
down: ['Down', 'ArrowDown'],
delete: ['Backspace', 'Delete', 'Del']
}
const listener = ($event) => {
if (this.disable) {
return
}
const keyName = Object.keys(keyNames).find(key => {
const keyName = $event.key
const value = keyNames[key]
return value === keyName || (Array.isArray(value) && value.includes(keyName))
})
if (keyName) {
// 避免和其他按键事件冲突
setTimeout(() => {
this.$emit(keyName, {})
}, 0)
}
}
document.addEventListener('keyup', listener)
this.$once('hook:beforeDestroy', () => {
document.removeEventListener('keyup', listener)
})
},
render: () => {}
}
// #endif

804
components/uni-datetime-picker/uni-datetime-picker.vue

@ -0,0 +1,804 @@
<template>
<view class="uni-datetime-picker">
<view @click="initTimePicker">
<slot>
<view class="uni-datetime-picker-timebox uni-datetime-picker-flex"
:class="{'uni-datetime-picker-disabled': disabled}">
{{time}}
<view v-if="!time" class="uni-datetime-picker-time">
选择{{title}}
</view>
<view class="uni-datetime-picker-down-arrow"></view>
</view>
</slot>
</view>
<view v-if="visible" class="uni-datetime-picker-mask" @click="tiggerTimePicker"></view>
<view v-if="visible" class="uni-datetime-picker-popup">
<view class="uni-title">
设置{{title}}
</view>
<picker-view v-show="dateShow" class="uni-datetime-picker-view" :indicator-style="indicatorStyle"
:value="ymd" @change="bindDateChange">
<picker-view-column class="uni-datetime-picker-hyphen">
<view class="uni-datetime-picker-item" v-for="(item,index) in years" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column class="uni-datetime-picker-hyphen">
<view class="uni-datetime-picker-item" v-for="(item,index) in months" :key="index">
{{item < 10 ? ((item+"").startsWith("0")? item:('0' +item)) : item}}
</view>
</picker-view-column>
<picker-view-column>
<view class="uni-datetime-picker-item" v-for="(item,index) in days" :key="index">
{{item < 10 ? '0' + item : item}}
</view>
</picker-view-column>
</picker-view>
<picker-view v-show="timeShow" class="uni-datetime-picker-view" :indicator-style="indicatorStyle"
:value="hms" @change="bindTimeChange">
<picker-view-column class="uni-datetime-picker-colon">
<view class="uni-datetime-picker-item" v-for="(item,index) in hours" :key="index">
{{item < 10 ? '0' + item : item}}
</view>
</picker-view-column>
<picker-view-column class="uni-datetime-picker-colon">
<view class="uni-datetime-picker-item" v-for="(item,index) in minutes" :key="index">
{{item < 10 ? '0' + item : item}}
</view>
</picker-view-column>
<picker-view-column>
<view class="uni-datetime-picker-item" v-for="(item,index) in seconds" :key="index">
{{item < 10 ? '0' + item : item}}
</view>
</picker-view-column>
</picker-view>
<view class="uni-datetime-picker-btn">
<view class="" @click="clearTime">清空</view>
<view class="uni-datetime-picker-btn-group">
<view class="uni-datetime-picker-cancel" @click="tiggerTimePicker">取消</view>
<view class="" @click="setTime">确定</view>
</view>
</view>
</view>
</view>
</template>
<script>
/**
* DatetimePicker 时间选择器
* @description 可以同时选择日期和时间的选择器
* @tutorial https://ext.dcloud.net.cn/plugin?id=xxx
* @property {String} type = [datetime | date | time] 显示模式
* @property {Boolean} multiple = [true|false] 是否多选
* @property {String|Number} value 默认值
* @property {String|Number} start 起始日期或时间
* @property {String|Number} end 起始日期或时间
* @property {String} return-type = [timestamp | string]
* @event {Function} change 选中发生变化触发
*/
export default {
data() {
return {
indicatorStyle: `height: 50px;`,
visible: false,
dateShow: true,
timeShow: true,
title: '日期和时间',
//
time: '',
//
year: 1900,
month: 0,
day: 0,
hour: 0,
minute: 0,
second: 0,
//
startYear: 1920,
startMonth: 1,
startDay: 1,
startHour: 0,
startMinute: 0,
startSecond: 0,
//
endYear: 2120,
endMonth: 12,
endDay: 31,
endHour: 23,
endMinute: 59,
endSecond: 59,
}
},
props: {
type: {
type: String,
default: 'datetime'
},
value: {
type: [String, Number],
default: ''
},
start: {
type: [Number, String],
default: ''
},
end: {
type: [Number, String],
default: ''
},
returnType: {
type: String,
default: 'string'
},
disabled: {
type: Boolean,
default: false
}
},
watch: {
value: {
handler(newVal, oldVal) {
if (newVal) {
this.parseValue(this.fixIosDateFormat(newVal)) // iOSsafari
this.initTime()
} else {
this.parseValue(Date.now())
}
},
immediate: true
},
type: {
handler(newValue) {
if (newValue === 'date') {
this.dateShow = true
this.timeShow = false
this.title = '日期'
} else if (newValue === 'time') {
this.dateShow = false
this.timeShow = true
this.title = '时间'
} else {
this.dateShow = true
this.timeShow = true
this.title = '日期和时间'
}
},
immediate: true
},
start: {
handler(newVal) {
this.parseDatetimeRange(this.fixIosDateFormat(newVal), 'start') // iOSsafari
},
immediate: true
},
end: {
handler(newVal) {
this.parseDatetimeRange(this.fixIosDateFormat(newVal), 'end') // iOSsafari
},
immediate: true
},
//
months(newVal) {
this.checkValue('month', this.month, newVal)
},
days(newVal) {
this.checkValue('day', this.day, newVal)
},
hours(newVal) {
this.checkValue('hour', this.hour, newVal)
},
minutes(newVal) {
this.checkValue('minute', this.minute, newVal)
},
seconds(newVal) {
this.checkValue('second', this.second, newVal)
}
},
created() {
this.form = this.getForm('uniForms')
this.formItem = this.getForm('uniFormsItem')
if (this.formItem) {
if (this.formItem.name) {
this.rename = this.formItem.name
this.form.inputChildrens.push(this)
}
}
},
computed: {
//
years() {
return this.getCurrentRange('year')
},
months() {
return this.getCurrentRange('month')
},
days() {
return this.getCurrentRange('day')
},
hours() {
return this.getCurrentRange('hour')
},
minutes() {
return this.getCurrentRange('minute')
},
seconds() {
return this.getCurrentRange('second')
},
// picker
ymd() {
return [this.year - this.minYear, this.month - this.minMonth, this.day - this.minDay]
},
hms() {
return [this.hour - this.minHour, this.minute - this.minMinute, this.second - this.minSecond]
},
// date start
currentDateIsStart() {
return this.year === this.startYear && this.month === this.startMonth && this.day === this.startDay
},
// date end
currentDateIsEnd() {
return this.year === this.endYear && this.month === this.endMonth && this.day === this.endDay
},
//
minYear() {
return this.startYear
},
maxYear() {
return this.endYear
},
minMonth() {
if (this.year === this.startYear) {
return this.startMonth
} else {
return 1
}
},
maxMonth() {
if (this.year === this.endYear) {
return this.endMonth
} else {
return 12
}
},
minDay() {
if (this.year === this.startYear && this.month === this.startMonth) {
return this.startDay
} else {
return 1
}
},
maxDay() {
if (this.year === this.endYear && this.month === this.endMonth) {
return this.endDay
} else {
return this.daysInMonth(this.year, this.month)
}
},
minHour() {
if (this.type === 'datetime') {
if (this.currentDateIsStart) {
return this.startHour
} else {
return 0
}
}
if (this.type === 'time') {
return this.startHour
}
},
maxHour() {
if (this.type === 'datetime') {
if (this.currentDateIsEnd) {
return this.endHour
} else {
return 23
}
}
if (this.type === 'time') {
return this.endHour
}
},
minMinute() {
if (this.type === 'datetime') {
if (this.currentDateIsStart && this.hour === this.startHour) {
return this.startMinute
} else {
return 0
}
}
if (this.type === 'time') {
if (this.hour === this.startHour) {
return this.startMinute
} else {
return 0
}
}
},
maxMinute() {
if (this.type === 'datetime') {
if (this.currentDateIsEnd && this.hour === this.startHour) {
return this.endMinute
} else {
return 59
}
}
if (this.type === 'time') {
if (this.hour === this.endHour) {
return this.endMinute
} else {
return 59
}
}
},
minSecond() {
if (this.type === 'datetime') {
if (this.currentDateIsStart && this.hour === this.startHour && this.minute === this.startMinute) {
return this.startSecond
} else {
return 0
}
}
if (this.type === 'time') {
if (this.hour === this.startHour && this.minute === this.startMinute) {
return this.startSecond
} else {
return 0
}
}
},
maxSecond() {
if (this.type === 'datetime') {
if (this.currentDateIsEnd && this.hour === this.startHour && this.minute === this.endMinute) {
return this.endSecond
} else {
return 59
}
}
if (this.type === 'time') {
if (this.hour === this.endHour && this.minute === this.endMinute) {
return this.endSecond
} else {
return 59
}
}
}
},
methods: {
/**
* 获取父元素实例
*/
getForm(name = 'uniForms') {
let parent = this.$parent;
let parentName = parent.$options.name;
while (parentName !== name) {
parent = parent.$parent;
if (!parent) return false
parentName = parent.$options.name;
}
return parent;
},
/**
* 解析时分秒字符串例如00:00:00
* @param {String} timeString
*/
parseTimeType(timeString) {
if (timeString) {
let timeArr = timeString.split(':')
this.hour = Number(timeArr[0])
this.minute = Number(timeArr[1])
this.second = Number(timeArr[2])
}
},
/**
* 解析选择器初始值类型可以是字符串时间戳例如2000-10-02'08:30:00' 1610695109000
* @param {String | Number} datetime
*/
initPickerValue(datetime) {
let defaultValue = null
if (datetime) {
defaultValue = this.compareValueWithStartAndEnd(datetime, this.start, this.end)
} else {
defaultValue = Date.now()
defaultValue = this.compareValueWithStartAndEnd(defaultValue, this.start, this.end)
}
this.parseValue(defaultValue)
},
/**
* 初始值规则
* - 用户设置初始值 value
* - 设置了起始时间 start终止时间 end start < value < end初始值为 value 否则初始值为 start
* - 只设置了起始时间 start start < value初始值为 value否则初始值为 start
* - 只设置了终止时间 end value < end初始值为 value否则初始值为 end
* - 无起始终止时间则初始值为 value
* - 无初始值 value则初始值为当前本地时间 Date.now()
* @param {Object} value
* @param {Object} dateBase
*/
compareValueWithStartAndEnd(value, start, end) {
let winner = null
value = this.superTimeStamp(value)
start = this.superTimeStamp(start)
end = this.superTimeStamp(end)
if (start && end) {
if (value < start) {
winner = new Date(start)
} else if (value > end) {
winner = new Date(end)
} else {
winner = new Date(value)
}
} else if (start && !end) {
winner = start <= value ? new Date(value) : new Date(start)
} else if (!start && end) {
winner = value <= end ? new Date(value) : new Date(end)
} else {
winner = new Date(value)
}
return winner
},
/**
* 转换为可比较的时间戳接受日期时分秒时间戳
* @param {Object} value
*/
superTimeStamp(value) {
let dateBase = ''
if (this.type === 'time' && value && typeof value === 'string') {
const now = new Date()
const year = now.getFullYear()
const month = now.getMonth() + 1
const day = now.getDate()
dateBase = year + '/' + month + '/' + day + ' '
}
if (Number(value) && typeof value !== NaN) {
value = parseInt(value)
dateBase = 0
}
return this.createTimeStamp(dateBase + value)
},
/**
* 解析默认值 value字符串时间戳
* @param {Object} defaultTime
*/
parseValue(value) {
if (!value) return
if (this.type === 'time' && typeof value === "string") {
this.parseTimeType(value)
} else {
let defaultDate = null
defaultDate = new Date(value)
if (this.type !== 'time') {
this.year = defaultDate.getFullYear()
this.month = defaultDate.getMonth() + 1
this.day = defaultDate.getDate()
}
if (this.type !== 'date') {
this.hour = defaultDate.getHours()
this.minute = defaultDate.getMinutes()
this.second = defaultDate.getSeconds()
}
}
},
/**
* 解析可选择时间范围 startend年月日字符串时间戳
* @param {Object} defaultTime
*/
parseDatetimeRange(point, pointType) {
if (point && this.type === 'time') {
const pointArr = point.split(':')
this[pointType + 'Hour'] = Number(pointArr[0])
this[pointType + 'Minute'] = Number(pointArr[1])
this[pointType + 'Second'] = Number(pointArr[2])
} else {
if (!point) {
pointType === 'start' ? this.startYear = this.year - 60 : this.endYear = this.year + 60
return
}
if (Number(point) && Number(point) !== NaN) {
point = parseInt(point)
}
// datetime end ,
const hasTime = /[0-9]:[0-9]/
if (this.type === 'datetime' && pointType === 'end' && typeof point === 'string' && !hasTime.test(
point)) {
point = point + ' 23:59:59'
}
// 2016/09/19 17:04:26:719
const split = point.split(" ")
const dataStr = split[0]
const timeStr = split[1]
this[pointType + 'Year'] = dataStr.split("/")[0]
this[pointType + 'Month'] = dataStr.split("/")[1]
this[pointType + 'Day'] = dataStr.split("/")[2]
if (this.type === 'datetime') {
this[pointType + 'Hour'] = timeStr.split(":")[0]
this[pointType + 'Minute'] = timeStr.split(":")[1]
this[pointType + 'Second'] = timeStr.split(":")[2]
}
}
},
//
getCurrentRange(value) {
const range = []
for (let i = this['min' + this.capitalize(value)]; i <= this['max' + this.capitalize(value)]; i++) {
range.push(i)
}
return range
},
//
capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1)
},
//
checkValue(name, value, values) {
if (values.indexOf(value) === -1) {
this[name] = values[0]
}
},
//
daysInMonth(year, month) { // Use 1 for January, 2 for February, etc.
return new Date(year, month, 0).getDate();
},
// iOSsafari
fixIosDateFormat(value) {
if (typeof value === 'string') {
value = value.replace(/-/g, '/')
}
return value
},
/**
* 生成时间戳
* @param {Object} time
*/
createTimeStamp(time) {
if (!time) return
if (typeof time === "number") {
return time
} else {
time = time.replace(/-/g, '/')
if (this.type === 'date') {
time = time + ' ' + '00:00:00'
}
return Date.parse(time)
}
},
/**
* 生成日期或时间的字符串
*/
createDomSting() {
const yymmdd = this.year +
'-' +
(this.month < 10 ? '0' + this.month : this.month) +
'-' +
(this.day < 10 ? '0' + this.day : this.day)
const hhmmss = (this.hour < 10 ? '0' + this.hour : this.hour) +
':' +
(this.minute < 10 ? '0' + this.minute : this.minute) +
':' +
(this.second < 10 ? '0' + this.second : this.second)
if (this.type === 'date') {
return yymmdd
} else if (this.type === 'time') {
return hhmmss
} else {
return yymmdd + ' ' + hhmmss
}
},
/**
* 初始化返回值并抛出 change 事件
*/
initTime() {
this.time = this.createDomSting()
if (this.returnType === 'timestamp' && this.type !== 'time') {
this.formItem && this.formItem.setValue(this.createTimeStamp(this.time))
this.$emit('change', this.createTimeStamp(this.time))
this.$emit('input', this.createTimeStamp(this.time))
} else {
this.formItem && this.formItem.setValue(this.time)
this.$emit('change', this.time)
this.$emit('input', this.time)
}
},
/**
* 用户选择日期或时间更新 data
* @param {Object} e
*/
bindDateChange(e) {
const val = e.detail.value
this.year = this.years[val[0]]
this.month = this.months[val[1]]
this.day = this.days[val[2]]
},
bindTimeChange(e) {
const val = e.detail.value
this.hour = this.hours[val[0]]
this.minute = this.minutes[val[1]]
this.second = this.seconds[val[2]]
},
/**
* 初始化弹出层
*/
initTimePicker() {
if (this.disabled) return
const value = this.fixIosDateFormat(this.value)
this.initPickerValue(value)
this.visible = !this.visible
},
/**
* 触发或关闭弹框
*/
tiggerTimePicker() {
this.visible = !this.visible
},
/**
* 用户点击清空按钮清空当前值
*/
clearTime() {
this.time = ''
this.formItem && this.formItem.setValue(this.time)
this.$emit('change', this.time)
this.$emit('input', this.time)
this.tiggerTimePicker()
},
/**
* 用户点击确定按钮
*/
setTime() {
this.initTime()
this.tiggerTimePicker()
}
}
}
</script>
<style>
.uni-datetime-picker-view {
width: 100%;
height: 130px;
margin-top: 30px;
cursor: pointer;
}
.uni-datetime-picker-item {
line-height: 50px;
text-align: center;
}
.uni-datetime-picker-btn {
margin-top: 60px;
display: flex;
justify-content: space-between;
color: #007AFF;
cursor: pointer;
}
.uni-datetime-picker-btn-group {
display: flex;
}
.uni-datetime-picker-cancel {
margin-right: 30px;
}
.uni-datetime-picker-mask {
position: fixed;
bottom: 0px;
top: 0px;
left: 0px;
right: 0px;
background-color: rgba(0, 0, 0, 0.4);
transition-duration: 0.3s;
z-index: 998;
}
.uni-datetime-picker-popup {
border-radius: 8px;
padding: 30px;
width: 270px;
background-color: #fff;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
transition-duration: 0.3s;
z-index: 999;
}
.uni-datetime-picker-time {
color: grey;
}
.uni-datetime-picker-colon::after {
content: ':';
position: absolute;
top: 53px;
right: 0;
}
.uni-datetime-picker-hyphen::after {
content: '-';
position: absolute;
top: 53px;
right: -2px;
}
.uni-datetime-picker-timebox {
border: 1px solid #E5E5E5;
border-radius: 5px;
padding: 7px 10px;
box-sizing: border-box;
cursor: pointer;
}
//
.uni-datetime-picker-down-arrow {
display: inline-block;
position: relative;
width: 20px;
height: 15px;
}
.uni-datetime-picker-down-arrow::after {
display: inline-block;
content: " ";
height: 9px;
width: 9px;
border-width: 0 1px 1px 0;
border-color: #E5E5E5;
border-style: solid;
transform: matrix(0.71, 0.71, -0.71, 0.71, 0, 0);
transform-origin: center;
transition: transform .3s;
position: absolute;
top: 50%;
right: 5px;
margin-top: -5px;
}
.uni-datetime-picker-flex {
display: flex;
justify-content: space-between;
}
.uni-datetime-picker-disabled {
opacity: 0.4;
/* #ifdef H5 */
cursor: not-allowed !important;
/* #endif */
}
</style>

243
components/uni-popup-dialog/uni-popup-dialog.vue

@ -0,0 +1,243 @@
<template>
<view class="uni-popup-dialog">
<view class="uni-dialog-title">
<text class="uni-dialog-title-text" :class="['uni-popup__'+dialogType]">{{title}}</text>
</view>
<view class="uni-dialog-content">
<text class="uni-dialog-content-text" v-if="mode === 'base'">{{content}}</text>
<input v-else class="uni-dialog-input" v-model="val" type="text" :placeholder="placeholder" :focus="focus">
</view>
<view class="uni-dialog-button-group">
<view class="uni-dialog-button" @click="close">
<text class="uni-dialog-button-text">取消</text>
</view>
<view class="uni-dialog-button uni-border-left" @click="onOk">
<text class="uni-dialog-button-text uni-button-color">确定</text>
</view>
</view>
</view>
</template>
<script>
/**
* PopUp 弹出层-对话框样式
* @description 弹出层-对话框样式
* @tutorial https://ext.dcloud.net.cn/plugin?id=329
* @property {String} value input 模式下的默认值
* @property {String} placeholder input 模式下输入提示
* @property {String} type = [success|warning|info|error] 主题样式
* @value success 成功
* @value warning 提示
* @value info 消息
* @value error 错误
* @property {String} mode = [base|input] 模式
* @value base 基础对话框
* @value input 可输入对话框
* @property {String} content 对话框内容
* @property {Boolean} beforeClose 是否拦截取消事件
* @event {Function} confirm 点击确认按钮触发
* @event {Function} close 点击取消按钮触发
*/
export default {
name: "uniPopupDialog",
props: {
value: {
type: [String, Number],
default: ''
},
placeholder: {
type: [String, Number],
default: '请输入内容'
},
/**
* 对话框主题 success/warning/info/error 默认 success
*/
type: {
type: String,
default: 'error'
},
/**
* 对话框模式 base/input
*/
mode: {
type: String,
default: 'base'
},
/**
* 对话框标题
*/
title: {
type: String,
default: '提示'
},
/**
* 对话框内容
*/
content: {
type: String,
default: ''
},
/**
* 拦截取消事件 如果拦截取消事件必须监听close事件执行 done()
*/
beforeClose: {
type: Boolean,
default: false
}
},
data() {
return {
dialogType: 'error',
focus: false,
val: ""
}
},
inject: ['popup'],
watch: {
type(val) {
this.dialogType = val
},
mode(val) {
if (val === 'input') {
this.dialogType = 'info'
}
},
value(val) {
this.val = val
}
},
created() {
//
this.popup.mkclick = false
if (this.mode === 'input') {
this.dialogType = 'info'
this.val = this.value
} else {
this.dialogType = this.type
}
},
mounted() {
this.focus = true
},
methods: {
/**
* 点击确认按钮
*/
onOk() {
this.$emit('confirm', () => {
this.popup.close()
if (this.mode === 'input') this.val = this.value
}, this.mode === 'input' ? this.val : '')
},
/**
* 点击取消按钮
*/
close() {
if (this.beforeClose) {
this.$emit('close', () => {
this.popup.close()
})
return
}
this.popup.close()
}
}
}
</script>
<style lang="scss" scoped>
.uni-popup-dialog {
width: 300px;
border-radius: 15px;
background-color: #fff;
}
.uni-dialog-title {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: center;
padding-top: 15px;
padding-bottom: 5px;
}
.uni-dialog-title-text {
font-size: 16px;
font-weight: 500;
}
.uni-dialog-content {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: center;
align-items: center;
padding: 5px 15px 15px 15px;
}
.uni-dialog-content-text {
font-size: 14px;
color: #6e6e6e;
}
.uni-dialog-button-group {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
border-top-color: #f5f5f5;
border-top-style: solid;
border-top-width: 1px;
}
.uni-dialog-button {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex: 1;
flex-direction: row;
justify-content: center;
align-items: center;
height: 45px;
}
.uni-border-left {
border-left-color: #f0f0f0;
border-left-style: solid;
border-left-width: 1px;
}
.uni-dialog-button-text {
font-size: 14px;
}
.uni-button-color {
color: $uni-color-primary;
}
.uni-dialog-input {
flex: 1;
font-size: 14px;
}
.uni-popup__success {
color: $uni-color-success;
}
.uni-popup__warn {
color: $uni-color-warning;
}
.uni-popup__error {
color: $uni-color-error;
}
.uni-popup__info {
color: #909399;
}
</style>

22
components/uni-popup/message.js

@ -0,0 +1,22 @@
export default {
created() {
if (this.type === 'message') {
// 不显示遮罩
this.maskShow = false
// 获取子组件对象
this.childrenMsg = null
}
},
methods: {
customOpen() {
if (this.childrenMsg) {
this.childrenMsg.open()
}
},
customClose() {
if (this.childrenMsg) {
this.childrenMsg.close()
}
}
}
}

25
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],
}

294
components/uni-popup/uni-popup.vue

@ -0,0 +1,294 @@
<template>
<view v-if="showPopup" class="uni-popup" :class="[popupstyle]" @touchmove.stop.prevent="clear">
<uni-transition v-if="maskShow" :mode-class="['fade']" :styles="maskClass" :duration="duration"
:show="showTrans" @click="onTap" />
<uni-transition :mode-class="ani" :styles="transClass" :duration="duration" :show="showTrans" @click="onTap">
<view class="uni-popup__wrapper-box" @click.stop="clear">
<slot />
</view>
</uni-transition>
</view>
</template>
<script>
import uniTransition from '../uni-transition/uni-transition.vue'
import popup from './popup.js'
/**
* PopUp 弹出层
* @description 弹出层组件为了解决遮罩弹层的问题
* @tutorial https://ext.dcloud.net.cn/plugin?id=329
* @property {String} type = [top|center|bottom] 弹出方式
* @value top 顶部弹出
* @value center 中间弹出
* @value bottom 底部弹出
* @value message 消息提示
* @value dialog 对话框
* @value share 底部分享示例
* @property {Boolean} animation = [ture|false] 是否开启动画
* @property {Boolean} maskClick = [ture|false] 蒙版点击是否关闭弹窗
* @event {Function} change 打开关闭弹窗触发e={show: false}
*/
export default {
name: 'UniPopup',
components: {
uniTransition
},
props: {
//
animation: {
type: Boolean,
default: true
},
// top: bottomcenter
// message: ; dialog :
type: {
type: String,
default: 'center'
},
// maskClick
maskClick: {
type: Boolean,
default: true
}
},
provide() {
return {
popup: this
}
},
mixins: [popup],
watch: {
/**
* 监听type类型
*/
type: {
handler: function(newVal) {
this[this.config[newVal]]()
},
immediate: true
},
/**
* 监听遮罩是否可点击
* @param {Object} val
*/
maskClick(val) {
this.mkclick = val
}
},
data() {
return {
duration: 300,
ani: [],
showPopup: false,
showTrans: false,
maskClass: {
'position': 'fixed',
'bottom': 0,
'top': 0,
'left': 0,
'right': 0,
'backgroundColor': 'rgba(0, 0, 0, 0.4)'
},
transClass: {
'position': 'fixed',
'left': 0,
'right': 0,
},
maskShow: true,
mkclick: true,
popupstyle: 'top'
}
},
created() {
this.mkclick = this.maskClick
if (this.animation) {
this.duration = 300
} else {
this.duration = 0
}
},
methods: {
clear(e) {
// TODO nvue
e.stopPropagation()
},
open() {
this.showPopup = true
this.$nextTick(() => {
new Promise(resolve => {
clearTimeout(this.timer)
this.timer = setTimeout(() => {
this.showTrans = true
// fixed by mehaotian app
this.$nextTick(() => {
resolve();
})
}, 50);
}).then(res => {
//
clearTimeout(this.msgtimer)
this.msgtimer = setTimeout(() => {
this.customOpen && this.customOpen()
}, 100)
this.$emit('change', {
show: true,
type: this.type
})
})
})
},
close(type) {
this.showTrans = false
this.$nextTick(() => {
this.$emit('change', {
show: false,
type: this.type
})
clearTimeout(this.timer)
//
this.customOpen && this.customClose()
this.timer = setTimeout(() => {
this.showPopup = false
}, 300)
})
},
onTap() {
if (!this.mkclick) return
this.close()
},
/**
* 顶部弹出样式处理
*/
top() {
this.popupstyle = 'top'
this.ani = ['slide-top']
this.transClass = {
'position': 'fixed',
'left': 0,
'right': 0,
}
},
/**
* 底部弹出样式处理
*/
bottom() {
this.popupstyle = 'bottom'
this.ani = ['slide-bottom']
this.transClass = {
'position': 'fixed',
'left': 0,
'right': 0,
'bottom': 0
}
},
/**
* 中间弹出样式处理
*/
center() {
this.popupstyle = 'center'
this.ani = ['zoom-out', 'fade']
this.transClass = {
'position': 'fixed',
/* #ifndef APP-NVUE */
'display': 'flex',
'flexDirection': 'column',
/* #endif */
'bottom': 0,
'left': 0,
'right': 0,
'top': 0,
'justifyContent': 'center',
'alignItems': 'center'
}
}
}
}
</script>
<style lang="scss" scoped>
.uni-popup {
position: fixed;
/* #ifndef APP-NVUE */
z-index: 9999;
/* #endif */
}
.uni-popup__mask {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: $uni-bg-color-mask;
opacity: 0;
}
.mask-ani {
transition-property: opacity;
transition-duration: 0.2s;
}
.uni-top-mask {
opacity: 1;
}
.uni-bottom-mask {
opacity: 1;
}
.uni-center-mask {
opacity: 1;
}
.uni-popup__wrapper {
/* #ifndef APP-NVUE */
display: block;
/* #endif */
position: absolute;
}
.top {
/* #ifdef H5 */
top: var(--window-top);
/* #endif */
/* #ifndef H5 */
top: 0;
/* #endif */
}
.bottom {
bottom: 0;
}
.uni-popup__wrapper-box {
/* #ifndef APP-NVUE */
display: block;
/* #endif */
position: relative;
/* iphonex 等安全区设置,底部安全区适配 */
/* #ifndef APP-NVUE */
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
/* #endif */
}
.content-ani {
// transition: transform 0.3s;
transition-property: transform, opacity;
transition-duration: 0.2s;
}
.uni-top-content {
transform: translateY(0);
}
.uni-bottom-content {
transform: translateY(0);
}
.uni-center-content {
transform: scale(1);
opacity: 1;
}
</style>

280
components/uni-transition/uni-transition.vue

@ -0,0 +1,280 @@
<template>
<view v-if="isShow" ref="ani" class="uni-transition" :class="[ani.in]"
:style="'transform:' +transform+';'+stylesObject" @click="change">
<slot></slot>
</view>
</template>
<script>
// #ifdef APP-NVUE
const animation = uni.requireNativePlugin('animation');
// #endif
/**
* Transition 过渡动画
* @description 简单过渡动画组件
* @tutorial https://ext.dcloud.net.cn/plugin?id=985
* @property {Boolean} show = [false|true] 控制组件显示或隐藏
* @property {Array} modeClass = [fade|slide-top|slide-right|slide-bottom|slide-left|zoom-in|zoom-out] 过渡动画类型
* @value fade 渐隐渐出过渡
* @value slide-top 由上至下过渡
* @value slide-right 由右至左过渡
* @value slide-bottom 由下至上过渡
* @value slide-left 由左至右过渡
* @value zoom-in 由小到大过渡
* @value zoom-out 由大到小过渡
* @property {Number} duration 过渡动画持续时间
* @property {Object} styles 组件样式 css 样式注意带-连接符的属性需要使用小驼峰写法如`backgroundColor:red`
*/
export default {
name: 'uniTransition',
props: {
show: {
type: Boolean,
default: false
},
modeClass: {
type: Array,
default () {
return []
}
},
duration: {
type: Number,
default: 300
},
styles: {
type: Object,
default () {
return {}
}
}
},
data() {
return {
isShow: false,
transform: '',
ani: {
in: '',
active: ''
}
};
},
watch: {
show: {
handler(newVal) {
if (newVal) {
this.open()
} else {
this.close()
}
},
immediate: true
}
},
computed: {
stylesObject() {
let styles = {
...this.styles,
'transition-duration': this.duration / 1000 + 's'
}
let transfrom = ''
for (let i in styles) {
let line = this.toLine(i)
transfrom += line + ':' + styles[i] + ';'
}
return transfrom
}
},
created() {
// this.timer = null
// this.nextTick = (time = 50) => new Promise(resolve => {
// clearTimeout(this.timer)
// this.timer = setTimeout(resolve, time)
// return this.timer
// });
},
methods: {
change() {
this.$emit('click', {
detail: this.isShow
})
},
open() {
clearTimeout(this.timer)
this.isShow = true
this.transform = ''
this.ani.in = ''
for (let i in this.getTranfrom(false)) {
if (i === 'opacity') {
this.ani.in = 'fade-in'
} else {
this.transform += `${this.getTranfrom(false)[i]} `
}
}
this.$nextTick(() => {
setTimeout(() => {
this._animation(true)
}, 50)
})
},
close(type) {
clearTimeout(this.timer)
this._animation(false)
},
_animation(type) {
let styles = this.getTranfrom(type)
// #ifdef APP-NVUE
if (!this.$refs['ani']) return
animation.transition(this.$refs['ani'].ref, {
styles,
duration: this.duration, //ms
timingFunction: 'ease',
needLayout: false,
delay: 0 //ms
}, () => {
if (!type) {
this.isShow = false
}
this.$emit('change', {
detail: this.isShow
})
})
// #endif
// #ifndef APP-NVUE
this.transform = ''
for (let i in styles) {
if (i === 'opacity') {
this.ani.in = `fade-${type?'out':'in'}`
} else {
this.transform += `${styles[i]} `
}
}
this.timer = setTimeout(() => {
if (!type) {
this.isShow = false
}
this.$emit('change', {
detail: this.isShow
})
}, this.duration)
// #endif
},
getTranfrom(type) {
let styles = {
transform: ''
}
this.modeClass.forEach((mode) => {
switch (mode) {
case 'fade':
styles.opacity = type ? 1 : 0
break;
case 'slide-top':
styles.transform += `translateY(${type?'0':'-100%'}) `
break;
case 'slide-right':
styles.transform += `translateX(${type?'0':'100%'}) `
break;
case 'slide-bottom':
styles.transform += `translateY(${type?'0':'100%'}) `
break;
case 'slide-left':
styles.transform += `translateX(${type?'0':'-100%'}) `
break;
case 'zoom-in':
styles.transform += `scale(${type?1:0.8}) `
break;
case 'zoom-out':
styles.transform += `scale(${type?1:1.2}) `
break;
}
})
return styles
},
_modeClassArr(type) {
let mode = this.modeClass
if (typeof(mode) !== "string") {
let modestr = ''
mode.forEach((item) => {
modestr += (item + '-' + type + ',')
})
return modestr.substr(0, modestr.length - 1)
} else {
return mode + '-' + type
}
},
// getEl(el) {
// console.log(el || el.ref || null);
// return el || el.ref || null
// },
toLine(name) {
return name.replace(/([A-Z])/g, "-$1").toLowerCase();
}
}
}
</script>
<style>
.uni-transition {
transition-timing-function: ease;
transition-duration: 0.3s;
transition-property: transform, opacity;
}
.fade-in {
opacity: 0;
}
.fade-active {
opacity: 1;
}
.slide-top-in {
/* transition-property: transform, opacity; */
transform: translateY(-100%);
}
.slide-top-active {
transform: translateY(0);
/* opacity: 1; */
}
.slide-right-in {
transform: translateX(100%);
}
.slide-right-active {
transform: translateX(0);
}
.slide-bottom-in {
transform: translateY(100%);
}
.slide-bottom-active {
transform: translateY(0);
}
.slide-left-in {
transform: translateX(-100%);
}
.slide-left-active {
transform: translateX(0);
opacity: 1;
}
.zoom-in-in {
transform: scale(0.8);
}
.zoom-out-active {
transform: scale(1);
}
.zoom-out-in {
transform: scale(1.2);
}
</style>

22
index.html

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<script>
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
CSS.supports('top: constant(a)'))
document.write(
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
</script>
<title></title>
<!--preload-links-->
<!--app-context-->
</head>
<body>
<div id="app">
<!--app-html-->
</div>
<script type="module" src="/main.js"></script>
</body>
</html>

99
main.js

@ -0,0 +1,99 @@
import App from './App'
import toast from './common/Toast.js'
import wxAuthLogin from './common/wxAuthLogin.js'
import wxSilentLogin from './common/wxSilentLogin.js'
import {
isEmpty
} from './common/TextUtils.js'
import {
write,
read,
write2,
read2
} from './common/PreferenceHelper.js'
import {
writeGameCahce,
clearGameCache,
readGameCahce,
hasCache
} from './common/GameCache.js'
import {
http,
httpOtherUrl,
httpCookie,
baseImgURL,
upload
} from './common/Http.js'
import px2upx from './common/PxToRpxOrPxToUpx.js'
import {
timeText,
timeFormat,
currentMillions,
getWeekStr,
getTodayEndTime
} from "./common/Time.js"
import {
back,
setResult,
onActivityResult
} from "./common/Back.js"
import getDistance from 'common/Distance.js'
import chooseUpload from 'common/ChooseUpload.js'
import {
putWEBExtra,
getWEBExtra
} from './common/intent.js'
Vue.prototype.WxSilentLogin = wxSilentLogin
Vue.prototype.WxAuthLogin = wxAuthLogin
Vue.prototype.Toast = toast
Vue.prototype.Back = back
Vue.prototype.WritePreference = write
Vue.prototype.WritePreference2 = write2
Vue.prototype.ReadPreference = read
Vue.prototype.ReadPreference2 = read2
Vue.prototype.HTTP = http
Vue.prototype.HttpOtherUrl = httpOtherUrl
Vue.prototype.Upload = upload
Vue.prototype.HttpCookie = httpCookie
Vue.prototype.IsEmpty = isEmpty
Vue.prototype.Px2Upx = px2upx
Vue.prototype.TimeText = timeText
Vue.prototype.TimeFormat = timeFormat
Vue.prototype.GetWeekStr = getWeekStr
Vue.prototype.CurrentMillions = currentMillions
Vue.prototype.GetTodayEndTime = getTodayEndTime
Vue.prototype.Back = back
Vue.prototype.SetResult = setResult
Vue.prototype.OnActivityResult = onActivityResult
Vue.prototype.WriteGameCahce = writeGameCahce
Vue.prototype.ClearGameCache = clearGameCache
Vue.prototype.ReadGameCahce = readGameCahce
Vue.prototype.HasCache = hasCache
Vue.prototype.getDistance = getDistance
Vue.prototype.chooseUpload = chooseUpload
Vue.prototype.putWEBExtra = putWEBExtra
Vue.prototype.getWEBExtra = getWEBExtra
// #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

68
manifest.json

@ -0,0 +1,68 @@
{
"name" : "signUpTool",
"appid" : "__UNI__8EDFE3C",
"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" : [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
]
},
/* ios */
"ios" : {},
/* SDK */
"sdkConfigs" : {}
}
},
/* */
"quickapp" : {},
/* */
"mp-weixin" : {
"appid" : "wxf869f371c1d0610c",
"setting" : {
"urlCheck" : true,
"postcss" : true,
"minified" : false,
"es6" : true
},
"usingComponents" : true,
"permission" : {
"scope.userLocation" : {
"desc" : "获取当前所在城市"
}
},
"lazyCodeLoading" : "requiredComponents"
}
}

255
pages.json

@ -0,0 +1,255 @@
{
"pages": [ //pageshttps://uniapp.dcloud.io/collocation/pages
{
"path": "pages/publish/Publish",
"style": {}
},
{
"path": "pages/publish/EnrollCondition",
"style": {}
},
{
"path": "pages/publish/EnrollRequired",
"style": {}
},
{
"path": "pages/find/ActivityList",
"style": {}
},
{
"path": "pages/find/ActivityDetail",
"style": {}
},
{
"path": "pages/find/BaoMingListActivity",
"style": {}
},
{
"path": "pages/find/TeamEnroll",
"style": {}
},
{
"path": "pages/team/MyTeam",
"style": {}
},
{
"path": "pages/test/DetailActivity",
"style": {}
},
{
"path": "pages/team/recruitList",
"style": {}
},
{
"path": "pages/test/test",
"style": {}
},
{
"path": "pages/team/CreateTeam",
"style": {}
},
{
"path": "pages/team/MyTeam2",
"style": {}
},
{
"path": "pages/team/JoinTeam",
"style": {}
},
{
"path": "pages/index/firstActivity",
"style": {}
},
{
"path": "pages/index/sponsorDetail",
"style": {}
},
{
"path": "pages/index/activity",
"style": {}
}, {
"path": "pages/index/addSponsor",
"style": {}
}, {
"path": "pages/index/activityList",
"style": {}
}, {
"path": "pages/index/DetailActivity",
"style": {}
}, {
"path": "pages/index/ArenaDetailActivity",
"style": {}
}, {
"path": "pages/index/BindPhone",
"style": {
}
}, {
"path": "pages/index/UserAuthentication",
"style": {
}
}, {
"path": "pages/index/sponsorList",
"style": {}
}, {
"path": "pages/index/mobileInfoActivity",
"style": {}
}, {
"path": "pages/index/eventActivity",
"style": {}
}, {
"path": "pages/index/explainActivity",
"style": {}
}, {
"path": "pages/index/InputHappyGameLimitActivity",
"style": {}
}, {
"path": "pages/index/InputHappyGameAreaActivity",
"style": {}
}, {
"path": "pages/city/CitySelectActivity",
"style": {}
}, {
"path": "pages/web/WebActivity",
"style": {}
}, {
"path": "pages/setup/upMobile",
"style": {}
}, {
"path": "pages/setup/upMobile2",
"style": {}
}, {
"path": "pages/setup/RetrievePasswordActivity",
"style": {}
}, {
"path": "pages/setup/NewPasswordActivity",
"style": {}
},
{
"path": "pages/me/PersonalCenter",
"style": {}
},
{
"path": "pages/me/AuthLogin",
"style": {}
}, {
"path": "pages/me/BaseInfo",
"style": {}
}, {
"path": "pages/me/MyActivity",
"style": {}
}, {
"path": "pages/me/ModiNickName",
"style": {}
},{
"path": "pages/me/ModiUserNumber",
"style": {}
}, {
"path": "pages/me/MyTeamCreate",
"style": {}
}, {
"path": "pages/me/MemberManage",
"style": {}
}
, {
"path": "pages/me/MyTeam",
"style": {}
}, {
"path": "pages/me/RealInfo",
"style": {}
},{
"path": "pages/me/Setup",
"style": {}
},{
"path": "pages/me/ModiRealName",
"style": {}
},{
"path": "pages/me/ModiMobileInputNew",
"style": {}
},{
"path": "pages/me/ModiPasswordVerify",
"style": {}
},{
"path": "pages/me/ModiPassword",
"style": {}
},
{
"path": "pages/publish/CreateActivity",
"style": {}
},
{
"path": "pages/publish/EditIntroduction",
"style": {}
},
{
"path": "pages/publish/EditDisclaimer",
"style": {}
},
{
"path": "pages/publish/SetLinker",
"style": {}
},
{
"path": "pages/publish/InputEnrollNumbersLimitMoney",
"style": {}
},
{
"path": "pages/team/WaitJoin",
"style": {}
}
],
//
"easycom": {
"autoscan": true,
"custom": {
"fui-(.*)": "@/components/firstui/fui-$1/fui-$1.vue"
}
},
"globalStyle": {
"navigationStyle": "custom",
"navigationBarBackgroundColor": "#2fa1f0"
},
"tabBar": {
"color": "#959595",
"selectedColor": "#41adf8",
"backgroundColor": "#FFFFFF",
"borderStyle": "black",
"list": [{
"pagePath": "pages/find/ActivityList",
"iconPath": "static/img/public/bottom-icon/find_normal.png",
"selectedIconPath": "static/img/public/bottom-icon/find_press.png",
"text": "发现"
}, {
"pagePath": "pages/publish/Publish",
"iconPath": "static/img/public/bottom-icon/add_normal.png",
"selectedIconPath": "static/img/public/bottom-icon/add_press.png",
"text": "发布"
}, {
"pagePath": "pages/me/PersonalCenter",
"iconPath": "static/img/public/bottom-icon/mine_normal.png",
"selectedIconPath": "static/img/public/bottom-icon/mine_press.png",
"text": "我的"
}]
},
"condition" : { //
"current": 0, //(list )
"list": [
{
"name": "", //
"path": "", //
"query": "" //onLoad
}
]
}
}

164
pages/city/CitySelectActivity.vue

@ -0,0 +1,164 @@
<template>
<RefreshView ref="mescrollRef" :hasBack="true" @backClick="backClick" :isInterceptBack="true" text="选择城市" :useDownScroll="false"
:useUpScroll="false" pageBg="#F1F2F5">
<view style="padding-bottom: 200rpx;">
<city-select @cityClick="cityClick" :formatName="formatName" :activeCity="activeCity" :hotCity="hotCity"
:obtainCitys="obtainCitys" :isSearch="true" ref="citys"></city-select>
</view>
</RefreshView>
</template>
<script>
import citys from '@/components/city-select/citys.js'
import citySelect from '@/components/city-select/city-select.vue'
export default {
components: {
citySelect
},
data() {
return {
//
formatName: 'title',
//
activeCity: {
id: 1,
title: '南京市'
},
//
hotCity: [{
id: 0,
title: '南京市'
},
{
id: 1,
title: '南京市'
}
],
//
obtainCitys: [{
id: 0,
title: '南京'
},
{
id: 1,
title: '北京'
},
{
id: 2,
title: '天津'
},
{
id: 3,
title: '东京'
}
],
location: {
city: "",
code: ""
}
}
},
onLoad: function(option) {
console.log(option);
this.location.city = option.city
this.location.code = option.code
//
this.formatName = 'cityName'
//
this.activeCity = {
cityName: '正在定位',
cityCode: 0
}
//
this.hotCity = [{
cityName: '北京',
cityCode: 110000
},
{
cityName: '上海',
cityCode: 310000
}, {
cityName: '广州',
cityCode: 440100
}, {
cityName: '深圳',
cityCode: 440300
}, {
cityName: '杭州',
cityCode: 330100
},
]
//
this.obtainCitys = citys
let _this = this
uni.getLocation({
type: 'gcj02',
success: function(res) {
let url =
"https://restapi.amap.com/v3/geocode/regeo?key=59970402d1c3f7dc1efff17d4dfcff21&location=" +
res.longitude + "," + res.latitude +
"&poitype=&radius=1000&extensions=all&batch=false&roadlevel=0";
_this.HttpOtherUrl({
url: url
}).then((res) => {
let json = JSON.stringify(res);
let info = JSON.parse(json).regeocode.addressComponent;
//
let city = info.city;
//
let code = info.adcode;
//
_this.formatName = 'cityName'
//
_this.activeCity = {
cityName: city,
cityCode: code
}
})
},
fail() {
//
_this.activeCity = {
cityName: '定位失败',
cityCode: 0
}
}
});
},
methods: {
backClick(){
console.log("location:", this.location);
this.SetResult(this.location)
},
cityClick(item) {
let json = JSON.stringify(item);
if (JSON.parse(json).cityCode == 0) {
this.Toast("当前选择无效")
} else {
console.log("city:", JSON.parse(json).cityName);
console.log("code:", JSON.parse(json).cityCode);
this.location.city = JSON.parse(json).cityName,
this.location.code = JSON.parse(json).cityCode
this.SetResult(this.location)
// uni.$emit('location', {
// city: JSON.parse(json).cityName,
// code: JSON.parse(json).cityCode
// });
// this.Back()
}
}
}
}
</script>
<style>
</style>

482
pages/find/ActivityDetail.vue

@ -0,0 +1,482 @@
<template>
<RefreshView ref="mescrollRef" text="活动详情" :useDownScroll="false" :useUpScroll="false"
:useTitleLeftBtn="parameter.userRoleId==1?1:0" titleLeftBtnSource=" 管理" :dropLeftList="page.manageList" @drop="selectManage">
<view class="activity-detail">
<!-- 广告 -->
<view class="image-area">
<swiper indicator-dots="true" autoplay="true">
<swiper-item v-for="(item,index) in page.activityDetails.listCoverImageUrl" :key="index">
<image class="image" :src="item" mode="aspectFill"></image>
</swiper-item>
</swiper>
</view>
<!-- 名称和活动类型 -->
<view class="name-category">
<h1>{{page.activityDetails.name}}</h1>
<text class="category">{{page.activityDetails.sportCategoryName}}</text>
</view>
<!-- 报名截止和整体活动时间 -->
<view class="row">
<!-- <view style="display: flex;flex-direction: row;margin-top: 15rpx;align-items: center;margin-left: 20rpx;margin-right: 30rpx;"> -->
<text class="field">报名截止</text>
<text>{{page.activityDetails.enrollEndTime}}</text>
<text style="color: #E99D42;font-size: 26rpx;flex: 1;text-align: right;">还有1天</text>
</view>
<view class="line-thin"></view>
<view class="row">
<text class="field">整体活动</text>
<view style="display: flex;flex-direction: column;">
<text>{{page.activityDetails.startTime}}</text>
<text class="margin-top20">{{page.activityDetails.endTime}}</text>
</view>
</view>
<!-- 活动项目 -->
<view v-for="(item,index) in page.activityDetails.listActivityItem" :key="index" >
<view class="item-area">
<!-- 活动项目名称类型金额 -->
<view class="item-name-mold-money">
<h2>{{item.name}}</h2>
<text class="mold-money">{{item.enrollMoldName}}/{{item.enrollMoney==0?"免费":item.enrollMoney+'元'}}</text>
</view>
<!-- 活动项目介绍 -->
<view class="item-introduction">
<text>{{item.introduction}}</text>
</view>
<view class="line-thin"></view>
<!-- 日期地点 -->
<view class="row">
<text class="date">{{item.startTime}}</text>
<text class="address">{{item.address}}</text>
<image v-if="!showAddress(item.gymnasiumSid)" style="width: 30rpx;height: 30rpx;" src="../../static/img/public/more.png" @click="gymnasiumName(item.gymnasiumSid)"></image>
</view>
<view class="line-thin"></view>
<view class="row">
<text class="field">报名数</text>
<text class="margin-left20 ">{{item.enrollNumbers==0?"暂无":item.enrollNumbers+'人'}}/{{item.enrollNumbersLimit==0?"不限":item.enrollNumbersLimit+'人'}}</text>
<view v-if="item.listUserHeadImageUrl.length>0" style="display:flex;flex-direction: row; margin-left:10rpx;" @click="userList(item.sid)">
<view v-for="(url,i) in item.listUserHeadImageUrl " :key="i">
<view
style="display:flex; width:100%;margin-left:2rpx;margin-right:2rpx;">
<image style="border-radius: 50%; width: 50rpx;height: 50rpx;" :src="url" mode="aspectFill"></image>
</view>
</view>
</view>
<image class="more" src="../../static/img/public/more.png" ></image>
</view>
</view>
</view>
<view class="line-thin"></view>
<!-- 活动介绍 -->
<view class="row">
<text class="field">活动介绍</text>
<view class="line"></view>
</view>
<text class="introduction">{{page.activityDetails.introduction}}</text>
<!-- 奖品奖项 -->
<view class="row">
<text class="field">奖品奖项</text>
<view class="line"></view>
</view>
<text class="introduction">{{page.activityDetails.notes}}</text>
<!-- 特别鸣谢 -->
<view class="row">
<text class="field">特别鸣谢</text>
<view class="line"></view>
</view>
<view
style="display: flex;margin-left: 40rpx;margin-right: 40rpx;flex-direction: column; margin-top: 30rpx;">
<view v-for="(item,pos) in page.activityDetails.listSpecialThanks " :key="pos">
<view
style="display: flex; width: 100%; padding-top: 10rpx;padding-bottom: 10rpx;justify-content: center;">
<text style="color: #898989;font: size 28rpx;flex: 1;">{{item.name}}</text>
</view>
</view>
</view>
<view class="line-thin"></view>
<!-- 主办方 -->
<view class="row">
<text class="field">主办方</text>
<text style="color: #898989;font-size: 25rpx;">{{page.activityDetails.organizer}}</text>
</view>
<view class="line-thin"></view>
<!-- 联系人 -->
<view class="row">
<text class="field">联系人</text>
<view style="display: flex;flex-direction: row;">
<text style="color: #898989;font-size: 25rpx;">{{page.activityDetails.linkerName}}</text>
<view style="width: 30rpx;"></view>
<text style="color: #898989; font-size: 25rpx;">{{page.activityDetails.linkerPhone}}</text>
</view>
</view>
<view class="line-thin"></view>
<view class="row">
<text class="field">报名必填</text>
<text>{{page.enrollRequiredName}}</text>
<!-- <image class = "more" src="../../static/img/public/more.png" ></image> -->
</view>
<view class="line-wide"></view>
<!-- 报名条款及按钮 -->
<view class="agreeMent">
<checkbox-group @change="checkboxChange">
<checkbox style="transform:scale(0.7)" :checked="checked1"></checkbox>
</checkbox-group>
<text class="text2">我已阅读并同意</text>
<text style="color: #007AFF;">参赛须知</text>
<text style="display: flex;text-align: center; margin-left: 20rpx; padding: 5rpx 10rpx;background: #0081D5;
color: #FFFFFF; font-size: 28rpx;" @click="enroll()"> 我要报名 </text>
</view>
<view style="height: 300rpx;"></view>
</view>
</RefreshView>
</template>
<script>
export default {
data() {
return {
parameter:{
activitySid:"",
userRoleId:0,
userRoleName:"游客",
passRequired : false,
noPassMsg : ""
},
isCreate:0,
checked1: true,
page:{
activityDetails:{},
manageList: [{
'name': '修改活动',
'src': '../../static/game-icon/renyuanguanli.png',
'id': 0
}, {
'name': '删除活动',
'src': '../../static/game-icon/shanchu.png',
'id': 1
}],
enrollRequiredName:""
},
}
},
onLoad: function(option) {
//
this.parameter.activitySid = option.activitySid
//
let _this = this
if (getApp().globalData.isLogin){
_this.parameter.userRoleId =1
let sysUserSid = getApp().globalData.sysUserSid;
let activitySid = _this.parameter.activitySid;
_this.HTTP({
url: 'aos/v1/aosUser/getActivityRole',
method: 'GET',
data: {
'sysUserSid': sysUserSid,
'activitySid':activitySid
},
paramsType: "FORM",
loading: true
}).then((res) => {
if (200 == res.code) {
_this.parameter.userRoleId = res.data.userRoleId
_this.parameter.userRoleName = res.data.userRoleName
// } else {
// _this.Toast(res.msg)
}
});
}else{
_this.parameter.userRoleId = 0
}
},
onShow() {
let _this = this
let activitySid = _this.parameter.activitySid;
_this.HTTP({
url: 'aos/v1/activity/getActivityDetails/' + activitySid,
method: 'GET',
paramsType: "FORM",
loading: true
}).then((res) => {
if (200 == res.code) {
_this.page.activityDetails = res.data
_this.setEnrollRequiredName()
} else {
_this.Toast(res.msg)
}
});
},
methods: {
setEnrollRequiredName(){
let enrollRequiredName = ""
if (this.page.activityDetails.enrollRequired.onRealName == 1){
enrollRequiredName += "姓名"
}
if (this.page.activityDetails.enrollRequired.onSex == 1){
if (this.page.activityDetails.enrollRequired.onRealName == 1){
enrollRequiredName += " / "
}
enrollRequiredName += "性别"
}
if (this.page.activityDetails.enrollRequired.onBirthday == 1){
if (this.page.activityDetails.enrollRequired.onSex == 1 || this.page.activityDetails.enrollRequired.onRealName == 1){
enrollRequiredName += " / "
}
enrollRequiredName += "生日"
}
if (this.page.activityDetails.enrollRequired.onAdCode == 1){
if (this.page.activityDetails.enrollRequired.onSex == 1 || this.page.activityDetails.enrollRequired.onRealName == 1 || this.page.activityDetails.enrollRequired.onBirthday == 1){
enrollRequiredName += " / "
}
enrollRequiredName += "地区"
}
this.page.enrollRequiredName = enrollRequiredName
},
selectManage(index, isLeft, selectData) {
let _this = this;
let id = selectData.id
if (id == 0) {
//
uni.navigateTo({
url: '../publish/CreateActivity?sid=' + _this.parameter.activitySid
})
} else if (id == 1) {
//
uni.showModal({
title: '温馨提示',
content: '确定删除此赛事?',
success(res) {
if (res.confirm) {
_this.HTTP({
url: 'aos/v1/activity/delActivity/' + _this.parameter.activitySid,
method: 'DELETE',
paramsType: "FORM",
loading: true
}).then((res) => {
if (200 == res.code) {
uni.navigateBack({
delta: 1
});
} else {
_this.Toast(res.msg)
}
});
}
}
});
} else if (id == -1) {
//
}
},
toCreateActivity(){
let activitySid = this.parameter.activitySid
uni.navigateTo({
url: '../publish/CreateActivity?sid=' + activitySid
});
},
showAddress(s) {
console.log("..." + s);
return this.IsEmpty(s)
},
checkboxChange(e) {
this.checked1 = !this.checked1
console.log(this.checked1)
},
gymnasiumName(e) {
uni.navigateTo({
url: "ArenaDetailActivity?sid=" + e
})
console.log("gymnasiumName>>", e)
},
userList(sid) {
console.log("userList>>", sid)
uni.navigateTo({
url: "BaoMingListActivity?activitySid=" + this.parameter.activitySid + "&activityItemSid=" + sid
})
},
enroll() {
let _this = this;
//
if (!this.checked1) {
this.Toast("请认真阅读参赛须知,并勾选。")
return
}
//
let listEnrollMenu = []
let listActivityItem = this.page.activityDetails.listActivityItem
for (var i = 0; i < listActivityItem.length; i++) {
let enrollMenu = listActivityItem[i].name
let enrollMold = listActivityItem[i].enrollMold
if ( enrollMold = 1){
enrollMenu += "(" + listActivityItem[i].enrollMoldName +")"
}
listEnrollMenu.push(enrollMenu)
}
//
uni.showActionSheet({
itemList: listEnrollMenu,
success: function(res) {
console.log("itemList==" + listEnrollMenu)
console.log("res==" + JSON.stringify(res))
_this.selectMenu(res.tapIndex, listEnrollMenu)
},
fail(e) {
console.log("reeees==" + JSON.stringify(e))
}
});
},
verifyEnrollRequired(){
},
selectMenu(id, info) {
//
if (!getApp().globalData.isLogin){
uni.navigateTo({
url: "../me/AuthLogin"
})
}
let _this = this
let activitySid = _this.page.activityDetails.sid
let sysUserSid = getApp().globalData.sysUserSid
// console.log("=============",JSON.stringify(_this.page.activityDetails))
//
let onRealName = _this.page.activityDetails.enrollRequired.onRealName
let onSex = _this.page.activityDetails.enrollRequired.onSex
let onBirthday = _this.page.activityDetails.enrollRequired.onBirthday
let onAdCode = _this.page.activityDetails.enrollRequired.onAdCode
if (onRealName == 0 || onSex == 0 || onBirthday == 0 || onAdCode == 0){
_this.HTTP({
url: 'aos/v1/activity/verifyEnrollRequired',
method: 'GET',
paramsType: "FORM",
data: {
'activitySid': activitySid,
'sysUserSid': sysUserSid
},
loading: true
}).then((res) => {
if ( res.code == "200") {
_this.parameter.passRequired = true
}else{
// _this.parameter.noPassMsg = res.msg
uni.showModal({
title: '报名需要补充如下个人信息',
content: res.msg,
success(res) {
if (res.confirm) {
uni.navigateTo({
url: '../me/RealInfo?sysUserSid=' + getApp().globalData.sysUserSid
})
}else
{
return
}
}
})
}
});
}
//
console.log(id)
console.log(info)
let activityItemSid = _this.page.activityDetails.listActivityItem[id].sid // SId
let enrollMold = _this.page.activityDetails.listActivityItem[id].enrollMold // (01)
console.log("用户SID",getApp().globalData.sysUserSid)
//
if (enrollMold == 0){
//
let passCondition = true
let sex = _this.page.activityDetails.listActivityItem[id].activityItemCondition.sex
let minAge = _this.page.activityDetails.listActivityItem[id].activityItemCondition.minAge
let maxAge = _this.page.activityDetails.listActivityItem[id].activityItemCondition.maxAge
if (sex > 0 || minAge > 0 || maxAge > 0){
_this.HTTP({
url: 'aos/v1/activityItem/verifyPersonalEnrollCondition',
method: 'GET',
paramsType: "FORM",
data: {
'activityItemSid': activitySid,
'sysUserSid': sysUserSid
},
loading: true
}).then((res) => {
if ( res.code != "200") {
return
uni.showToast({
title: '报名条件不符',
content:res.msg,
duration:3000
})
}
})
}
this.HTTP({
url: 'aos/v1/activityItem/personalEnroll',
method: 'POST',
data: {
'activityItemSid': activityItemSid,
'sysUserSid': getApp().globalData.sysUserSid,
'paymentMemberSid': getApp().globalData.sysUserSid,
},
paramsType: "JSON",
loading: true
}).then((res) => {
if (res.code == 200) {
this.Toast("报名成功!")
} else {
this.Toast(res.msg)
}
}, (err) => {
//
_this.Toast("出错了:" + err.data.errmsg)
})
}
return
//
if (enrollMold == 1){
//
uni.navigateTo({
url: "../find/TeamEnroll?activityItemSid="+activityItemSid
})
}
},
}
}
</script>
<style lang="scss">
@import url("../../static/columns.css");
.items {
display: flex;
flex-direction: column;
}
.agreeMent {
display: flex;
flex-direction: row;
align-items: center;
margin-top: 50rpx;
margin-left: 30rpx;
.text2 {
color: #666666;
font-size: 30rpx;
}
}
.content{
display: flex;
flex-direction: column;
background: #FFFFFF;
width: 100%;
height: 100%;
}
</style>

255
pages/find/ActivityList.vue

@ -0,0 +1,255 @@
<template>
<RefreshView id="mescrollRef" ref="mescrollRef" :pageBg="F1F2F5" @refresh="refresh" text="活动列表" :useDownScroll="true" :useUpScroll="true">
<sl-filter id="header" ref="slLilter" v-if="menuList.length!=0" :color="fd6d2a" themeColor="#000000" :menuList.sync="menuList" @result="result"></sl-filter>
<view v-for="(item,index) in page.listActivity " :key="index">
<view class="activity-area" @click="clickItem(index)">
<view class="summary">
<view class=".name-category">
<text class="name">{{item.name}}</text>
<text class="category">{{item.sportCategoryName}}</text>
</view>
<text class="enroll">报名截止{{item.enrollEndTime}}</text>
</view>
<view class="image-area">
<image class="image" mode="aspectFill" :src="item.firstCoverImage"></image>
<view class="tips">
<image class="icon" src="../../static/img/public/renqi.png"></image>
<text class="popularity">{{item.popularity}}</text>
<text :class="{'enroll-state enroll-no':item.enrollState ==1||item.enrollState ==3,'enroll-state enroll-yes':item.enrollState==2}" >{{item.enrollStateName}}</text>
</view>
</view>
<view v-for="(info,pos) in item.listActivityItemsArea " :key="pos">
<view class="item-area">
<view class="item-name-mold-money">
<text class="item-name">{{info.name}}</text>
<text class="mold-money">{{info.enrollMoldName}}/{{info.enrollMoney==0?"免费":info.enrollMoney}}</text>
</view>
<view class="enroll-amount-date">
<text class="amount">报名数{{info.enrollNumbers==0?"暂无":info.enrollNumbersLimit+'人'}}{{info.enrollNumbersLimit==0?"不限":info.enrollNumbersLimit+'人'}}</text>
<text class="date">{{info.startTime}}</text>
</view>
</view>
<view v-if="item.listActivityItemsArea.length>pos+1" class="line-thin margin-top20"></view>
</view>
</view>
</view>
</RefreshView>
</template>
<script>
//
import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";
export default {
// 使mixin (main.js)
mixins: [MescrollMixin],
data() {
return {
index: 0,
list: [],
page: {
'sort': 0,
'city': this.ReadPreference("find_city"),
'adCode': this.ReadPreference("find_city_code").slice(0, -2),
state: "",
listActivity:[]
},
menuList: [{
'title': '当前定位城市',
'key': 'type',
'reflexTitle': true,
'defaultSelectedIndex': 0,
detailList: [{
'title': this.ReadPreference("find_city"),
'value': this.ReadPreference("find_city_code").slice(0, -2)
},
{
'title': '选择其他城市',
'value': '0'
}
]
}, {
'title': '默认活动类型',
'key': 'state',
'reflexTitle': true,
'defaultSelectedIndex': 0,
detailList: [{
'title': '全部活动',
'value': ""
}]
}, {
'title': '默认排序',
'key': 'sort',
'reflexTitle': true,
'defaultSelectedIndex': 0,
'detailList': [{
'title': '按发布时间排序',
'value': 0
},
{
'title': '按报名时间排序',
'value': 1
},
{
'title': '按人气从高到低',
'value': 2
}
]
}
]
}
},
onLoad() {
let that = this
that.HTTP({
url: 'aos/v1/activity/getActivityCreatePageParameter',
method: 'GET',
data: {
"adcode": that.page.adCode
},
paramsType: "FORM",
loading: true
}).then((res) => {
console.log("that.menuList[1].detailList", that.menuList[1].detailList);
if (200 == res.code) {
//
let listSportCategoryArea = [];
for (var i = 0; i < res.data.listSportCategoryArea.length; i++) {
listSportCategoryArea.push({
title: res.data.listSportCategoryArea[i].sportCategoryName,
value: res.data.listSportCategoryArea[i].sid
})
}
//
that.menuList[1].detailList = that.menuList[1].detailList.concat(listSportCategoryArea)
this.$refs.slLilter.setTitle(this.menuList)
}
});
},
onShow() {
let backResult = this.OnActivityResult();
if (backResult != undefined) {
if (!this.IsEmpty(backResult.code)) {
console.log("结果2>" + backResult.code)
this.page.adCode = backResult.code
this.page.city = backResult.city
this.menuList[0].detailList[0].title = backResult.city
this.menuList[0].detailList[0].value = backResult.code
console.log("qqq", this.menuList[0].detailList);
this.$refs.slLilter.setTitle(this.menuList)
this.$refs.mescrollRef.resetPageOne();
}
}
},
methods: {
result(val) {
if (!this.IsEmpty(val.sort)) {
this.page.sort = val.sort;
this.list = []
console.log("this.page.sort", this.page.sort);
}
if (!this.IsEmpty(val.type)) {
if (val.type != 0) {
this.page.game = val.type;
this.list = []
} else {
console.log("===>1" + JSON.stringify(this.page))
uni.navigateTo({
url: "../../pages/city/CitySelectActivity?city=" + this.page.city +
"&code=" + this.page.adCode
})
}
}
if (!this.IsEmpty(val.state)) {
this.page.state = val.state;
this.list = []
console.log("this.page.state", this.page.state);
}
this.$refs.mescrollRef.resetPageOne();
},
refresh(page) {
let _this = this
this.HTTP({
url: 'aos/v1/activity/getActivityAreaPagerList',
paramsType: "JSON",
method: "POST",
data: {
current: page.num,
size: 10,
params: {
name: "",
adCode: _this.menuList[0].detailList[0].value,
orderBy: _this.page.sort,
sportCategorySid: _this.page.state
}
},
loading: true
}).then((res) => {
//
_this.$refs.mescrollRef.refreshFinished(res.data.records.length)
if (page.num == 1) {
//
_this.page.listActivity = []
}
//
_this.page.listActivity = _this.page.listActivity.concat(res.data.records)
}).catch(function(err) {
_this.$refs.mescrollRef.refreshError()
});
},
clickItem(index) {
let activitySid = this.page.listActivity[index].sid
console.log(activitySid)
uni.navigateTo({
url: '../find/ActivityDetail?activitySid=' + activitySid
});
},
}
}
</script>
<style lang="scss">
@import url("../../static/columns.css");
.top {
display: flex;
flex-direction: row;
height: 100rpx;
background: #FFFFFF;
opacity: 0.8;
align-items: center;
border-radius: 20rpx;
margin-top: 20rpx;
margin-left: 30rpx;
margin-right: 30rpx;
.unselected {
text-align: center;
font-size: 28rpx;
font-family: Adobe Heiti Std;
font-weight: normal;
color: #191919;
line-height: 40rpx;
}
.selected {
text-align: center;
font-size: 28rpx;
font-family: Adobe Heiti Std;
font-weight: normal;
color: #2CAB69;
line-height: 40rpx;
border-bottom: 2rpx #2CAB69 solid;
padding-bottom: 10rpx;
}
}
</style>

136
pages/find/BaoMingListActivity.vue

@ -0,0 +1,136 @@
<template>
<view>
<RefreshView ref="mescrollRef" @refresh="refresh" :hasBack="true" text="报名列表">
<view v-for="(item,index) in data.list" :key='index' class="list-item-layout">
<image :src="item.headImage" class="list-item-img" mode="aspectFill">
</image>
<view class="list-item-right">
<view class="list-item-name">{{item.userNickName}}</view>
<view class="list-item-content">报名时间{{item.enrollTime}}</view>
</view>
</view>
</RefreshView>
</view>
</template>
<script>
//
import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";
export default {
// 使mixin (main.js)
mixins: [MescrollMixin],
data() {
return {
page: {
activitySid: '',
activityItemSid: '',
},
data: {
list: []
}
}
},
onLoad(options) {
//
this.page.activitySid = options.activitySid
this.page.activityItemSid = options.activityItemSid
},
methods: {
refresh(page) {
let _this = this;
this.HTTP({
url: 'aos/v1/participantRelation/getPageListPersonalParticipant',
paramsType: "JSON",
method: "POST",
data: {
current: page.num,
size: 10,
params: {
activitySid: this.page.activitySid,
activityItemSid: this.page.activityItemSid ,
}
},
loading: true
}).then((res) => {
//
_this.$refs.mescrollRef.refreshFinished(res.data.records.length)
if (page.num == 1) {
//
_this.data.list = []
}
//
_this.data.list = _this.data.list.concat(res.data.records)
}).catch(function(err) {
console.log("11111");
_this.$refs.mescrollRef.refreshError()
})
},
}
}
</script>
<style lang="scss">
.list-item-layout {
display: flex;
width: 100%;
flex-direction: row;
box-sizing: border-box;
padding-top: 20rpx;
padding-bottom: 10rpx;
margin-left: 33rpx;
margin-right: 33rpx;
border-bottom: 0.1px #F1F1F1 solid;
.list-item-img {
width: 140rpx;
height: 140rpx;
margin-right: 25rpx;
flex-shrink: 0;
}
.list-item-right {
flex: 1;
margin-top: 20rpx;
display: flex;
flex-direction: column;
height: 140rpx;
.list-item-name {
width: 100%;
flex: 1;
color: #333333;
font-size: 27rpx;
}
.list-item-content {
width: 100%;
flex: 1;
color: #999999;
font-size: 24rpx;
}
.list-item-time {
width: 100%;
color: #999999;
font-size: 24rpx;
flex: 1;
}
}
}
</style>

217
pages/find/TeamEnroll.vue

@ -0,0 +1,217 @@
<template>
<RefreshView ref="mescrollRef" :hasBack="true" text="队伍报名" :useDownScroll="false" :useUpScroll="false">
<view class="top">
<text class="top_lift">选中({{page.selectedMemberAmount}})</text>
<text class="top_right" @click="enroll()" >比赛报名</text>
</view>
<view class="line"></view>
<view>
<view v-for="(item,index) in page.listTeamMemberArea " :key="index">
<view class="item">
<view class="line" style="height: 1px;"></view>
<view class="item_content">
<checkbox-group class="item_check" @change="checkboxChange($event, index)">
<checkbox :checked="item.checked"></checkbox>
</checkbox-group>
<image class="item_img" :src="item.headImage" mode="aspectFill"></image>
<text class="item_text">{{item.realName}}</text>
</view>
</view>
</view>
</view>
</RefreshView>
</template>
<script>
export default {
data() {
return {
data:{
activityItemSid:"", // Sid
teamSid:"", // Sid
listTeamMemberSid:[], // Sid
paymentMemberSid:"" // Sid
},
page:{
type: 1, // 1
listTeamMemberArea:[], //
selectedMemberAmount:0 //
}
}
},
onLoad:function(options) {
// Sid
this.data.activityItemSid = options.activityItemSid
//
if (!getApp().globalData.isLogin){
//
}
let sysUserSid = getApp().globalData.sysUserSid
let _teamSid =""
//
let _this = this
_this.HTTP({
url: 'aos/v1/aosUser/getMyTeamAreaPagerList',
paramsType: "JSON",
method: "POST",
data: {
current: 1,
size: 10,
params: {
sysUserSid: sysUserSid,
type: 1
}
},
loading: true
})
.then((res) => {
//
if (!res.success){
return
}
let listTeamArea = res.data.records
if (listTeamArea.length > 1 ){
console.log('队伍数大于1支跳转:', listTeamArea)
}
// Sid
_this.data.teamSid = listTeamArea[0].sid
console.log('最新的队伍Sid', _this.data.teamSid)
//
_this.HTTP({
url: 'aos/v1/aosUser/getTeamArea',
paramsType: "FORM",
method: "GET",
data: {
teamSid: _this.data.teamSid
},
toast: true,
loading: true
})
.then((res) => {
console.log('res', res)
_this.page.listTeamMemberArea = res.data.listTeamMemberArea
});
}, (err) => {
//
_this.Toast("出错了:" + err.data.errmsg)
})
},
methods: {
enroll() {
//
let _this = this
console.log('=====', _this)
let _activityItemSid = this.data.activityItemSid
let _teamSid = _this.data.teamSid
let _listTeamMemberSid = _this.data.listTeamMemberSid
_this.HTTP({
url: 'aos/v1/activityItem/teamEnroll',
paramsType: "JSON",
method: "POST",
data: {
'activityItemSid':_activityItemSid,
'teamSid': _teamSid,
'listTeamMemberSid': _listTeamMemberSid
},
loading: true
})
.then((res) => {
uni.navigateBack({
delta: 1
})
});
},
checkboxChange(e, index) {
//
this.page.listTeamMemberArea[index].checked = !this.page.listTeamMemberArea[index].checked
console.log("listTeamMemberArea" + JSON.stringify(this.page.listTeamMemberArea[0]))
// Sid
if (this.page.listTeamMemberArea[index].checked) {
this.data.listTeamMemberSid.push(this.page.listTeamMemberArea[index].sid)
}
else {
// Sid
let sid = this.page.listTeamMemberArea[index].sid
let pos = this.data.listTeamMemberSid.findIndex((x) => {
return x == sid
})
this.data.listTeamMemberSid.splice(pos, 1)
}
this.page.selectedMemberAmount = this.data.listTeamMemberSid.length
}
}
}
</script>
<style lang="scss">
@import url("../../static/master.css");
.line {
height: 10rpx;
background: #eee;
width: 100%;
}
.top {
display: flex;
background: #fff;
flex-direction: row;
width: 100%;
padding-top: 26rpx;
padding-bottom: 26rpx;
.top_lift {
margin-left: 30rpx;
font-weight: 550;
font-family: sans-serif;
flex: 1;
color: #101010;
font-size: 36rpx;
}
.top_right {
margin-right: 30rpx;
background-color: #F4CE98;
color: #fff;
font-size: 24rpx;
padding: 10rpx 20rpx;
text-align: right;
}
}
.item {
display: flex;
flex-direction: column;
.item_content {
display: flex;
align-items: center;
background-color: #fff;
padding: 20rpx 32rpx;
flex-direction: row;
.item_check {
width: 32rpx;
height: 32rpx;
}
.item_img {
margin-left: 40rpx;
width: 80rpx;
height: 80rpx;
border-radius: 10rpx;
}
.item_text {
font-size: 28rpx;
color: #101010;
margin-left: 40rpx;
}
}
}
</style>

364
pages/home/AddFragment.vue

@ -0,0 +1,364 @@
<template>
<view>
<RefreshView ref="mescrollRef" text="首页" :useDownScroll="false" :useUpScroll="false" pageBg="#F1F2F5">
<view style="display: flex;flex-direction: column;align-items: center;width: 100vw;">
<view style="display: flex;flex-direction: column;width: 100vw;align-items: center;background-color: #2fa1f0;padding-top: 80rpx;
border-bottom-right-radius: 30rpx;border-bottom-left-radius: 30rpx;">
<view style="width: 100vw;text-align: center;font-size: 40px;font-weight: 700;color: #fff;" >
<!-- <view style="left: 100px;
top: 79px;
font-weight: 700;
width: 160px;
height: 58px;
color: rgba(255, 255, 255, 100);
font-size: 40px;
text-align: center;
font-family: SourceHanSansSC-bold;">-->
体育活动
</view>
<view
style="width: 100vw;text-align: center;font-size: 22px;font-weight: 600;color: #fff;margin-top: 10rpx;">
<!-- <view style="left: 114px;
top: 139px;
font-weight: 600;
width: 132px;
height: 32px;
color: rgba(255, 255, 255, 100);
font-size: 22px;
text-align: center;
font-family: SourceHanSansSC-bold;"> -->
组织报名工具
</view>
<text style="margin-top: 80rpx;color:#fff;font-size: 16px;">一分钟创建 轻松组织活动</text>
<view
style="display: flex;flex-direction: row;margin-top: 40rpx;justify-content: center;margin-bottom: 100rpx;align-items: center;">
<image src="../../static/home-icon/visits.png" style="width: 35rpx;height: 35rpx;"></image>
<text style="margin-left: 20rpx;color: #fff;font-size: 12px;">访问量{{visits}}</text>
</view>
</view>
</view>
<view style="display: flex;flex-direction: row;margin-top: 60rpx;margin-left: 30rpx;align-items:baseline">
<text style="font-weight: 400; color: #E99D42 ;font-size: 36rpx;">发布活动类别</text>
<text style="margin-left: 20rpx;font-weight: 400; color: #E99D42;font-size: 28rpx;">(点击体育活动标签发布)</text>
</view>
<view style="display: flex; margin-left: 10rpx;margin-right: 30rpx;margin-top: 40rpx;
justify-content: center;">
<grid>
<view style="display: flex; margin-bottom: 10rpx; margin-left: 10rpx;margin-right: 10rpx;"
v-for="(item,index) in SportCategoryList">
<text class="labelLayout" @click="onClick(index)">{{item.sportCategoryName}}</text>
</view>
</grid>
</view>
<view class="line" style=" margin-top: 100rpx;margin-left: 30rpx;margin-right: 30rpx;"></view>
<view
style="display: flex;flex-direction: column;margin-top: 40rpx;margin-bottom: 20rpx;margin-left: 30rpx;margin-right: 30rpx;">
<text style="font-size: 32rpx;color: #666666;font-weight: 600">友情提示</text>
<text style="font-size: 30rpx;color: #666666;margin-top: 40rpx;font-weight: 500;">{{notices}}</text>
</view>
<view class="line" style=" margin-left: 30rpx;margin-right: 30rpx;margin-top: 40rpx;"></view>
<view style="margin-top: 30rpx;margin-left: 30rpx;margin-right: 30rpx;">
<text style="font-size: 40rpx;color: #4095E5;font-weight: 600" @click="jump()">{{page.city}}</text>
<text
style="font-size: 32rpx;color: #666666;font-weight: 500;margin-left: 20rpx;">共有{{activityAmount}}场活动</text>
</view>
<view style="height: 150px;"></view>
</RefreshView>
</view>
</template>
<script>
//
import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";
export default {
// 使mixin (main.js)
mixins: [MescrollMixin],
data() {
return {
index: 0,
notices: "\u3000\u3000您注册的帐号可以在宇运动主体下的子平台一号通行,同时您发布的活动将会按类别发布到各子平台。(点击查看宇运动子平台)",
list: [],
SportCategoryList: [],
page: {
city: "正在获取...",
code: ""
},
visits: "...",
activityAmount: "..."
}
},
onLoad(options) {
this.HTTP({
url: 'aos/v1/activityManagement/add1PointOnVisits',
method: 'GET',
data: {},
paramsType: "FORM",
loading: false
}).then((res) => {
});
let that = this
let find_city_code = this.ReadPreference("find_city_code")
let find_city = this.ReadPreference("find_city")
if (this.IsEmpty(find_city_code)) {
//
find_city_code = 0;
//
let _this = this;
//
uni.getLocation({
type: 'gcj02',
success: function(res) {
let locat = res
console.log("latitude", res.longitude);
console.log("longitude", res.latitude);
//
_this.location = {
latitude: locat.latitude,
longitude: locat.longitude
}
let url =
"https://restapi.amap.com/v3/geocode/regeo?key=59970402d1c3f7dc1efff17d4dfcff21&location=" +
res.longitude + "," + res.latitude +
"&poitype=&radius=1000&extensions=all&batch=false&roadlevel=0";
_this.HttpOtherUrl({
url: url
}).then((res) => {
let json = JSON.stringify(res);
let info = JSON.parse(json).regeocode.addressComponent;
//
let city = info.city;
//
let code = info.adcode;
//
if (code > 0) {
console.log("find_city1", city);
console.log("find_city_code1", code);
//
_this.WritePreference("find_city", city)
_this.WritePreference("find_city_code", code)
_this.page.code = code
_this.page.city = city
_this.getCode()
} else {
console.log("find_city2", city);
console.log("find_city_code2", code);
//
_this.WritePreference("find_city", "石家庄")
_this.WritePreference("find_city_code", "130104")
_this.page.code = "130104"
_this.page.city = "石家庄"
_this.getCode()
}
}, (err) => {
console.log("find_city3", "石家庄");
console.log("find_city_code3", "130104");
//
_this.WritePreference("find_city", "石家庄")
_this.WritePreference("find_city_code", "130104")
_this.page.code = "130104"
_this.page.city = "石家庄"
_this.getCode()
})
},
fail(err) {
console.log("find_city4", "石家庄");
console.log("find_city_code4", "130104");
//
_this.WritePreference("find_city", "石家庄")
_this.WritePreference("find_city_code", "130104")
_this.page.code = "130104"
_this.page.city = "石家庄"
_this.getCode()
}
});
} else {
this.page.code = find_city_code
this.page.city = find_city
this.getCode()
}
},
onShow() {
let find_city_code = this.ReadPreference("find_city_code")
let find_city = this.ReadPreference("find_city")
if (!this.IsEmpty(find_city_code)) {
this.getCode()
}
},
methods: {
// wwww(){
// uni.navigateTo({
// url: "../team/CreateTeam"
// })
// },
getCode() {
let that = this
that.HTTP({
url: 'aos/v1/activityManagement/getActivityCreatePageParameter',
method: 'GET',
data: {
"adcode": this.page.code
},
paramsType: "FORM",
loading: true
}).then((res) => {
that.SportCategoryList = res.data.listSportCategoryArea
that.activityAmount = res.data.activityAmount
that.visits = res.data.visits
});
},
onClick(index) {
uni.navigateTo({
url: "../index/activity?sportCategoryName=" + this.SportCategoryList[index].sportCategoryName +
"&sportCategorySid=" + this.SportCategoryList[index].sid + "&isCreate=0&sid="
})
},
jump() {
console.log('this.page.code', this.page.code)
uni.switchTab({
url: "../home/FindFragment"
})
}
}
}
</script>
<style lang="scss">
.line {
height: 5rpx;
background-color: #eee
}
.btn {
display: flex;
width: 80%;
height: 80rpx;
flex-direction: column;
background-color: $uni-base-color;
margin-left: auto;
margin-right: auto;
align-items: center;
justify-content: center;
border-radius: 10rpx;
}
.nav-bar {
height: 92rpx;
width: 100%;
display: flex;
justify-content: space-between;
.bar-text {
font-size: 32rpx;
height: 100%;
line-height: 92rpx;
padding-left: 28rpx;
}
.bar-img {
width: 90rpx;
height: 92rpx;
padding-right: 28rpx;
}
}
.top {
display: flex;
flex-direction: row;
height: 100rpx;
background: #FFFFFF;
opacity: 0.8;
align-items: center;
border-radius: 20rpx;
margin-top: 20rpx;
margin-left: 30rpx;
margin-right: 30rpx;
.unselected {
text-align: center;
font-size: 28rpx;
font-family: Adobe Heiti Std;
font-weight: normal;
color: #191919;
line-height: 40rpx;
}
.selected {
text-align: center;
font-size: 28rpx;
font-family: Adobe Heiti Std;
font-weight: normal;
color: #2CAB69;
line-height: 40rpx;
border-bottom: 2rpx #2CAB69 solid;
padding-bottom: 10rpx;
}
}
.btn1 {
background: #BBBBBB;
}
.btn2 {
background: #0081D5;
}
.labelLayout {
flex: 1;
width: 100%;
margin-left: 20rpx;
margin-right: 20rpx;
background-color: #F2BF5C;
color: #FFFFFF;
padding-top: 8rpx;
padding-bottom: 8rpx;
padding-left: 20rpx;
padding-right: 20rpx;
justify-content: center;
text-align: center;
font-size: 30rpx;
}
</style>

326
pages/home/FindFragment.vue

@ -0,0 +1,326 @@
<template>
<view style="background: #F1F2F5;">
<RefreshView id="mescrollRef" ref="mescrollRef" :pageBg="F1F2F5" @refresh="refresh" text="活动列表"
:useDownScroll="true" :useUpScroll="true">
<sl-filter id="header" ref="slLilter" v-if="menuList.length!=0" :color="fd6d2a" themeColor="#000000"
:menuList.sync="menuList" @result="result"></sl-filter>
<view>
<view v-for="(item,index) in list " :key="index">
<view
style="display: flex;flex-direction: column;margin-bottom: 30rpx; padding: 30rpx;background: #FFFFFF;"
@click="clickItem(index)">
<view style="display: flex;flex-direction: column;">
<view style="display: flex; flex-direction: row;">
<text style="color: #101010; font-size: 32rpx; display:-webkit-box;-webkit-line-clamp:1;
overflow:hidden;text-overflow:ellipsis;-webkit-box-orient:vertical;
word-break:break-all;flex: 1;">{{item.name}}</text>
<text
style="color: #fff; font-size: 24rpx; background-color: #F2BF5C; padding: 5rpx 10rpx;">{{item.sportCategoryName}}</text>
</view>
<text
style="color: #898989 ; font-size: 28rpx; margin-top: 10rpx;">报名截止{{item.enrollEndTime}}</text>
</view>
<view style="width: 100%;height: 300rpx; margin-top: 20rpx;margin-bottom: 20rpx;">
<image style="width: 100%;height: 100%; border-radius: 30rpx;" mode="aspectFill"
:src="item.firstCoverImage"></image>
<view style="display: flex; height: 50rpx;width: 100%; margin-top: -80rpx; ">
<view style=" display: flex;align-items: center;width: 100%;">
<image style="width: 28px;height: 48rpx;margin-left: 30rpx;"
src="../../static/renqi.png"></image>
<text style="color: #fff; margin-left: 15rpx;flex: 1;">{{item.popularity}}</text>
<text
:class="{'btn1':item.enrollState ==1||item.enrollState ==3,'btn2':item.enrollState==2}"
style="display: flex;text-align: center; padding: 8rpx 15rpx;
color: #FFFFFF; font-size: 28rpx; margin-right:30rpx; border-radius: 10rpx;">{{item.enrollStateName}}</text>
</view>
</view>
</view>
<view v-for="(info,pos) in item.listActivityItemsArea " :key="pos">
<view style="display: flex;flex-direction: column;">
<view
style="display: flex;flex-direction: row; align-items: center; margin-top: 10rpx;">
<text style="color: #101010; font-size: 28rpx; flex: 1;">{{info.name}}</text>
<view style=" align-items: center;">
<text style="color: #ff0000 ; font-size: 24rpx;">{{info.enrollMoldName}}</text>
<text
style="color: #ff0000 ; font-size: 24rpx;margin-left: 10rpx;margin-right: 10rpx;">/</text>
<text
style="color: #ff0000 ; font-size: 24rpx;">{{info.enrollMoney==0?"免费":info.enrollMoney}}</text>
</view>
</view>
<view style="display: flex;flex-direction: row;margin-top: 5rpx;">
<view style="flex: 1; align-items: center;">
<text style="color: #919191; font-size: 24rpx;">报名数</text>
<text
style="color: #919191 ; font-size: 24rpx;">{{info.enrollNumbers==0?"暂无":info.enrollNumbersLimit+'人'}}</text>
<text
style="color: #919191 ; font-size: 24rpx;margin-left: 10rpx;margin-right: 10rpx;">/</text>
<text
style="color: #919191 ; font-size: 24rpx;">{{info.enrollNumbersLimit==0?"不限":info.enrollNumbersLimit+'人'}}</text>
</view>
<text
style="margin-top: 10rpx; color: #919191; font-size: 24rpx;">{{info.startTime}}</text>
</view>
<view v-if="item.listActivityItemsArea.length>pos+1"
style="width: 100%; height: 2rpx;background-color: #efefef; margin-top: 10rpx;">
</view>
</view>
</view>
</view>
</view>
</view>
</RefreshView>
</view>
</template>
<script>
//
import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";
export default {
// 使mixin (main.js)
mixins: [MescrollMixin],
data() {
return {
index: 0,
list: [],
page: {
'sort': 0,
'city': this.ReadPreference("find_city"),
'adCode': this.ReadPreference("find_city_code").slice(0, -2),
state: ""
},
menuList: [{
'title': '当前定位城市',
'key': 'type',
'reflexTitle': true,
'defaultSelectedIndex': 0,
detailList: [{
'title': this.ReadPreference("find_city"),
'value': this.ReadPreference("find_city_code").slice(0, -2)
},
{
'title': '选择其他城市',
'value': '0'
}
]
}, {
'title': '默认活动类型',
'key': 'state',
'reflexTitle': true,
'defaultSelectedIndex': 0,
detailList: [{
'title': '全部活动',
'value': ""
}]
}, {
'title': '默认排序',
'key': 'sort',
'reflexTitle': true,
'defaultSelectedIndex': 0,
'detailList': [{
'title': '按发布时间排序',
'value': 0
},
{
'title': '按报名时间排序',
'value': 1
},
{
'title': '按人气从高到低',
'value': 2
}
]
}
]
}
},
onLoad() {
let that = this
that.HTTP({
url: 'aos/v1/activityManagement/getActivityCreatePageParameter',
method: 'GET',
data: {
"adcode": that.page.adCode
},
paramsType: "FORM",
loading: true
}).then((res) => {
console.log("that.menuList[1].detailList", that.menuList[1].detailList);
if (200 == res.code) {
let list = [];
for (var i = 0; i < res.data.listSportCategoryArea.length; i++) {
list.push({
title: res.data.listSportCategoryArea[i].sportCategoryName,
value: res.data.listSportCategoryArea[i].sid
})
}
//
that.menuList[1].detailList = that.menuList[1].detailList.concat(list)
this.$refs.slLilter.setTitle(this.menuList)
console.log("qqq", that.menuList[1].detailList);
}
});
},
onShow() {
let backResult = this.OnActivityResult();
if (backResult != undefined) {
console.log("结果>" + JSON.stringify(backResult))
if (!this.IsEmpty(backResult.code)) {
console.log("结果2>" + backResult.code)
this.page.adCode = backResult.code
this.page.city = backResult.city
this.menuList[0].detailList[0].title = backResult.city
this.menuList[0].detailList[0].value = backResult.code
console.log("qqq", this.menuList[0].detailList);
this.$refs.slLilter.setTitle(this.menuList)
this.$refs.mescrollRef.resetPageOne();
}
}
},
methods: {
result(val) {
if (!this.IsEmpty(val.sort)) {
this.page.sort = val.sort;
this.list = []
console.log("this.page.sort", this.page.sort);
}
if (!this.IsEmpty(val.type)) {
if (val.type != 0) {
this.page.game = val.type;
this.list = []
} else {
console.log("===>1" + JSON.stringify(this.page))
uni.navigateTo({
url: "../../pages/city/CitySelectActivity?city=" + this.page.city +
"&code=" + this.page.adCode
})
}
}
if (!this.IsEmpty(val.state)) {
this.page.state = val.state;
this.list = []
console.log("this.page.state", this.page.state);
}
this.$refs.mescrollRef.resetPageOne();
},
refresh(page) {
let _this = this
this.HTTP({
url: 'aos/v1/activityManagement/getActivityAreaPagerList',
paramsType: "JSON",
method: "POST",
data: {
current: page.num,
size: 10,
params: {
name: "",
adcode: _this.menuList[0].detailList[0].value,
orderBy: _this.page.sort,
sportCategorySid: _this.page.state
}
},
loading: true
}).then((res) => {
//
_this.$refs.mescrollRef.refreshFinished(res.data.records.length)
if (page.num == 1) {
//
_this.list = []
}
//
_this.list = _this.list.concat(res.data.records)
}).catch(function(err) {
console.log("11111");
_this.$refs.mescrollRef.refreshError()
});
},
clickItem(index) {
let raceSid = this.list[index].sid
console.log(raceSid)
uni.navigateTo({
url: '../index/DetailActivity?raceSid=' + raceSid
});
},
}
}
</script>
<style lang="scss">
.top {
display: flex;
flex-direction: row;
height: 100rpx;
background: #FFFFFF;
opacity: 0.8;
align-items: center;
border-radius: 20rpx;
margin-top: 20rpx;
margin-left: 30rpx;
margin-right: 30rpx;
.unselected {
text-align: center;
font-size: 28rpx;
font-family: Adobe Heiti Std;
font-weight: normal;
color: #191919;
line-height: 40rpx;
}
.selected {
text-align: center;
font-size: 28rpx;
font-family: Adobe Heiti Std;
font-weight: normal;
color: #2CAB69;
line-height: 40rpx;
border-bottom: 2rpx #2CAB69 solid;
padding-bottom: 10rpx;
}
}
.btn1 {
background: #BBBBBB;
}
.btn2 {
background: #0081D5;
}
</style>

299
pages/home/UserFragment.vue

@ -0,0 +1,299 @@
<template>
<view class="content">
<RefreshView ref="mescrollRef" :pageBg="EDEDED" text="个人中心" :useUpScroll="false" :useDownScroll="false"
:useTitleLeftBtn="1" titleLeftBtnSource="管理" :dropLeftList="page.btnList" @drop="drop">
<view class="top">
<image class="touxiang" :src="page.headImage" mode="aspectFill"></image>
<view class="top-right">
<text class="name" @click="bind()">{{page.userNickName}}</text>
<view style="display: flex;align-items: center;margin-right: 30rpx; margin-top: 35rpx;"
@click="baseInfo()" v-if="!this.IsEmpty(page.loginName)">
<text class="name2" style="flex: 1;">{{page.loginName}}</text>
<image src="../../static/home-icon/more.png" style="width: 35rpx;height: 35rpx;"></image>
</view>
</view>
</view>
<view @click="click(2)" class="menu-item">
<image class = "icon" src="../../static/home-icon/game.png" ></image>
<text class="text">我的活动</text>
<text class="explain">{{page.participateInActivityExplain}}</text>
<image class = "more" src="../../static/home-icon/more.png" ></image>
</view>
<view class="line-wide"></view>
<view @click="click(3)" class="menu-item">
<image class = "icon" src="../../static/home-icon/game.png" ></image>
<text class="text">我的队伍</text>
<image class = "more" src="../../static/home-icon/more.png" ></image>
</view>
<view class="line-wide"></view>
<view @click="click(4)" class="menu-item">
<image class = "icon" src="../../static/home-icon/about.png" ></image>
<text class="text">实名信息</text>
<image class = "more" src="../../static/home-icon/more.png" ></image>
</view>
<view class="line-thin"></view>
<view @click="click(5)" class="menu-item">
<image class = "icon" src="../../static/home-icon/shezhi.png" ></image>
<text class="text">设置</text>
<image class = "more" src="../../static/home-icon/more.png" ></image>
</view>
</RefreshView>
</view>
</template>
<script>
export default {
data() {
return {
page: {
btnList: [{
'name': '报名管理',
'src': '../../static/game-icon/renyuanguanli.png',
'id': 0
}, {
'name': '删除活动',
'src': '../../static/game-icon/shanchu.png',
'id': 1
}, {
'name': '取消关闭',
'src': '../../static/game-icon/fanhui.png',
'id': -1
}],
userNickName: "微信登录/注册",
headImage: "http://www.ourpyw.com/upload//touxiang/default_tx.jpg",
isRealAttestation: "",
participateInActivityExplain: "",
realAttestationExplain: "认证后可发布活动。未认证",
loginName: ""
},
}
},
methods: {
drop(index, isLeft, selectData) {
let _this = this;
let id = selectData.id
if (id == 0) {
//
uni.navigateTo({
url: './ManagerHappyGameMembersActivity?gameSid=' + this.page.gameSid
})
} else if (id == 1) {
//
uni.showModal({
title: '温馨提示',
content: '确定删除此赛事?',
success(res) {
if (res.confirm) {
_this.confirm()
}
}
});
} else if (id == -1) {
//
}
},
baseInfo() {
//
let _this = this
this.Login()
.then((res) => {
getApp().globalData.memberSid = res
_this.WritePreference("memberSid", res)
getApp().globalData.isLogin = true
// //
// uni.navigateTo({
// url: '../info/RealMessageActivity'
// })
uni.navigateTo({
url: '../user/baseInfo'
})
})
},
click(id) {
let _this = this
switch (id) {
case 1:
//
this.Login()
.then((res) => {
getApp().globalData.memberSid = res
_this.WritePreference("memberSid", res)
getApp().globalData.isLogin = true
// //
// uni.navigateTo({
// url: '../info/RealMessageActivity'
// })
console.log("asdasdas")
uni.navigateTo({
url: '../user/baseInfo'
})
})
break;
case 2:
//
this.Login()
.then((res) => {
getApp().globalData.memberSid = res
_this.WritePreference("memberSid", res)
getApp().globalData.isLogin = true
uni.navigateTo({
url: '../games/MyGamesActivity'
})
})
// uni.navigateTo({
// url: '../web/WebActivity?url=' + this.putWEBExtra("https://www.ourpyw.com/hide/#/")
// })
break;
case 3:
//
uni.navigateTo({
url: "../me/myteam/CreateTeam"
})
case 4:
uni.navigateTo({
url: '../index/UserAuthentication'
})
// //
// this.$refs.popup.open()
break;
case 5:
//
this.Login()
.then((res) => {
getApp().globalData.memberSid = res
_this.WritePreference("memberSid", res)
getApp().globalData.isLogin = true
uni.navigateTo({
url: '../setup/setUp'
})
})
break;
}
},
bind() {
if (!this.IsEmpty(getApp().globalData.memberSid)) {
return;
}
//
uni.navigateTo({
url: '../me/Login'
})
//
// uni.navigateTo({
// url: '../index/BindPhone?sysUserLoginAuthSid=' +
// getApp().globalData.sysUserLoginAuthSid
// })
},
refresh() {
let _this = this
if (this.IsEmpty(getApp().globalData.memberSid)) {
_this.page.userNickName = "微信登录/注册",
_this.page.headImage = "http://www.ourpyw.com/upload//touxiang/default_tx.jpg",
_this.page.isRealAttestation = "",
_this.page.participateInActivityExplain = "",
_this.page.realAttestationExplain = "认证后可发布活动。未认证",
_this.page.loginName = ""
return;
}
_this.HTTP({
url: 'aos/v1/aosUserManagement/getMyPage/' + getApp().globalData.memberSid,
method: 'GET',
data: {},
paramsType: "FORM",
loading: true
}).then((res) => {
console.log("我的页面初始化", res);
if (res.code == 200) {
_this.page = res.data
_this.page.loginName = "用户号:" + res.data.loginName
//
_this.$refs.mescrollRef.refreshFinished()
}
}, (err) => {
//
_this.$refs.mescrollRef.refreshError()
});;
},
},
onShow() {
this.refresh()
}
}
</script>
<style lang="scss">
@import url("../../styles/master.css");
.content {
display: flex;
flex-direction: column;
height: 100%;
background-color: #EDEDED;
box-sizing: border-box;
.top {
display: flex;
flex-direction: row;
padding-bottom: 35rpx;
padding-top: 55rpx;
width: 100%;
background-color: #FFFFFF;
box-sizing: border-box;
.touxiang {
width: 150rpx;
height: 150rpx;
border-radius: 10%;
margin-left: 35rpx;
margin-right: 35rpx;
flex-shrink: 0;
}
.top-right {
display: flex;
flex-direction: column;
flex: 1;
box-sizing: border-box;
.name {
font-family: sans-serif;
font-weight: 500;
color: #101010;
font-size: 40rpx;
}
.name2 {
display: -webkit-box;
-webkit-line-clamp: 1;
overflow: hidden;
text-overflow: ellipsis;
-webkit-box-orient: vertical;
word-break: break-all;
color: #828282;
font-size: 28rpx;
}
}
}
.user-item-bg {
background-color: #FFFFFF;
margin-top: 20rpx;
}
}
</style>

312
pages/index/ArenaDetailActivity.vue

@ -0,0 +1,312 @@
<template>
<RefreshView ref="mescrollRef" :hasBack="true" :useTitleLeftBtn="0" :useTitleRightBtn="0"
:text="page.title" :useUpScroll="false">
<image class="type" src="../../static/dianhua.png" @click="call"></image>
<view class="name">{{data.linkerName}}</view>
<view class="area">{{data.regionName}}</view>
<view class="address">{{page.distance}}</view>
<!-- <view class="banner">
<swiper class="swiper" indicator-dots="true" autoplay="true">
<swiper-item class="swiper-item" v-for="(item,index) in data.listTwo" :key="index">-->
<image style="width: 100%;height: 450rpx;" :src="data.logo" mode="aspectFill"></image>
<!-- </swiper-item>
</swiper>
</view>-->
<!-- <view style="margin-top: 20rpx;">
<text style="background-color: #F1F1F1;border-radius: 5rpx;margin: 15rpx;padding-left: 15rpx;
padding-right: 15rpx;padding-top: 5rpx;padding-bottom: 5rpx;color: #4c576d;font-size: 22rpx;">{{data.environmentType}}</text>
</view>
-->
<view
style="display: flex;flex-direction: row;box-sizing: border-box;margin-top: 30rpx;margin-left: 20rpx;margin-right: 20rpx;">
<text style="font-size: 24rpx;color: #FF5722;">营业时间</text>
<text style="font-size: 24rpx;color: #FF5722;">{{data.shopTime}}</text>
</view>
<view class="nav-bar">
<image class="bar-left-img" src="../../static/area_dizhi.png" mode="aspectFit"></image>
<text class="bar-text">{{data.address}}</text>
</view>
<view class="line"></view>
<text class="bar-text">球馆简介</text>
<view class="short">
<rich-text :nodes="getShort()" class="short"></rich-text>
</view>
<!-- <uni-popup ref="popupBrowser" type="top">
<uni-pop-browser></uni-pop-browser>
/uni-popup>
#ifdef H5
<view>
<drag-button :isDock="true" :existTabBar="true" @btnClick="btnClick" />
</view>
#endif
<uni-popup ref="popup" type="share">
<uni-popup-share title="分享" @select="select"></uni-popup-share>
</uni-popup> -->
</RefreshView>
</template>
<script>
export default {
data() {
return {
page: {
sid: '',
title: '球馆详情',
lat: -1,
lon: -1,
distance: ""
},
data: {}
}
},
created() {
let _this = this
//
uni.getLocation({
type: 'gcj02',
success: function(res) {
let locat = res
_this.page.lat = locat.latitude
_this.page.lon = locat.longitude
if (_this.page.distance == "") {
// if (_this.data.point != undefined) {
// let split = _this.data.point.split(",")
// //
// _this.page.distance = "" + _this.getDistance(_this.page.lat, _this.page.lon,
// split[1], split[0])
// console.log(_this.page.distance)
// }
//
_this.page.distance = "距您" + _this.getDistance(_this.page.lat, _this.page.lon,
_this.data.lat, _this.data.lng)
console.log(_this.page.distance)
}
_this.refresh()
},
fail(err) {
console.log(err)
}
});
},
onLoad(options) {
this.page.sid = options.sid
},
methods: {
refresh() {
let _this = this;
this.HTTP({
url: 'gms/v1/gymnasiumsManagement/getGymnasiumDetails/'+_this.page.sid,
paramsType: "FORM",
method: "GET",
loading: true
}).then((res) => {
this.data = res.data
//
_this.$refs.mescrollRef.refreshFinished()
console.log("1》》》", _this.page.distance)
if (_this.page.distance == "") {
console.log("2》》》》", _this.page.lat)
if (_this.page.lat != -1) {
console.log("point》》》》",_this.data.point)
// let split = _this.data.point.split(",")
// //
// _this.page.distance = "" + _this.getDistance(_this.page.lat, _this.page.lon, split[
// 1], split[0])
// console.log("3", _this.page.distance)
//
_this.page.distance = "距您" + _this.getDistance(_this.page.lat, _this.page.lon,
_this.data.lat, _this.data.lng)
console.log(_this.page.distance)
}
}
}, (err) => {
//
_this.$refs.mescrollRef.refreshFinished()
})
},
call() {
uni.makePhoneCall({
phoneNumber: this.data.linkerPhone,
success(res) {
console.log(res)
},
fail(err) {
console.log(err)
}
})
},
getShort() {
if (this.IsEmpty(this.data.summary)) {
return "球馆管理员未录入简介"
} else {
return this.data.summary
}
},
daoHang() {
},
},
}
</script>
<style lang="scss">
.short {
margin-left: 40rpx;
margin-right: 40rpx;
}
.bar-text {
font-size: 32rpx;
height: 100%;
line-height: 92rpx;
padding-left: 28rpx;
}
.name {
font-size: 33rpx;
border-radius: 15rpx;
color: #FFFFFF;
z-index: 9000;
top: --window-top;
margin-top: 300rpx;
padding: 10rpx 15rpx;
position: absolute;
left: 20rpx;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
line-height: 50rpx;
}
.area {
font-size: 28rpx;
border-radius: 15rpx;
color: #FFFFFF;
z-index: 9000;
top: --window-top;
margin-top: 350rpx;
padding: 10rpx 15rpx;
position: absolute;
left: 20rpx;
}
.address {
font-size: 25rpx;
border-radius: 15rpx;
color: #FFFFFF;
z-index: 9000;
top: --window-top;
margin-top: 390rpx;
padding: 10rpx 15rpx;
position: absolute;
left: 20rpx;
}
.type {
font-size: 28rpx;
border-radius: 15rpx;
color: #2C405A;
font-weight: bold;
z-index: 9000;
top: --window-top;
margin-top: 410rpx;
padding: 10rpx 15rpx;
position: absolute;
right: 20rpx;
width: 70rpx;
height: 70rpx;
}
.banner {
height: 450rpx;
width: 100%;
box-sizing: border-box;
.swiper {
height: 100%;
width: 100%;
.swiper-item {
height: 100%;
width: 100%;
.banner-image {
width: 100%;
height: 100%;
border-bottom-left-radius: 20rpx;
border-bottom-right-radius: 20rpx;
}
}
}
}
.nav-bar {
min-height: 92rpx;
width: 100%;
display: flex;
margin-top: 30rpx;
box-sizing: border-box;
flex-direction: row;
align-items: center;
margin-bottom: 30rpx;
margin-right: 35rpx;
.bar-left-img {
width: 60rpx;
height: 60rpx;
margin-left: 20rpx;
box-sizing: border-box;
}
.bar-text {
font-size: 28rpx;
flex: 1;
box-sizing: border-box;
line-height: 40rpx;
}
.bar-img {
width: 90rpx;
height: 92rpx;
padding-right: 28rpx;
}
}
.line {
width: 100%;
height: 18rpx;
background-color: #f5f4f9;
}
</style>

136
pages/index/BaoMingListActivity.vue

@ -0,0 +1,136 @@
<template>
<view>
<RefreshView ref="mescrollRef" @refresh="refresh" :hasBack="true" text="报名列表">
<view v-for="(item,index) in data.list" :key='index' class="list-item-layout">
<image :src="item.headImage" class="list-item-img" mode="aspectFill">
</image>
<view class="list-item-right">
<view class="list-item-name">{{item.realName}}</view>
<view class="list-item-content">报名时间{{item.timeStr}}</view>
</view>
</view>
</RefreshView>
</view>
</template>
<script>
//
import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";
export default {
// 使mixin (main.js)
mixins: [MescrollMixin],
data() {
return {
page: {
activitySid: '',
activityItemSid: '',
},
data: {
list: []
}
}
},
onLoad(options) {
//
this.page.activitySid = options.activitySid
this.page.activityItemSid = options.activityItemSid
},
methods: {
refresh(page) {
let _this = this;
this.HTTP({
url: 'aos/v1/participantRelation/getPageListPersonalParticipant',
paramsType: "JSON",
method: "POST",
data: {
current: page.num,
size: 10,
params: {
activitySid: this.page.activitySid,
activityItemSid: this.page.activityItemSid ,
}
},
loading: true
}).then((res) => {
//
_this.$refs.mescrollRef.refreshFinished(res.data.records.length)
if (page.num == 1) {
//
_this.data.list = []
}
//
_this.data.list = _this.data.list.concat(res.data.records)
}).catch(function(err) {
console.log("11111");
_this.$refs.mescrollRef.refreshError()
})
},
}
}
</script>
<style lang="scss">
.list-item-layout {
display: flex;
width: 100%;
flex-direction: row;
box-sizing: border-box;
padding-top: 20rpx;
padding-bottom: 10rpx;
margin-left: 33rpx;
margin-right: 33rpx;
border-bottom: 0.1px #F1F1F1 solid;
.list-item-img {
width: 140rpx;
height: 140rpx;
margin-right: 25rpx;
flex-shrink: 0;
}
.list-item-right {
flex: 1;
margin-top: 20rpx;
display: flex;
flex-direction: column;
height: 140rpx;
.list-item-name {
width: 100%;
flex: 1;
color: #333333;
font-size: 27rpx;
}
.list-item-content {
width: 100%;
flex: 1;
color: #999999;
font-size: 24rpx;
}
.list-item-time {
width: 100%;
color: #999999;
font-size: 24rpx;
flex: 1;
}
}
}
</style>

146
pages/index/BindPhone.vue

@ -0,0 +1,146 @@
<template>
<view>
<RefreshView ref="mescrollRef" :hasBack="true" text="绑定手机号" :useDownScroll="false" :useUpScroll="false">
<view style="margin-top: 30rpx;">
<view class="inputRow">
<image src="../../static/img/login/username.png" mode="aspectFill" class="drawableLeft"></image>
<input type="number" maxlength="11" @input="phoneText" placeholder="请输入手机号" class="input" />
<SendCodeItem :phoneNum="page.phone" url="aos/v1/aosUser/sendCodeFromWxBindMobile" @click="send" ref="wxCodeItem"></SendCodeItem>
</view>
</view>
<view class="inputRow">
<image src="../../static/img/login/code.png" mode="aspectFill" class="drawableLeft"></image>
<input type="number" @input="codeText" maxlength="6" placeholder="请输入验证码" class="input" />
</view>
<view class="btn" @click="next">
<text class="btnText">绑定手机</text>
</view>
</RefreshView>
</view>
</template>
<script>
export default {
data() {
return {
page: {
phone: '',
sysUserWxAuthSid: '',
code: ''
}
};
},
onLoad(options) {
this.page.sysUserWxAuthSid = options.sysUserWxAuthSid
console.log('=======', options)
console.log('=======', options.sysUserWxAuthSid)
},
methods: {
next() {
var phoneLength = this.page.phone.length;
var codeLength = this.page.code.length;
if (phoneLength == 0) {
this.Toast("请输入手机号")
return;
}
if (codeLength == 0) {
this.Toast("验证码不能为空")
return;
}
let _this = this
this.HTTP({
url: 'aos/v1/aosUser/wxBindMobile',
data: {
mobile: this.page.phone,
sysUserWxAuthSid: this.page.sysUserWxAuthSid,
code: this.page.code
},
method: 'POST',
paramsType: "JSON",
loading: true
}).then((res) => {
console.log('=======', res)
if (res.code == 200) {
//
_this.WritePreference("sysUserSid", res.data)
_this.WritePreference("isLogin", true)
getApp().globalData.isLogin = true
getApp().globalData.sysUserSid = res.data
console.log('=======1111111111111111111111111111111sdfasdf;kjasdfjkasdklfkasdjf;asdddddddddddddd',res)
// $emit webviwew
// uni.$emit('login', res.data.memberSid)
uni.navigateBack({
delta: 10
});
}
});
},
phoneText(e) {
//
this.page.phone = e.detail.value;
},
send(e) { //
console.log(e);
},
codeText(e) {
//
this.page.code = e.detail.value;
}
}
};
</script>
<style lang="scss">
.inputRow {
display: flex;
margin-left: 30rpx;
margin-right: 30rpx;
margin-bottom: 10rpx;
margin-top: 10rpx;
padding-bottom: 10rpx;
border-bottom: 0.1px #F1F1F1 solid;
align-items: center;
.input {
margin-left: 20rpx;
height: 70rpx;
flex: 1;
font-size: 32rpx;
}
.drawableLeft {
width: 40rpx;
height: 40rpx;
margin: 20rpx;
}
}
.btn {
display: flex;
width: 90%;
height: 80rpx;
flex-direction: column;
background-color: $uni-base-color;
margin-top: 80rpx;
margin-left: auto;
margin-right: auto;
align-items: center;
justify-content: center;
border-radius: 10rpx;
.btnText {
color: #ffffff;
font-size: 33rpx;
}
}
</style>

394
pages/index/DetailActivity.vue

@ -0,0 +1,394 @@
<template>
<RefreshView ref="mescrollRef" :hasBack="true" :text="data.name" :useDownScroll="false" :useUpScroll="false"
useTitleRightBtn="0">
<view class="banner">
<swiper class="swiper" indicator-dots="true" autoplay="true">
<swiper-item class="swiper-item" v-for="(item,index) in data.listCoverImage" :key="index">
<image style="width: 100%;height: 450rpx;" :src="item" mode="aspectFill"></image>
</swiper-item>
</swiper>
</view>
<view style="display: flex;flex-direction: column;background: #FFFFFF; width: 100%;height: 100%;">
<!-- <view
style="display: flex;flex-direction: column;background: #FFFFFF;
margin-top: 20rpx;margin-left: 30rpx;margin-right: 30rpx; border: 2rpx solid #666666; border-radius: 30rpx;padding: 20px;">
-->
<view>
<view style="display: flex; flex-direction: row; margin-left: 20rpx;margin-top: 20rpx;">
<text
style="color: #101010; font-size: 36rpx;font-weight: 520; white-space: nowrap;overflow: hidden;text-overflow: ellipsis">{{data.name}}</text>
<text
style=" word-break:keep-all;
white-space:nowrap;display: flex;align-items: center; margin-left: 30rpx; color: #fff; font-size: 20rpx; background-color: #F2BF5C; padding: 3rpx 15rpx;border-radius: 5rpx;">{{data.sportCategoryName}}</text>
</view>
</view>
<view style="background-color: #F1F1F1; height: 5rpx; width: 100%; margin-top: 20rpx;"></view>
<view
style="display: flex;flex-direction: row;margin-top: 15rpx;align-items: center;margin-left: 20rpx;margin-right: 30rpx;">
<text style="font-size: 30rpx;color: #080808;">报名截止</text>
<text style="color: #666666;font-size: 26rpx;">{{data.enrollEndTime}}</text>
<text style="color: #E99D42;font-size: 26rpx;flex: 1;text-align: right;">还有1天</text>
</view>
<view style="background-color: #F1F1F1; height: 5rpx; width: 100%; margin-top: 20rpx;"></view>
<view style="display: flex;flex-direction: row;margin-top: 15rpx;margin-left: 20rpx;margin-right: 30rpx;">
<text style="font-size: 30rpx;color: #080808;">整体活动</text>
<view style="display: flex;flex-direction: column;">
<text style="color: #666666; font-size: 26rpx;">{{data.startTime}}</text>
<text style="color: #666666;margin-top: 20rpx;font-size: 26rpx;">{{data.endTime}}</text>
</view>
</view>
<view style="">
<view v-for="(item,index) in data.listActivityItemsDetails " :key="index" style="margin-top: 20rpx;">
<view
style="display: flex;flex-direction: row;margin-top: 20rpx;background-color: #F1F1F1;padding: 20rpx 30rpx;">
<text style="font-size: 30rpx;flex: 1;">{{item.name}}</text>
<view style="display: flex;flex-direction: row;">
<text style="font-size: 25rpx; color: #E99D42; ">{{item.enrollMoldName}}</text>
<text
style="color: #E99D42; margin-left: 10rpx;margin-right: 10rpx; font-size: 25rpx;">/</text>
<text
style="color: #E99D42; font-size: 25rpx;">{{item.enrollMoney==0?"免费":item.enrollMoney+'元'}}</text>
</view>
</view>
<view
style="display: flex;flex-direction: column;margin-top: 20rpx;margin-left: 30rpx;margin-right: 30rpx;">
<text style="color: #666666; margin-top: 15rpx;font-size: 28rpx;">{{item.introduction}}</text>
</view>
<view style="background-color: #F1F1F1; height: 5rpx; width: 100%;margin-top: 15rpx; ">
</view>
<!-- <view
style="display: flex;margin-top: 20rpx;margin-left: 30rpx;margin-right: 30rpx;align-items: center;">
<text style="font-size: 30rpx;">活动时间</text>
<text
style="color: #666666; font-size: 25rpx;flex: 1;">{{item.startTime+" 至 "+item.endTime}}</text>
</view>
-->
<view
style="display: flex;flex-direction: row;margin-top: 20rpx;margin-left: 30rpx;margin-right: 30rpx;align-items: center;">
<view style="display: flex;flex-direction: row;align-items: center;flex: 1;">
<text style="font-size: 30rpx;color: #E3A428 ;">{{item.startTime}}</text>
<text
style="color: #666666; font-size: 30rpx;text-align: right;flex: 1;">{{item.gymnasiumName}}</text>
</view>
<image v-if="!showAddress(item.gymnasiumSid)" style="width: 30rpx;height: 30rpx;"
src="../../static/home-icon/more.png" @click="gymnasiumName(item.gymnasiumSid)">
</image>
</view>
<view style="background-color: #F1F1F1; height: 5rpx; width: 100%;margin-top: 15rpx; "></view>
<view
style="display: flex;flex-direction: row;margin-top: 20rpx;margin-left: 30rpx;margin-right: 30rpx;align-items: center;">
<view style="display: flex;flex-direction: row;align-items: center;flex: 1;">
<text style="font-size: 30rpx;">报名数</text>
<text
style="color: #919191 ; font-size: 25rpx; margin-left: 20rpx;">{{item.enrollNumbers==0?"暂无":item.enrollNumbersLimit+'人'}}</text>
<text
style="color: #919191 ; font-size: 25rpx;margin-left: 10rpx;margin-right: 10rpx;">/</text>
<text
style="color: #919191 ; font-size: 25rpx;">{{item.enrollNumbersLimit==0?"不限":item.enrollNumbersLimit+'人'}}</text>
</view>
<view v-if="item.listUserHeadImageUrl.length>0" style="display: flex;flex-direction: row; "
@click="userList(item.sid)">
<view v-for="(url,i) in item.listUserHeadImageUrl " :key="i">
<view
style="display: flex; width: 100%; margin-left: 5rpx; margin-right: 5rpx;justify-content: center;">
<image style="border-radius: 50%; width: 50rpx;height: 50rpx;" :src="url"
mode="aspectFit"></image>
</view>
</view>
</view>
<image style="width: 30rpx;height: 30rpx;" src="../../static/home-icon/more.png"
@click="userList(item.sid)">
</image>
</view>
</view>
</view>
<view style="margin-top: 20rpx;background-color: #F1F1F1;height: 20rpx;">
</view>
<view style="display: flex;flex-direction: row;margin: 20rpx 30rpx 0rpx 30rpx; align-items: center;">
<text style="font-size: 30rpx;">活动介绍</text>
<view style="background-color: #F1F1F1; height: 10rpx; flex: 1;margin-left: 20rpx; "></view>
</view>
<text style="margin: 30rpx; color: #999999;font-size: 28rpx; ">{{data.introduction}}</text>
<view style="display: flex;flex-direction: row;margin: 20rpx 30rpx 0rpx 30rpx; align-items: center;">
<text style="font-size: 30rpx;">奖品奖项</text>
<view style="background-color: #F1F1F1; height: 10rpx; flex: 1;margin-left: 20rpx; "></view>
</view>
<text style="margin: 30rpx; color: #999999;font-size: 28rpx; ">{{data.notes}}</text>
<!--
<textarea style="margin: 30rpx; color: #999999 ; font-size: 25rpx;white-space: pre-wrap; "
read-only="readOnly" disabled="disabled " placeholder="无" v-model="data.notes" /> -->
<view style="display: flex;flex-direction: row;margin: 10rpx 30rpx 0rpx 30rpx; align-items: center;">
<text style="font-size: 30rpx;">特别鸣谢</text>
<view style="background-color: #F1F1F1; height: 10rpx; flex: 1;margin-left: 20rpx; "></view>
</view>
<view
style="display: flex;margin-left: 40rpx;margin-right: 40rpx;flex-direction: column; margin-top: 30rpx;">
<view v-for="(item,pos) in data.listSpecialThanks " :key="pos">
<view
style="display: flex; width: 100%; padding-top: 10rpx;padding-bottom: 10rpx;justify-content: center;">
<text style="color: #898989;font: size 28rpx;flex: 1;">{{item.name}}</text>
</view>
</view>
</view>
<view style="background-color: #F1F1F1; height: 5rpx; width: 100%;margin-top: 20rpx; "></view>
<view
style="display: flex;flex-direction: row;margin-top: 20rpx;margin-left: 30rpx; margin-right: 30rpx; align-items: center;">
<text style="color: #898989; font-size: 28rpx;">主办方</text>
<text style="color: #898989;font-size: 25rpx;">{{data.organizer}}</text>
</view>
<view style="background-color: #F1F1F1; height: 5rpx; width: 100%; margin-top: 20rpx;"></view>
<view
style="display: flex;flex-direction: row;margin-top: 20rpx;margin-left: 30rpx; margin-right: 30rpx; align-items: center;">
<text style="color: #898989;font-size: 28rpx;">联系人</text>
<view style="display: flex;flex-direction: row;">
<text style="color: #898989;font-size: 25rpx;">{{data.linkerName}}</text>
<view style="width: 30rpx;"></view>
<text style="color: #898989; font-size: 25rpx;">{{data.linkerPhone}}</text>
</view>
</view>
<view style="background-color: #F1F1F1; height: 5rpx; width: 100%; margin-top: 20rpx;"></view>
<view class="agreeMent">
<checkbox-group @change="checkboxChange">
<checkbox style="transform:scale(0.7)" :checked="checked1"></checkbox>
</checkbox-group>
<text class="text2">我已阅读并同意</text>
<text style="color: #007AFF;">参赛须知</text>
<text style="display: flex;text-align: center; margin-left: 20rpx; padding: 5rpx 10rpx;background: #0081D5;
color: #FFFFFF; font-size: 28rpx;" @click="click()"> 我要报名 </text>
</view>
</view>
</view>
<view style="height: 300rpx;"></view>
</RefreshView>
</template>
<script>
export default {
data() {
return {
checked1: true,
data: {
},
raceSid: ""
}
},
onLoad: function(option) {
this.raceSid = option.raceSid;
this.HTTP({
url: 'aos/v1/activityManagement/getActivityDetails/' + this.raceSid,
method: 'GET',
paramsType: "FORM",
loading: true
}).then((res) => {
if (200 == res.code) {
this.data = res.data
} else {
this.Toast(res.msg)
uni.navigateBack({
delta: 1
})
}
});
},
methods: {
showAddress(s) {
console.log("..." + s);
return this.IsEmpty(s)
},
checkboxChange(e) {
this.checked1 = !this.checked1
console.log(this.checked1)
},
gymnasiumName(e) {
uni.navigateTo({
url: "ArenaDetailActivity?sid=" + e
})
console.log("gymnasiumName>>", e)
},
userList(sid) {
console.log("zazzz>>", this.data.sid)
console.log("userList>>", sid)
uni.navigateTo({
url: "BaoMingListActivity?activitySid=" + this.data.sid + "&activityItemSid=" + sid
})
},
click() {
let _this = this;
if (!this.checked1) {
this.Toast("请认真阅读参赛须知,并勾选。")
return
}
// if (this.data.listActivityItemsDetails.length == 1) {
// let evendSid = this.data.sid
// let eventsSubprojectSid = this.data.listActivityItemsDetails[0].sid
// this.HTTP({
// url: 'aos/events/v1/eventsenroll/save',
// method: 'POST',
// data: {
// 'eventsSid': evendSid,
// 'eventsSubprojectSid': eventsSubprojectSid,
// 'participantSid': getApp().globalData.memberSid,
// 'paymentMemberSid': "",
// },
// paramsType: "JSON",
// loading: true
// }).then((res) => {
// if (res.code == 200) {
// this.Toast("")
// // delta
// uni.navigateBack({
// delta: 10
// });
// } else {
// this.Toast(res.msg)
// }
// }, (err) => {
// //
// _this.Toast("" + err.data.errmsg)
// });
// } else {
console.log("===========" + this.data.listActivityItemsDetails.length)
let list = this.data.listActivityItemsDetails
let newList = []
for (var i = 0; i < list.length; i++) {
newList.push(list[i].name)
}
uni.showActionSheet({
itemList: newList,
success: function(res) {
console.log("itemList==" + newList)
console.log("res==" + JSON.stringify(res))
_this.listSelect(res.tapIndex, newList)
},
fail(e) {
console.log("reeees==" + JSON.stringify(e))
}
});
// }
},
listSelect(id, info) {
console.log(id)
console.log(info)
let _this = this
let evendSid = this.data.sid
let eventsSubprojectSid = this.data.listActivityItemsDetails[id].sid
console.log(eventsSubprojectSid)
this.Login()
.then((res) => {
this.HTTP({
url: 'aos/v1/activityItemManagement/enroll',
method: 'POST',
data: {
'activitySid': evendSid,
'activityItemsSid': eventsSubprojectSid,
'participantSid': res,
'paymentMemberSid': "",
},
paramsType: "JSON",
loading: true
}).then((res) => {
if (res.code == 200) {
this.Toast("报名成功!")
// delta
uni.navigateBack({
delta: 10
});
} else {
this.Toast(res.msg)
}
}, (err) => {
//
_this.Toast("出错了:" + err.data.errmsg)
})
})
},
}
}
</script>
<style lang="scss">
.items {
display: flex;
flex-direction: column;
}
.agreeMent {
display: flex;
flex-direction: row;
align-items: center;
margin-top: 50rpx;
margin-left: 30rpx;
.text2 {
color: #666666;
font-size: 30rpx;
}
}
.banner {
height: 450rpx;
width: 100%;
box-sizing: border-box;
.swiper {
height: 100%;
width: 100%;
.swiper-item {
height: 100%;
width: 100%;
.banner-image {
width: 100%;
height: 100%;
border-bottom-left-radius: 20rpx;
border-bottom-right-radius: 20rpx;
}
}
}
}
</style>

470
pages/index/InputHappyGameAreaActivity.vue

@ -0,0 +1,470 @@
<template>
<TabLayout ref='tabLayout' text="选择球馆" :tabTitleData="page.tabTitle" @tabClickItem='clickTab' @downRefresh="down">
<swiper style="min-height: 100vh;" :current="page.currentTab" @change="swiperTab">
<swiper-item v-for="(listItem,listIndex) in data.tabList" :key="listIndex" style="box-sizing: border-box;">
<scroll-view style="height: 100%;" scroll-y="true" @scrolltolower="lower1">
<view :style="{'width': '100%','padding-top': page.paddingTop}">
<loading v-if="page.showLoading[listIndex]" :key='listIndex'></loading>
<view class="outer" v-for="(item,index) in listItem">
<view class="Item">
<image class="Img" :src="item.logo" mode="aspectFill" @click="img(item.sid)"></image>
<view class="Right" @click="right(item.sid,item.name)">
<text class="gameTv">{{item.name}}</text>
<text class="gameTv2">{{item.address}}</text>
<!-- <text class="gameTv2">{{item.linkerName+" "+item.linkerPhone}}</text> -->
</view>
</view>
</view>
<loading v-if="page.showBottomLoading[listIndex]" :key="'bottom'+listIndex"></loading>
<noData v-if="page.showNoData[listIndex]" :key="'nodata'+listIndex"></noData>
</view>
</scroll-view>
</swiper-item>
</swiper>
</TabLayout>
</template>
<script>
const util = require('../../util/util.js');
export default {
data() {
return {
page: {
tabTitle: ['常驻球馆', '所有球馆'],
tabType: ["gms/v1/userGymnasiumsManagement/getUserGymnasiumList",
"gms/v1/gymnasiumsManagement/getGymnasiumAreaVoPagerList"
],
currentTab: 0,
pages: [1, 1], //swiper
nowLoadingPages: [false, false], //
paddingTop: '0px',
showLoading: [false, false],
showBottomLoading: [false, false],
showNoData: [false, false],
city: "石家庄",
code: "1301"
},
data: {
tabList: [
[],
[],
[],
[]
]
},
info: {
address: "",
placeName: ''
}
};
},
onLoad(options) {
let find_city_code = this.ReadPreference("find_city_code")
let find_city = this.ReadPreference("find_city")
let _this = this
this.$nextTick()
.then(function() {
_this.page.paddingTop = _this.$refs.tabLayout.getViewPagerTop()
})
if (this.IsEmpty(find_city_code)) {
//
find_city_code = 0;
//
let _this = this;
//
uni.getLocation({
type: 'gcj02',
success: function(res) {
let locat = res
//
_this.location = {
latitude: locat.latitude,
longitude: locat.longitude
}
let url =
"https://restapi.amap.com/v3/geocode/regeo?key=b564c757b4cf4fd4a5d914625ca9373f&location=" +
res.longitude +
"," + res.latitude +
"&poitype=&radius=1000&extensions=all&batch=false&roadlevel=0";
_this.HttpOtherUrl({
url: url
}).then((res) => {
let json = JSON.stringify(res);
let info = JSON.parse(json).regeocode.addressComponent;
//
let city = info.city;
//
let code = info.adcode;
//
if (code > 0) {
//
_this.WritePreference("find_city", city)
_this.WritePreference("find_city_code", code)
_this.page.code = code
_this.page.city = city
_this.page.tabTitle[1] = "所有球馆(" + _this.page.city + ")"
} else {
//
_this.WritePreference("find_city", "石家庄")
_this.WritePreference("find_city_code", "1301")
_this.page.code = "1301"
_this.page.city = "石家庄"
_this.page.tabTitle[1] = "所有球馆(" + _this.page.city + ")"
}
}, (err) => {
//
_this.WritePreference("find_city", "石家庄")
_this.WritePreference("find_city_code", "1301")
_this.page.code = "1301"
_this.page.city = "石家庄"
_this.page.tabTitle[1] = "所有球馆(" + _this.page.city + ")"
})
},
fail(err) {
//
_this.WritePreference("find_city", "石家庄")
_this.WritePreference("find_city_code", "1301")
_this.page.code = "1301"
_this.page.city = "石家庄"
_this.page.tabTitle[1] = "所有球馆(" + _this.page.city + ")"
}
});
} else {
this.page.code = find_city_code
this.page.city = find_city
this.page.tabTitle[1] = "所有球馆(" + this.page.city + ")"
}
//
this.down(0)
},
methods: {
img(sid) {
uni.navigateTo({
url: 'ArenaDetailActivity?sid=' + sid
})
},
right(sid, name) {
this.info.sid = sid
this.info.name = name
console.log("传值>" + JSON.stringify(this.info))
this.SetResult(this.info)
},
// swiper
swiperTab: function(e) {
// tab
this.$refs.tabLayout.changeTab(e);
},
clickTab(index) {
this.page.currentTab = index
//
this.down(index)
},
down(index) {
//
let _this = this
this.data.tabList[index] = []
//
_this.$forceUpdate()
//
this.page.pages[index] = 1
//
this.page.nowLoadingPages[index] = true
//
this.page.showLoading[index] = true
this.page.showNoData[index] = false
if (index == 0) {
this.Login()
.then((res) => {
let path = index == 0 ? "/" + getApp().globalData.memberSid : "";
this.HTTP({
url: _this.page.tabType[index] + path,
method: index == 0 ? 'GET' : 'POST',
paramsType: index == 0 ? 'FORM' : "JSON",
data: index == 0 ? {} : {
"current": 1,
"size": 10,
"params": {
"memberSid": getApp().globalData.memberSid,
"regionId": _this.page.code
}
}
}).then((res) => {
//
_this.page.nowLoadingPages[index] = false
//
_this.data.tabList[index] = res.data
_this.$refs.tabLayout.downRefresh()
//
_this.$forceUpdate()
// loading
_this.page.showLoading[index] = false
_this.page.showNoData[index] = res.data.length == 0
}, (err) => {
//
_this.page.nowLoadingPages[index] = false
_this.$refs.tabLayout.downRefresh()
//
_this.$forceUpdate()
// loading
_this.page.showLoading[index] = false
_this.page.showNoData[index] = false
})
})
}else{
let path = index == 0 ? "/" + getApp().globalData.memberSid : "";
this.HTTP({
url: _this.page.tabType[index] + path,
method: index == 0 ? 'GET' : 'POST',
paramsType: index == 0 ? 'FORM' : "JSON",
data: index == 0 ? {} : {
"current": 1,
"size": 10,
"params": {
"memberSid": getApp().globalData.memberSid,
"regionId": _this.page.code
}
}
}).then((res) => {
//
_this.page.nowLoadingPages[index] = false
//
_this.data.tabList[index] = res.data.records
_this.$refs.tabLayout.downRefresh()
//
_this.$forceUpdate()
// loading
_this.page.showLoading[index] = false
_this.page.showNoData[index] = res.data.records.length == 0
}, (err) => {
//
_this.page.nowLoadingPages[index] = false
_this.$refs.tabLayout.downRefresh()
//
_this.$forceUpdate()
// loading
_this.page.showLoading[index] = false
_this.page.showNoData[index] = false
})
}
},
request(index, pagerStart) {
let _this = this
let path = index == 0 ? "/" + getApp().globalData.memberSid : "";
this.HTTP({
url: _this.page.tabType[index] + path,
method: index == 0 ? 'GET' : 'POST',
paramsType: index == 0 ? 'FORM' : "JSON",
data: index == 0 ? {} : {
"current": pagerStart,
"size": 10,
"params": {
"memberSid": getApp().globalData.memberSid,
"regionId": _this.page.code
}
}
}).then((res) => {
//
_this.page.nowLoadingPages[index] = false
_this.$refs.tabLayout.downRefresh()
_this.page.showBottomLoading[this.page.currentTab] = false
if (res.data.records.length == 0) {
_this.Toast('没有更多数据了')
//
_this.$forceUpdate()
return
}
_this.data.tabList[index] = _this.data.tabList[index].concat(res.data.records)
//
_this.$forceUpdate()
}, (err) => {
//
_this.page.nowLoadingPages[index] = false
_this.$refs.tabLayout.downRefresh()
//
_this.$forceUpdate()
})
},
// util.throttle
lower1: util.throttle(function(e) {
if (this.page.nowLoadingPages[this.page.currentTab]) {
//
return
}
this.page.showBottomLoading[this.page.currentTab] = true
//
this.$forceUpdate()
//
this.page.pages[this.page.currentTab]++
//
this.page.nowLoadingPages[this.page.currentTab] = true
//
this.request(this.page.currentTab, this.page.pages[this.page.currentTab])
}, 300)
}
}
</script>
<style lang="scss">
.header {
display: flex;
flex-direction: row;
justify-content: space-between;
margin-left: 30rpx;
margin-right: 30rpx;
margin-top: 25rpx;
padding-bottom: 20rpx;
.textLogin {
background-color: #E91E63;
border-radius: 30rpx;
padding-left: 25rpx;
padding-right: 25rpx;
padding-top: 7rpx;
padding-bottom: 7rpx;
color: #FFFFFF;
font-size: 24rpx;
}
}
.textGray3 {
color: #999999;
font-size: 26rpx;
margin-top: 5rpx;
display: block;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.outer {
width: 100%;
display: flex;
flex-direction: column;
padding: 22rpx 38rpx;
box-sizing: border-box;
border-bottom: 0.1px #F1F1F1 solid;
.title {
padding-bottom: 22rpx;
flex: 1;
font-size: 28rpx;
color: #007AFF;
overflow: hidden;
box-orient: vertical;
text-overflow: ellipsis;
}
.Item {
display: flex;
flex-direction: row;
width: 100%;
height: 144rpx;
box-sizing: border-box;
.Img {
height: 100%;
width: 220rpx;
margin-right: 15rpx;
flex-shrink: 0;
}
}
.Right {
height: 100%;
display: flex;
flex-direction: column;
flex: 1;
box-sizing: border-box;
.gameTv {
font-size: 30rpx;
color: #000000;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
overflow: hidden;
}
.gameTv2 {
font-size: 25rpx;
margin-top: 10rpx;
color: #666666;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
overflow: hidden;
}
}
}
</style>

128
pages/index/InputHappyGameLimitActivity.vue

@ -0,0 +1,128 @@
<template>
<RefreshView ref="mescrollRef" :hasBack="true" text="人数限制" :useDownScroll="false" :useUpScroll="false"
useTitleRightBtn="1" titleRightBtnSource="保存" @rightBtn='rightBtnClick'>
<view style="display: flex;flex-direction: column;margin-top: 50rpx;margin-left: 30rpx;margin-right: 30rpx;">
<text>人数限制</text>
<view style="display: flex;flex-direction: row;align-items: center;margin-top: 20rpx;">
<view style="border: 2px #F1F1F1 solid;display: flex; ">
<input class="right" type="number" @input="enrollNumbersLimitText" placeholder="请输入人数限制"
:value="result.enrollNumbersLimit"></input>
</view>
<text
style="display: flex;flex: 1; justify-content: flex-end; font-size: 30rpx;color: #FF0000;">0为不限制</text>
</view>
</view>
<view style="display: flex;flex-direction: column;margin-top: 50rpx;margin-left: 30rpx;margin-right: 30rpx;">
<text>报名费用</text>
<view style="display: flex;flex-direction: row;align-items: center;margin-top: 20rpx;">
<view style="border: 2px #F1F1F1 solid;display: flex; ">
<input class="right" type="digit" @input="enrollMoneyText" placeholder="请输入报名费用"
:value="result.enrollMoney"></input>
</view>
<text
style="display: flex;flex: 1; justify-content: flex-end; font-size: 30rpx;color: #FF0000;">0为免费</text>
</view>
</view>
<!-- <radio-group v-if="!this.IsEmpty(result.enrollMoney)" style="margin-top: 50rpx; display: flex;flex-direction: row; " @change="radioChange">
<radio style="display: flex;flex: 1;justify-content: center;" :checked="result.checked1">线上收费</radio>
<radio style="display: flex;flex: 1;justify-content: center;" :checked="result.checked2">线下收费</radio>
</radio-group>
-->
</RefreshView>
</template>
<script>
export default {
data() {
return {
result: {
enrollNumbersLimit: "0",
enrollMoney: "0",
// type: "",
// checked1: "",
// checked2: "",
},
}
},
onLoad(options) {
this.result.enrollNumbersLimit = options.enrollNumbersLimit
this.result.enrollMoney = options.enrollMoney
// this.result.type = options.type
// if ("线" == this.result.type) {
// this.result.checked1 = true
// this.result.checked2 = false
// } else {
// this.result.checked1 = false
// this.result.checked2 = true
// }
console.log("1===" + this.result.index)
console.log("2===" + this.result.enrollNumbersLimit)
console.log("3===" + this.result.enrollMoney)
// console.log("4===" + this.result.type)
},
methods: {
enrollNumbersLimitText(e) {
console.log("1===" + e.detail.value)
this.result.enrollNumbersLimit = e.detail.value
},
enrollMoneyText(e) {
console.log("2===" + e.detail.value)
this.result.enrollMoney = e.detail.value
},
// radioChange(e) {
// this.result.checked1 = !this.result.checked1
// this.result.checked2 = !this.result.checked2
// console.log('radiochangevalue', this.result.checked1)
// console.log('radiochangevalue', this.result.checked2)
// if (this.result.checked1) {
// this.result.type = "线"
// } else {
// this.result.type = "线"
// }
// console.log('type===', this.result.type)
// },
rightBtnClick() {
if (this.IsEmpty(this.result.enrollNumbersLimit)) {
this.result.enrollNumbersLimit = 0
}
let o = parseInt(this.result.enrollNumbersLimit)
if (isNaN(o)) {
this.Toast("输入的格式有误")
return
}
if (this.IsEmpty(this.result.enrollMoney)) {
this.result.enrollMoney = 0
}
let s = parseInt(this.result.enrollMoney)
if (isNaN(s)) {
this.Toast("输入的格式有误")
return
}
this.SetResult(this.result)
}
}
}
</script>
<style lang="scss">
.right {
padding: 20rpx;
flex: 1;
color: #555555;
font-size: 30rpx;
}
</style>

127
pages/index/UserAuthentication.vue

@ -0,0 +1,127 @@
<template>
<view>
<RefreshView ref="mescrollRef" :hasBack="true" text="用户认证" :useDownScroll="false" :useUpScroll="false">
<view style="margin-top: 30rpx;">
<view class="inputRow">
<image src="../../static/login/username.png" mode="aspectFill" class="drawableLeft"></image>
<input type="number" maxlength="11" @input="phoneText" placeholder="请输入手机号" class="input" />
<SendCodeItem :phoneNum="page.phone" url="portal/v1/sysUserManagement/sendCodeFromAttestation" @click="send" ref="wxCodeItem"></SendCodeItem>
</view>
</view>
<view class="inputRow">
<image src="../../static/login/code.png" mode="aspectFill" class="drawableLeft"></image>
<input type="number" @input="codeText" maxlength="6" placeholder="请输入验证码" class="input" />
</view>
<view class="btn" @click="next">
<text class="btnText">验证</text>
</view>
</RefreshView>
</view>
</template>
<script>
export default {
data() {
return {
page: {
phone: '',
code: ''
}
};
},
methods: {
next() {
var phoneLength = this.page.phone.length;
var codeLength = this.page.code.length;
if (phoneLength == 0) {
this.Toast("请输入手机号")
return;
}
if (codeLength == 0) {
this.Toast("验证码不能为空")
return;
}
let _this = this
this.HTTP({
url: 'portal/v1/sysUserManagement/verifyCodeFromAttestation',
data: {
mobile: this.page.phone,
code: this.page.code
},
method: 'GET',
paramsType: "FORM",
loading: true
}).then((res) => {
uni.navigateTo({
url: '../info/RealInfo'
})
});
},
phoneText(e) {
//
this.page.phone = e.detail.value;
},
send(e) { //
console.log(e);
},
codeText(e) {
//
this.page.code = e.detail.value;
}
}
};
</script>
<style lang="scss">
.inputRow {
display: flex;
margin-left: 30rpx;
margin-right: 30rpx;
margin-bottom: 10rpx;
margin-top: 10rpx;
padding-bottom: 10rpx;
border-bottom: 0.1px #F1F1F1 solid;
align-items: center;
.input {
margin-left: 20rpx;
height: 70rpx;
flex: 1;
font-size: 32rpx;
}
.drawableLeft {
width: 40rpx;
height: 40rpx;
margin: 20rpx;
}
}
.btn {
display: flex;
width: 90%;
height: 80rpx;
flex-direction: column;
background-color: $uni-base-color;
margin-top: 80rpx;
margin-left: auto;
margin-right: auto;
align-items: center;
justify-content: center;
border-radius: 10rpx;
.btnText {
color: #ffffff;
font-size: 33rpx;
}
}
</style>

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save