报名工具小程序初始代码
This commit is contained in:
131
.gitignore
vendored
Normal file
131
.gitignore
vendored
Normal file
@@ -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
Normal file
154
App.vue
Normal file
@@ -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
Normal file
32
common/Back.js
Normal file
@@ -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
Normal file
79
common/ChooseUpload.js
Normal file
@@ -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
Normal file
37
common/Distance.js
Normal file
@@ -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
Normal file
72
common/GameCache.js
Normal file
@@ -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
Normal file
465
common/Http.js
Normal file
@@ -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
Normal file
48
common/PreferenceHelper.js
Normal file
@@ -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
Normal file
5
common/PxToRpxOrPxToUpx.js
Normal file
@@ -0,0 +1,5 @@
|
||||
const px2Upx = (windowWidth, px) => {
|
||||
return px / (windowWidth / 750)
|
||||
}
|
||||
|
||||
export default px2Upx
|
||||
11
common/TextUtils.js
Normal file
11
common/TextUtils.js
Normal file
@@ -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
Normal file
201
common/Time.js
Normal file
@@ -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
Normal file
8
common/Toast.js
Normal file
@@ -0,0 +1,8 @@
|
||||
const toast = (msg) => {
|
||||
uni.showToast({
|
||||
title: msg,
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
|
||||
export default toast
|
||||
12
common/intent.js
Normal file
12
common/intent.js
Normal file
@@ -0,0 +1,12 @@
|
||||
const putWEBExtra = (url) => {
|
||||
return encodeURIComponent(url)
|
||||
}
|
||||
|
||||
const getWEBExtra = (options) => {
|
||||
return decodeURIComponent(options.url)
|
||||
}
|
||||
|
||||
export {
|
||||
putWEBExtra,
|
||||
getWEBExtra
|
||||
}
|
||||
353
common/utils.js
Normal file
353
common/utils.js
Normal file
@@ -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} true:是;false:不是;
|
||||
*/
|
||||
isObject(val) {
|
||||
return Object.prototype.toString.call(val) === '[object Object]' && val !== null && val !== undefined;
|
||||
},
|
||||
|
||||
/**
|
||||
* 判断数据是否是 Array 类型
|
||||
* @param {Any} val 要判断的数据
|
||||
* @returns {Boolean} true:是;false:不是;
|
||||
*/
|
||||
isArray(val) {
|
||||
return Object.prototype.toString.call(val) === '[object Array]';
|
||||
},
|
||||
|
||||
/**
|
||||
* 判断数据是否是 String 类型
|
||||
* @param {Any} val 要判断的数据
|
||||
* @returns {Boolean} true:是;false:不是;
|
||||
*/
|
||||
isString(val) {
|
||||
return Object.prototype.toString.call(val) === '[object String]';
|
||||
},
|
||||
|
||||
/**
|
||||
* 精确判断数据是否是 Date 类型
|
||||
* @param {Any} val 要判断的数据
|
||||
* @returns {Boolean} true:是;false:不是;
|
||||
*/
|
||||
isDate(val) {
|
||||
return Object.prototype.toString.call(val) === '[object Date]';
|
||||
},
|
||||
|
||||
/**
|
||||
* 精确判断数据是否是 Function 类型
|
||||
* @param {Any} val 要判断的数据
|
||||
* @returns {Boolean} true:是;false:不是;
|
||||
*/
|
||||
isFunction(val) {
|
||||
return Object.prototype.toString.call(val) === '[object Function]';
|
||||
},
|
||||
|
||||
/**
|
||||
* 精确判断数据是否是 Number 类型
|
||||
* @param {Any} val 要判断的数据
|
||||
* @returns {Boolean} true:是;false:不是;
|
||||
*/
|
||||
isNumber(val) {
|
||||
return Object.prototype.toString.call(val) === '[object Number]';
|
||||
},
|
||||
|
||||
/**
|
||||
* 精确判断数据是否是 Boolean 类型
|
||||
* @param {Any} val 要判断的数据
|
||||
* @returns {Boolean} true:是;false:不是;
|
||||
*/
|
||||
isBoolean(val) {
|
||||
return Object.prototype.toString.call(val) === '[object Boolean]';
|
||||
},
|
||||
|
||||
/**
|
||||
* 判断 URL 是否是绝对 URL。
|
||||
* @param {String} url 要判断的 URL
|
||||
* @return {Boolean} true:是绝对URL;false:不是绝对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
Normal file
170
common/wxAuthLogin.js
Normal file
@@ -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
Normal file
73
common/wxSilentLogin.js
Normal file
@@ -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
Normal file
76
components/EditText/EditText.vue
Normal file
@@ -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
Normal file
51
components/PublishItem/PublishItem.vue
Normal file
@@ -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
Normal file
585
components/RefreshView/RefreshView.vue
Normal file
@@ -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, // 下拉刷新状态 (inOffset:1, outOffset:2, showLoading:3, endDownScroll:4)
|
||||
upLoadType: 0, // 上拉加载状态:0(loading前),1(loading中),2(没有更多了)
|
||||
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失效,需注意把fixed元素写在mescroll之外
|
||||
},
|
||||
// 是否在加载中
|
||||
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%"则等于windowHeight的10%
|
||||
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;
|
||||
}
|
||||
},
|
||||
// 使用created初始化mescroll对象; 如果用mounted部分css样式编译到H5会失效
|
||||
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为滚动区域
|
||||
// init回调mescroll对象
|
||||
vm.$emit('init', vm.mescroll);
|
||||
|
||||
// 设置高度
|
||||
const sys = uni.getSystemInfoSync();
|
||||
if (sys.windowHeight) vm.windowHeight = sys.windowHeight;
|
||||
if (sys.statusBarHeight) vm.statusBarH = sys.statusBarHeight;
|
||||
// 使down的bottomOffset生效
|
||||
vm.mescroll.setBodyHeight(sys.windowHeight);
|
||||
// mescroll-body在Android小程序下拉会卡顿,无法像mescroll-uni那样通过设置"disableScroll":true解决,只能用动画过渡缓解
|
||||
// #ifdef MP
|
||||
if (sys.platform == "android") vm.downTransition = 'transform 200ms'
|
||||
// #endif
|
||||
|
||||
// 因为使用的是page的scroll,这里需自定义scrollTo
|
||||
vm.mescroll.resetScrollTo((y, t) => {
|
||||
uni.pageScrollTo({
|
||||
scrollTop: y,
|
||||
duration: t
|
||||
})
|
||||
});
|
||||
|
||||
// 具体的界面如果不配置up.toTop.safearea,则取本vue的safearea值
|
||||
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
Normal file
65
components/RefreshView/mescroll-down.css
Normal file
@@ -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
Normal file
79
components/RefreshView/readme.md
Normal file
@@ -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
Normal file
19
components/RefreshView/refresh-option.js
Normal file
@@ -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
Normal file
275
components/RichTextEditor/RichTextEditor.vue
Normal file
@@ -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
Normal file
239
components/RichTextEditor/editor-icon.css
Normal file
File diff suppressed because one or more lines are too long
30
components/RichTextEditor/readme.md
Normal file
30
components/RichTextEditor/readme.md
Normal file
@@ -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
Normal file
108
components/SendCodeItem/SendCodeItem.vue
Normal file
@@ -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
Normal file
245
components/TabLayout/TabLayout.vue
Normal file
@@ -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(使用控件的页面更改data里currentTab值)
|
||||
// 保证能跟着滚动
|
||||
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
Normal file
93
components/UserItem/UserItem.vue
Normal file
@@ -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
Normal file
516
components/biaofun-datetime-picker/biaofun-datetime-picker.vue
Normal file
@@ -0,0 +1,516 @@
|
||||
<!--
|
||||
* @插件:日期时间选择器
|
||||
* @作者:陈万照
|
||||
* @公司:山东标梵互动信息技术有限公司
|
||||
* @官网:http://biaofun.com/
|
||||
* @微信:C207668802
|
||||
* @QQ:207668802
|
||||
* @邮箱: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
Normal file
30
components/biaofun-datetime-picker/使用说明.md
Normal file
@@ -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
Normal file
422
components/city-select/city-select.vue
Normal file
@@ -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
Normal file
36
components/city-select/citySelect.js
Normal file
File diff suppressed because one or more lines are too long
1024
components/city-select/citys.js
Normal file
1024
components/city-select/citys.js
Normal file
File diff suppressed because it is too large
Load Diff
697
components/cropper/cropper.vue
Normal file
697
components/cropper/cropper.vue
Normal file
@@ -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
|
||||
},
|
||||
|
||||
//目标文件的类型。默认值为jpg,jpg:输出jpg格式图片;png:输出png格式图片
|
||||
outputFileType: {
|
||||
type: String,
|
||||
default: 'jpg'
|
||||
},
|
||||
//目标图片的宽高比,默认null,即不限制剪裁宽高比。aspectRatio需大于0
|
||||
aspectRatio: {
|
||||
type: [Number, null],
|
||||
default: null
|
||||
},
|
||||
//最小剪裁尺寸与原图尺寸的比率,默认0.15,即宽度最小剪裁到原图的0.15宽。
|
||||
minBoxWidthRatio: {
|
||||
type: Number,
|
||||
default: 0.15
|
||||
},
|
||||
//同minBoxWidthRatio,当设置aspectRatio时,minBoxHeight值设置无效。minBoxHeight值由minBoxWidth 和 aspectRatio自动计算得到。
|
||||
minBoxHeightRatio: {
|
||||
type: Number,
|
||||
default: 0.15
|
||||
},
|
||||
//剪裁框初始大小比率。默认值0.8,即剪裁框默认宽度为图片宽度的0.8倍。
|
||||
initialBoxWidthRatio: {
|
||||
type: Number,
|
||||
default: 0.8
|
||||
},
|
||||
//同initialBoxWidthRatio,当设置aspectRatio时,initialBoxHeightRatio值设置无效。initialBoxHeightRatio值由initialBoxWidthRatio 和 aspectRatio自动计算得到。
|
||||
initialBoxHeightRatio: {
|
||||
type: Number,
|
||||
default: 0.8
|
||||
},
|
||||
// 保存的模式 None(无限制)ScaleTo500(缩放到指定的尺寸 如 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
Normal file
79
components/cropper/readme.md
Normal file
@@ -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
Normal file
163
components/drop-item/drop-item.vue
Normal file
@@ -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
vendored
Normal file
BIN
components/firstui/.DS_Store
vendored
Normal file
Binary file not shown.
379
components/firstui/fui-button/fui-button.vue
Normal file
379
components/firstui/fui-button/fui-button.vue
Normal file
@@ -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: {
|
||||
//样式类型:primary,success, warning,danger,link,purple,gray
|
||||
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 为:phoneNumber、userInfo
|
||||
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
Normal file
445
components/firstui/fui-dropdown-menu/fui-dropdown-menu.vue
Normal file
@@ -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
|
||||
},
|
||||
//right大于等于0时生效,left失效
|
||||
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
Normal file
154
components/firstui/fui-icon/fui-icon.js
Normal file
@@ -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
Normal file
BIN
components/firstui/fui-icon/fui-icon.ttf
Normal file
Binary file not shown.
93
components/firstui/fui-icon/fui-icon.vue
Normal file
93
components/firstui/fui-icon/fui-icon.vue
Normal file
File diff suppressed because one or more lines are too long
217
components/firstui/fui-list-cell/fui-list-cell.vue
Normal file
217
components/firstui/fui-list-cell/fui-list-cell.vue
Normal file
@@ -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值,上、右、下、左,nvue下padding-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
|
||||
//上边框left值,单位rpx
|
||||
topLeft: {
|
||||
type: [Number, String],
|
||||
default: 0
|
||||
},
|
||||
//上边框right值,单位rpx
|
||||
topRight: {
|
||||
type: [Number, String],
|
||||
default: 0
|
||||
},
|
||||
//下边框left值,单位rpx
|
||||
bottomLeft: {
|
||||
type: [Number, String],
|
||||
default: 32
|
||||
},
|
||||
//下边框right值,单位rpx
|
||||
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
Normal file
85
components/firstui/fui-theme/fui-theme.css
Normal file
@@ -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
Normal file
21
components/grid/grid.vue
Normal file
@@ -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
Normal file
68
components/helang-asyncSwitch/helang-asyncSwitch.vue
Normal file
@@ -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
Normal file
186
components/item-drop/item-drop.vue
Normal file
@@ -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
Normal file
149
components/item-scan/item-scan.vue
Normal file
@@ -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
Normal file
41
components/loading/loading.vue
Normal file
@@ -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
Normal file
56
components/mescroll-uni/components/mescroll-down.css
Normal file
@@ -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
Normal file
54
components/mescroll-uni/components/mescroll-down.vue
Normal file
@@ -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, // 下拉状态(inOffset:1, outOffset:2, showLoading:3, endDownScroll:4)
|
||||
rate: Number // 下拉比率 (inOffset: rate<1; outOffset: rate>=1)
|
||||
},
|
||||
computed: {
|
||||
// 支付宝小程序需写成计算属性,prop定义default仍报错
|
||||
mOption() {
|
||||
return this.option || {}
|
||||
},
|
||||
// 是否在加载中
|
||||
isDownLoading() {
|
||||
return this.type === 3
|
||||
},
|
||||
// 旋转的角度
|
||||
downRotate() {
|
||||
return 'rotate(' + 360 * this.rate + 'deg)'
|
||||
},
|
||||
// 文本提示
|
||||
downText() {
|
||||
switch (this.type) {
|
||||
case 1:
|
||||
return this.mOption.textInOffset;
|
||||
case 2:
|
||||
return this.mOption.textOutOffset;
|
||||
case 3:
|
||||
return this.mOption.textLoading;
|
||||
case 4:
|
||||
return this.mOption.textLoading;
|
||||
default:
|
||||
return this.mOption.textInOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@import "./mescroll-down.css";
|
||||
</style>
|
||||
92
components/mescroll-uni/components/mescroll-empty.vue
Normal file
92
components/mescroll-uni/components/mescroll-empty.vue
Normal file
@@ -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
Normal file
80
components/mescroll-uni/components/mescroll-top.vue
Normal file
@@ -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: {
|
||||
// 支付宝小程序需写成计算属性,prop定义default仍报错
|
||||
mOption() {
|
||||
return this.option || {}
|
||||
},
|
||||
// 优先显示左边
|
||||
left() {
|
||||
return this.mOption.left ? this.addUnit(this.mOption.left) : 'auto';
|
||||
},
|
||||
// 右边距离 (优先显示左边)
|
||||
right() {
|
||||
return this.mOption.left ? 'auto' : this.addUnit(this.mOption.right);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addUnit(num) {
|
||||
if (!num) return 0;
|
||||
if (typeof num === 'number') return num + 'rpx';
|
||||
return num
|
||||
},
|
||||
toTopClick() {
|
||||
this.$emit('input', false); // 使v-model生效
|
||||
this.$emit('click'); // 派发点击事件
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* 回到顶部的按钮 */
|
||||
.mescroll-totop {
|
||||
z-index: 9990;
|
||||
position: fixed !important;
|
||||
/* 加上important避免编译到H5,在多mescroll中定位失效 */
|
||||
right: 20rpx;
|
||||
bottom: 120rpx;
|
||||
width: 72rpx;
|
||||
height: auto;
|
||||
border-radius: 50%;
|
||||
opacity: 0;
|
||||
transition: opacity 0.5s;
|
||||
/* 过渡 */
|
||||
margin-bottom: var(--window-bottom);
|
||||
/* css变量 */
|
||||
}
|
||||
|
||||
/* 适配 iPhoneX */
|
||||
.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
Normal file
47
components/mescroll-uni/components/mescroll-up.css
Normal file
@@ -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
Normal file
39
components/mescroll-uni/components/mescroll-up.vue
Normal file
@@ -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 // 上拉加载的状态:0(loading前),1(loading中),2(没有更多了)
|
||||
},
|
||||
computed: {
|
||||
// 支付宝小程序需写成计算属性,prop定义default仍报错
|
||||
mOption() {
|
||||
return this.option || {};
|
||||
},
|
||||
// 加载中
|
||||
isUpLoading() {
|
||||
return this.type === 1;
|
||||
},
|
||||
// 没有更多了
|
||||
isUpNoMore() {
|
||||
return this.type === 2;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@import './mescroll-up.css';
|
||||
</style>
|
||||
15
components/mescroll-uni/mescroll-body.css
Normal file
15
components/mescroll-uni/mescroll-body.css
Normal file
@@ -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
Normal file
309
components/mescroll-uni/mescroll-body.vue
Normal file
@@ -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, // 下拉刷新状态 (inOffset:1, outOffset:2, showLoading:3, endDownScroll:4)
|
||||
upLoadType: 0, // 上拉加载状态:0(loading前),1(loading中),2(没有更多了)
|
||||
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失效,需注意把fixed元素写在mescroll之外
|
||||
},
|
||||
// 是否在加载中
|
||||
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%"则等于windowHeight的10%
|
||||
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); // 派发点击回到顶部按钮的回调
|
||||
}
|
||||
},
|
||||
// 使用created初始化mescroll对象; 如果用mounted部分css样式编译到H5会失效
|
||||
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为滚动区域
|
||||
// init回调mescroll对象
|
||||
vm.$emit('init', vm.mescroll);
|
||||
|
||||
// 设置高度
|
||||
const sys = uni.getSystemInfoSync();
|
||||
if (sys.windowHeight) vm.windowHeight = sys.windowHeight;
|
||||
if (sys.statusBarHeight) vm.statusBarHeight = sys.statusBarHeight;
|
||||
// 使down的bottomOffset生效
|
||||
vm.mescroll.setBodyHeight(sys.windowHeight);
|
||||
// mescroll-body在Android小程序下拉会卡顿,无法像mescroll-uni那样通过设置"disableScroll":true解决,只能用动画过渡缓解
|
||||
// #ifdef MP
|
||||
if (sys.platform == "android") vm.downTransition = 'transform 200ms'
|
||||
// #endif
|
||||
|
||||
// 因为使用的是page的scroll,这里需自定义scrollTo
|
||||
vm.mescroll.resetScrollTo((y, t) => {
|
||||
uni.pageScrollTo({
|
||||
scrollTop: y,
|
||||
duration: t
|
||||
})
|
||||
});
|
||||
|
||||
// 具体的界面如果不配置up.toTop.safearea,则取本vue的safearea值
|
||||
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
Normal file
60
components/mescroll-uni/mescroll-mixins.js
Normal file
@@ -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
Normal file
34
components/mescroll-uni/mescroll-uni-option.js
Normal file
@@ -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
Normal file
33
components/mescroll-uni/mescroll-uni.css
Normal file
@@ -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
Normal file
869
components/mescroll-uni/mescroll-uni.js
Normal file
@@ -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
Normal file
386
components/mescroll-uni/mescroll-uni.vue
Normal file
@@ -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), // 随机生成mescroll的id(不能数字开头,否则找不到元素)
|
||||
downHight: 0, //下拉刷新: 容器高度
|
||||
downRate: 0, // 下拉比率(inOffset: rate<1; outOffset: rate>=1)
|
||||
downLoadType: 4, // 下拉刷新状态 (inOffset:1, outOffset:2, showLoading:3, endDownScroll:4)
|
||||
upLoadType: 0, // 上拉加载状态:0(loading前),1(loading中),2(没有更多了)
|
||||
isShowEmpty: false, // 是否显示空布局
|
||||
isShowToTop: false, // 是否显示回到顶部按钮
|
||||
scrollTop: 0, // 滚动条的位置
|
||||
scrollAnim: false, // 是否开启滚动动画
|
||||
windowTop: 0, // 可使用窗口的顶部位置
|
||||
windowBottom: 0, // 可使用窗口的底部位置
|
||||
windowHeight: 0, // 可使用窗口的高度
|
||||
statusBarHeight: 0, // 状态栏高度
|
||||
isSafearea: false, // 支持安全区
|
||||
scrollToViewId: '' // 滚动到指定view的id
|
||||
}
|
||||
},
|
||||
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: { // 是否通过fixed固定mescroll的高度, 默认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失效,需注意把fixed元素写在mescroll之外
|
||||
},
|
||||
// 是否在加载中
|
||||
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%"则等于windowHeight的10%
|
||||
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();
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
// 使用created初始化mescroll对象; 如果用mounted部分css样式编译到H5会失效
|
||||
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
|
||||
// init回调mescroll对象
|
||||
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;
|
||||
// 使down的bottomOffset生效
|
||||
vm.mescroll.setBodyHeight(sys.windowHeight);
|
||||
|
||||
// 因为使用的是scrollview,这里需自定义scrollTo
|
||||
vm.mescroll.resetScrollTo((y, t) => {
|
||||
vm.scrollAnim = (t !== 0); // t为0,则不使用动画过渡
|
||||
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,则取本vue的safearea值
|
||||
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
Normal file
23
components/mescroll-uni/mixins/mescroll-comp.js
Normal file
@@ -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
Normal file
48
components/mescroll-uni/mixins/mescroll-more-item.js
Normal file
@@ -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
Normal file
56
components/mescroll-uni/mixins/mescroll-more.js
Normal file
@@ -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
Normal file
20
components/noData/noData.vue
Normal file
@@ -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
Normal file
135
components/pick-regions/pick-regions.vue
Normal file
@@ -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
Normal file
10330
components/pick-regions/regions.json
Normal file
File diff suppressed because it is too large
Load Diff
459
components/sl-filter/filter-view.vue
Normal file
459
components/sl-filter/filter-view.vue
Normal file
@@ -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)) { // 把所有默认的为false的点为true
|
||||
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])
|
||||
// 把所有不是默认的为true的点为false
|
||||
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
Normal file
20
components/sl-filter/iconfont/iconfont.css
Normal file
@@ -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
Normal file
122
components/sl-filter/popup-layer.vue
Normal file
@@ -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', // 方向 top,bottom,left,right
|
||||
},
|
||||
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
Normal file
301
components/sl-filter/sl-filter.vue
Normal file
@@ -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
Normal file
173
components/tabRefresh/tabRefresh.vue
Normal file
@@ -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
Normal file
112
components/tabTop/tabTop.vue
Normal file
@@ -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
Normal file
45
components/uni-datetime-picker/keypress.js
Normal file
@@ -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
Normal file
804
components/uni-datetime-picker/uni-datetime-picker.vue
Normal file
@@ -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)) //兼容 iOS、safari 日期格式
|
||||
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') //兼容 iOS、safari 日期格式
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
end: {
|
||||
handler(newVal) {
|
||||
this.parseDatetimeRange(this.fixIosDateFormat(newVal), 'end') //兼容 iOS、safari 日期格式
|
||||
},
|
||||
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()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 解析可选择时间范围 start、end,年月日字符串、时间戳
|
||||
* @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();
|
||||
},
|
||||
|
||||
//兼容 iOS、safari 日期格式
|
||||
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
Normal file
243
components/uni-popup-dialog/uni-popup-dialog.vue
Normal file
@@ -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
Normal file
22
components/uni-popup/message.js
Normal file
@@ -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
Normal file
25
components/uni-popup/popup.js
Normal file
@@ -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
Normal file
294
components/uni-popup/uni-popup.vue
Normal file
@@ -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: 顶部弹出层;bottom:底部弹出层;center:全屏弹出层
|
||||
// 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
Normal file
280
components/uni-transition/uni-transition.vue
Normal file
@@ -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
Normal file
22
index.html
Normal file
@@ -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
Normal file
99
main.js
Normal file
@@ -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
Normal file
68
manifest.json
Normal file
@@ -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
Normal file
255
pages.json
Normal file
@@ -0,0 +1,255 @@
|
||||
{
|
||||
"pages": [ //pages数组中第一项表示应用启动页,参考:https://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
Normal file
164
pages/city/CitySelectActivity.vue
Normal file
@@ -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
Normal file
482
pages/find/ActivityDetail.vue
Normal file
@@ -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 // 报名模式(0为个人,1为团体)
|
||||
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
Normal file
255
pages/find/ActivityList.vue
Normal file
@@ -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
Normal file
136
pages/find/BaoMingListActivity.vue
Normal file
@@ -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
Normal file
217
pages/find/TeamEnroll.vue
Normal file
@@ -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
Normal file
364
pages/home/AddFragment.vue
Normal file
@@ -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
Normal file
326
pages/home/FindFragment.vue
Normal file
@@ -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
Normal file
299
pages/home/UserFragment.vue
Normal file
@@ -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
Normal file
312
pages/index/ArenaDetailActivity.vue
Normal file
@@ -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
Normal file
136
pages/index/BaoMingListActivity.vue
Normal file
@@ -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
Normal file
146
pages/index/BindPhone.vue
Normal file
@@ -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
Normal file
394
pages/index/DetailActivity.vue
Normal file
@@ -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
Normal file
470
pages/index/InputHappyGameAreaActivity.vue
Normal file
@@ -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
Normal file
128
pages/index/InputHappyGameLimitActivity.vue
Normal file
@@ -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('radio发生change事件,携带value值为:', this.result.checked1)
|
||||
// console.log('radio发生change事件,携带value值为:', 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
Normal file
127
pages/index/UserAuthentication.vue
Normal file
@@ -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 have changed in this diff Show More
Reference in New Issue
Block a user