巴林闲侠
3 years ago
278 changed files with 26570 additions and 0 deletions
@ -0,0 +1,12 @@ |
|||
# http://editorconfig.org |
|||
root = true |
|||
|
|||
[*] |
|||
indent_style = space |
|||
indent_size = 2 |
|||
charset = utf-8 |
|||
trim_trailing_whitespace = true |
|||
insert_final_newline = true |
|||
|
|||
[*.md] |
|||
trim_trailing_whitespace = false |
@ -0,0 +1,3 @@ |
|||
module.exports = { |
|||
'extends': ['taro/react'] |
|||
} |
@ -0,0 +1,6 @@ |
|||
dist/ |
|||
deploy_versions/ |
|||
.temp/ |
|||
.rn_temp/ |
|||
node_modules/ |
|||
.DS_Store |
@ -0,0 +1,10 @@ |
|||
// babel-preset-taro 更多选项和默认值:
|
|||
// https://github.com/NervJS/taro/blob/next/packages/babel-preset-taro/README.md
|
|||
module.exports = { |
|||
presets: [ |
|||
['taro', { |
|||
framework: 'react', |
|||
ts: false |
|||
}] |
|||
] |
|||
} |
@ -0,0 +1,9 @@ |
|||
module.exports = { |
|||
env: { |
|||
NODE_ENV: '"development"' |
|||
}, |
|||
defineConstants: { |
|||
}, |
|||
mini: {}, |
|||
h5: {} |
|||
} |
@ -0,0 +1,93 @@ |
|||
import path from 'path'; |
|||
|
|||
const config = { |
|||
projectName: 'fs-wxapp-taro3', |
|||
date: '2020-12-7', |
|||
designWidth: 750, |
|||
deviceRatio: { |
|||
640: 2.34 / 2, |
|||
750: 1, |
|||
828: 1.81 / 2 |
|||
}, |
|||
sourceRoot: 'src', |
|||
outputRoot: 'dist', |
|||
plugins: [], |
|||
defineConstants: { |
|||
}, |
|||
alias: { |
|||
'@/components': path.resolve(__dirname, '..', 'src/components'), |
|||
'@/actions': path.resolve(__dirname, '..', 'src/actions'), |
|||
'@/utils': path.resolve(__dirname, '..', 'src/utils'), |
|||
'@/hooks': path.resolve(__dirname, '..', 'src/hooks'), |
|||
'@/services': path.resolve(__dirname, '..', 'src/services'), |
|||
'@/constants': path.resolve(__dirname, '..', 'src/constants'), |
|||
}, |
|||
copy: { |
|||
patterns: [ |
|||
{ from: 'src/components/vant-weapp/wxs', to: 'dist/components/vant-weapp/wxs' }, |
|||
{ from: 'src/components/vant-weapp/common/style', to: 'dist/components/vant-weapp/common/style' }, |
|||
{ from: 'src/components/vant-weapp/common/index.wxss', to: 'dist/components/vant-weapp/common/index.wxss' }, |
|||
{ from: 'src/components/vant-weapp/button/index.wxs', to: 'dist/components/vant-weapp/button/index.wxs' }, |
|||
{ from: 'src/components/vant-weapp/icon/index.wxs', to: 'dist/components/vant-weapp/icon/index.wxs' }, |
|||
{ from: 'src/components/vant-weapp/loading/index.wxs', to: 'dist/components/vant-weapp/loading/index.wxs' }, |
|||
{ from: 'src/components/vant-weapp/popup/index.wxs', to: 'dist/components/vant-weapp/popup/index.wxs' }, |
|||
{ from: 'src/components/vant-weapp/cell/index.wxs', to: 'dist/components/vant-weapp/cell/index.wxs' }, |
|||
{ from: 'src/components/vant-weapp/dropdown-menu/index.wxs', to: 'dist/components/vant-weapp/dropdown-menu/index.wxs' }, |
|||
{ from: 'src/components/vant-weapp/transition/index.wxs', to: 'dist/components/vant-weapp/transition/index.wxs' }, |
|||
{ from: 'src/components/vant-weapp/tag/index.wxs', to: 'dist/components/vant-weapp/tag/index.wxs' }, |
|||
{ from: 'src/components/vant-weapp/row/index.wxs', to: 'dist/components/vant-weapp/row/index.wxs' }, |
|||
{ from: 'src/components/vant-weapp/col/index.wxs', to: 'dist/components/vant-weapp/col/index.wxs' }, |
|||
], |
|||
options: { |
|||
} |
|||
}, |
|||
framework: 'react', |
|||
mini: { |
|||
postcss: { |
|||
pxtransform: { |
|||
enable: true, |
|||
config: { |
|||
selectorBlackList: [/van-/] |
|||
} |
|||
}, |
|||
url: { |
|||
enable: true, |
|||
config: { |
|||
limit: 1024 // 设定转换尺寸上限
|
|||
} |
|||
}, |
|||
cssModules: { |
|||
enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true
|
|||
config: { |
|||
namingPattern: 'module', // 转换模式,取值为 global/module
|
|||
generateScopedName: '[name]__[local]___[hash:base64:5]' |
|||
} |
|||
} |
|||
} |
|||
}, |
|||
h5: { |
|||
publicPath: '/', |
|||
staticDirectory: 'static', |
|||
postcss: { |
|||
autoprefixer: { |
|||
enable: true, |
|||
config: { |
|||
} |
|||
}, |
|||
cssModules: { |
|||
enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true
|
|||
config: { |
|||
namingPattern: 'module', // 转换模式,取值为 global/module
|
|||
generateScopedName: '[name]__[local]___[hash:base64:5]' |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
module.exports = function (merge) { |
|||
if (process.env.NODE_ENV === 'development') { |
|||
return merge({}, config, require('./dev')) |
|||
} |
|||
return merge({}, config, require('./prod')) |
|||
} |
@ -0,0 +1,18 @@ |
|||
module.exports = { |
|||
env: { |
|||
NODE_ENV: '"production"' |
|||
}, |
|||
defineConstants: { |
|||
}, |
|||
mini: {}, |
|||
h5: { |
|||
/** |
|||
* 如果h5端编译后体积过大,可以使用webpack-bundle-analyzer插件对打包体积进行分析。 |
|||
* 参考代码如下: |
|||
* webpackChain (chain) { |
|||
* chain.plugin('analyzer') |
|||
* .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin, []) |
|||
* } |
|||
*/ |
|||
} |
|||
} |
@ -0,0 +1,14 @@ |
|||
{ |
|||
"compilerOptions": { |
|||
"experimentalDecorators": true, |
|||
"baseUrl": ".", |
|||
"paths": { |
|||
"@/components/*": ["./src/components/*"], |
|||
"@/actions/*": ["./src/actions/*"], |
|||
"@/utils/*": ["./src/utils/*"], |
|||
"@/hooks/*": ["./src/hooks/*"], |
|||
"@/services/*": ["./src/services/*"], |
|||
"@constants/*": ["./src/constants/*"], |
|||
} |
|||
} |
|||
} |
File diff suppressed because it is too large
@ -0,0 +1,69 @@ |
|||
{ |
|||
"name": "fs-wxapp-taro3", |
|||
"version": "1.0.0", |
|||
"private": true, |
|||
"description": "飞尚小程序taro3模板", |
|||
"templateInfo": { |
|||
"name": "redux", |
|||
"typescript": false, |
|||
"css": "sass" |
|||
}, |
|||
"scripts": { |
|||
"build:weapp": "taro build --type weapp", |
|||
"build:swan": "taro build --type swan", |
|||
"build:alipay": "taro build --type alipay", |
|||
"build:tt": "taro build --type tt", |
|||
"build:h5": "taro build --type h5", |
|||
"build:rn": "taro build --type rn", |
|||
"build:qq": "taro build --type qq", |
|||
"build:jd": "taro build --type jd", |
|||
"build:quickapp": "taro build --type quickapp", |
|||
"start": "npm run build:weapp -- --watch", |
|||
"dev:weapp": "npm run build:weapp -- --watch", |
|||
"dev:swan": "npm run build:swan -- --watch", |
|||
"dev:alipay": "npm run build:alipay -- --watch", |
|||
"dev:tt": "npm run build:tt -- --watch", |
|||
"dev:h5": "npm run build:h5 -- --watch", |
|||
"dev:rn": "npm run build:rn -- --watch", |
|||
"dev:qq": "npm run build:qq -- --watch", |
|||
"dev:jd": "npm run build:jd -- --watch", |
|||
"dev:quickapp": "npm run build:quickapp -- --watch" |
|||
}, |
|||
"browserslist": [ |
|||
"last 3 versions", |
|||
"Android >= 4.1", |
|||
"ios >= 8" |
|||
], |
|||
"author": "", |
|||
"license": "MIT", |
|||
"dependencies": { |
|||
"@antv/f2-canvas": "^1.0.5", |
|||
"@antv/wx-f2": "^1.1.4", |
|||
"@babel/runtime": "^7.7.7", |
|||
"@tarojs/components": "3.1.4", |
|||
"@tarojs/react": "3.1.4", |
|||
"@tarojs/runtime": "3.1.4", |
|||
"@tarojs/taro": "3.1.4", |
|||
"dayjs": "^1.9.8", |
|||
"deepmerge": "^4.0.0", |
|||
"react": "^16.10.0", |
|||
"react-dom": "^16.10.0", |
|||
"swr": "^0.5.4", |
|||
"taro-ui": "^3.0.0-alpha.10" |
|||
}, |
|||
"devDependencies": { |
|||
"@babel/core": "^7.8.0", |
|||
"@tarojs/mini-runner": "3.1.4", |
|||
"@tarojs/webpack-runner": "3.1.4", |
|||
"@types/react": "^16.0.0", |
|||
"@types/webpack-env": "^1.16.0", |
|||
"babel-preset-taro": "3.1.4", |
|||
"blueimp-md5": "^2.12.0", |
|||
"eslint": "^7.22.0", |
|||
"eslint-config-taro": "3.1.4", |
|||
"eslint-plugin-import": "^2.22.1", |
|||
"eslint-plugin-react": "^7.23.1", |
|||
"eslint-plugin-react-hooks": "^4.2.0", |
|||
"stylelint": "^13.12.0" |
|||
} |
|||
} |
@ -0,0 +1,46 @@ |
|||
{ |
|||
"miniprogramRoot": "dist/", |
|||
"projectname": "四好公路", |
|||
"description": "项目配置文件,详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html", |
|||
"appid": "wx3c0c46dcf49a85be", |
|||
"setting": { |
|||
"urlCheck": false, |
|||
"es6": false, |
|||
"postcss": false, |
|||
"minified": false, |
|||
"coverView": true, |
|||
"lazyloadPlaceholderEnable": false, |
|||
"preloadBackgroundData": false, |
|||
"autoAudits": false, |
|||
"uglifyFileName": false, |
|||
"uploadWithSourceMap": true, |
|||
"enhance": true, |
|||
"showShadowRootInWxmlPanel": true, |
|||
"packNpmManually": false, |
|||
"packNpmRelationList": [], |
|||
"minifyWXSS": true, |
|||
"useStaticServer": true, |
|||
"showES6CompileOption": false, |
|||
"checkInvalidKey": true, |
|||
"babelSetting": { |
|||
"ignore": [], |
|||
"disablePlugins": [], |
|||
"outputPath": "" |
|||
}, |
|||
"disableUseStrict": false, |
|||
"useCompilerPlugins": false, |
|||
"minifyWXML": true |
|||
}, |
|||
"compileType": "miniprogram", |
|||
"libVersion": "2.25.1", |
|||
"srcMiniprogramRoot": "dist/", |
|||
"packOptions": { |
|||
"ignore": [], |
|||
"include": [] |
|||
}, |
|||
"condition": {}, |
|||
"editorSetting": { |
|||
"tabIndent": "insertSpaces", |
|||
"tabSize": 2 |
|||
} |
|||
} |
@ -0,0 +1,7 @@ |
|||
{ |
|||
"projectname": "%E5%9B%9B%E5%A5%BD%E5%85%AC%E8%B7%AF", |
|||
"setting": { |
|||
"compileHotReLoad": true |
|||
}, |
|||
"description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html" |
|||
} |
@ -0,0 +1,54 @@ |
|||
|
|||
import Taro from '@tarojs/taro'; |
|||
import request from '../services/request'; |
|||
|
|||
function dealError(error) { |
|||
Taro.showToast({ |
|||
title: error, |
|||
icon: 'none', |
|||
duration: 1500 |
|||
}); |
|||
throw new Error(error); |
|||
} |
|||
export const login = (url, data) => { |
|||
return request.post(url, data, { hideErrorToast: true }).then(res => { |
|||
if (res.statusCode == 200) { |
|||
//let obj = {};
|
|||
// res.cookies.map(t=>{
|
|||
// const fsiota = t.split(';').find(v=> v.includes('fsiota')).split('=');
|
|||
// obj[fsiota[0]] = fsiota[1];
|
|||
// })
|
|||
// console.log(obj);
|
|||
// Taro.setStorageSync("cookie", {
|
|||
// fsiota: obj.fsiota,
|
|||
// sig: obj['fsiota.sig']
|
|||
// })
|
|||
Taro.setStorageSync('token', res.data.token) |
|||
Taro.setStorageSync('userInfo', res.data); |
|||
return res.data; |
|||
} else { |
|||
dealError(res.data.message || '请求出错'); |
|||
} |
|||
}, err => { |
|||
dealError(err.message || '请求出错'); |
|||
}); |
|||
|
|||
}; |
|||
|
|||
|
|||
export const logout = (url, data) => { |
|||
return request.post(url, data).then(res => { |
|||
if (res.statusCode == 200 || res.statusCode == 204) { |
|||
Taro.clearStorage(); |
|||
return res.data; |
|||
} else { |
|||
throw new Error(res.data.message || '登出失败'); |
|||
} |
|||
}).catch(err => { |
|||
throw new Error(err.message || '请求出错'); |
|||
}); |
|||
}; |
|||
|
|||
|
|||
|
|||
|
@ -0,0 +1,12 @@ |
|||
import useSWR, { useSWRInfinite } from 'swr'; |
|||
import { |
|||
getIndustryUrl, |
|||
} from '@/services/api'; |
|||
import { fetcher, useCommonSwr } from './fetcher'; |
|||
|
|||
//获取行业列表
|
|||
export const useIndustry = () => { |
|||
const url = getIndustryUrl(); |
|||
return useCommonSwr(url); |
|||
|
|||
} |
@ -0,0 +1,41 @@ |
|||
import request from '@/services/request'; |
|||
import useSWR, { useSWRInfinite } from 'swr'; |
|||
export const PAGE_SIZE = 10; |
|||
|
|||
//swr 返回获取数据函数
|
|||
export const fetcher = ({ url, data }, cb) => { |
|||
return async function () { |
|||
const res = await request.get(url, data); |
|||
const payload = cb ? cb(res.data) : res.data; |
|||
if (res.statusCode === 200 || res.statusCode === 204) { |
|||
return payload; |
|||
} else { |
|||
//向外抛出错误
|
|||
throw new Error(res.data.message || '请求出错'); |
|||
} |
|||
} |
|||
} |
|||
//分页获取数据
|
|||
export const pageFetcher = async (url) => { |
|||
const res = await request.get(url); |
|||
const payload = res.data; |
|||
if (res.statusCode === 200 || res.statusCode === 204) { |
|||
if(payload && payload.rows){ |
|||
return payload.rows |
|||
} |
|||
return payload; |
|||
} else { |
|||
//向外抛出错误
|
|||
throw new Error(res.data.message || '请求出错'); |
|||
} |
|||
} |
|||
|
|||
export const useCommonSwr = (url) => { |
|||
const { data, error } = useSWR(url, fetcher({ url })); |
|||
return { |
|||
data: data, |
|||
isLoading: !error && !data, |
|||
error: error |
|||
} |
|||
|
|||
} |
@ -0,0 +1,49 @@ |
|||
export default { |
|||
pages: [ |
|||
'pages/home/index', |
|||
'pages/user/index', |
|||
'pages/auth/login/login' |
|||
], |
|||
permission: { |
|||
"scope.userLocation": { |
|||
"desc": '需要获取您的地理位置' |
|||
} |
|||
}, |
|||
window: { |
|||
backgroundTextStyle: 'light', |
|||
navigationBarBackgroundColor: "#2C66F3", |
|||
navigationBarTitleText: '智慧交通', |
|||
backgroundColor: "#f2f2f2", |
|||
}, |
|||
tabBar: { |
|||
color: '#B2B2B2', |
|||
selectedColor: '#0080EE', |
|||
list: [ |
|||
{ |
|||
'pagePath': 'pages/home/index', |
|||
'iconPath': 'static/img/tabbar/shouye.png', |
|||
'selectedIconPath': 'static/img/tabbar/shouye-active.png', |
|||
'text': '首页' |
|||
}, |
|||
{ |
|||
'pagePath': 'pages/user/index', |
|||
'iconPath': 'static/img/tabbar/wode.png', |
|||
'selectedIconPath': 'static/img/tabbar/wode-active.png', |
|||
'text': '我的' |
|||
}, |
|||
] |
|||
}, |
|||
usingComponents: { |
|||
'chart-wx': 'components/wx-chart/chart', |
|||
"van-icon": "components/vant-weapp/icon/index", |
|||
"van-dropdown-menu": "components/vant-weapp/dropdown-menu/index", |
|||
"van-dropdown-item": "components/vant-weapp/dropdown-item/index", |
|||
"van-tag": "components/vant-weapp/tag/index", |
|||
"van-button": "components/vant-weapp/button/index", |
|||
"van-row": "components/vant-weapp/row/index", |
|||
"van-col": "components/vant-weapp/col/index", |
|||
"van-transition": "components/vant-weapp/transition/index", |
|||
"ec-canvas": "components/ec-canvas/ec-canvas" |
|||
|
|||
} |
|||
} |
@ -0,0 +1,52 @@ |
|||
import React, { Component } from 'react'; |
|||
import Taro from '@tarojs/taro'; |
|||
import { SWRConfig } from 'swr'; |
|||
import 'taro-ui/dist/style/index.scss'; |
|||
//import appUpdate from './utils/appUpdate'; |
|||
import './app.scss'; |
|||
|
|||
class App extends Component { |
|||
|
|||
|
|||
componentDidMount() { |
|||
const userInfo = Taro.getStorageSync('userInfo') || null; |
|||
const token = Taro.getStorageSync('token') || null; |
|||
if (!userInfo || !token) { |
|||
Taro.showModal({ |
|||
title: '提示', |
|||
content: '未获取用户信息,请重新登录', |
|||
showCancel: false, |
|||
success: (res) => { |
|||
if (res.confirm) { |
|||
Taro.reLaunch({ |
|||
url: '/pages/auth/login/login' |
|||
}); |
|||
} |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
|
|||
componentDidShow() { } |
|||
|
|||
componentDidHide() { } |
|||
|
|||
componentDidCatchError() { } |
|||
|
|||
// 在 App 类中的 render() 函数没有实际作用 |
|||
// 请勿修改此函数 |
|||
render() { |
|||
return ( |
|||
<SWRConfig |
|||
value={{ |
|||
suspense: false, |
|||
dedupingInterval: 2000 |
|||
}} |
|||
> |
|||
{this.props.children} |
|||
</SWRConfig> |
|||
) |
|||
} |
|||
} |
|||
|
|||
export default App |
@ -0,0 +1,256 @@ |
|||
//$color-brand: #2579C7 !default; |
|||
@import 'src/styles/theme'; |
|||
/* 引入 Taro UI 默认样式 */ |
|||
@import "~taro-ui/dist/style/index.scss"; |
|||
|
|||
|
|||
//全局默认字体, 蚂蚁金服 |
|||
page,view,button{ |
|||
font-family: "Monospaced Number", "Chinese Quote", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, sans-serif; |
|||
} |
|||
|
|||
/* 修改 taro ui样式 */ |
|||
// .at-list::after { |
|||
// border-bottom-width: 0; |
|||
// } |
|||
/* 弹性布局 */ |
|||
.flex{ |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
} |
|||
.flex-start{ |
|||
justify-content: flex-start; |
|||
} |
|||
.flex-col{ |
|||
flex-direction: column; |
|||
} |
|||
.flex-between{ |
|||
justify-content: space-between; |
|||
} |
|||
|
|||
/* 自定义png图标颜色 */ |
|||
$icon-size: 50px; |
|||
.color-icon{ |
|||
display: inline-block; |
|||
width: $icon-size; |
|||
height: $icon-size; |
|||
overflow: hidden; |
|||
.img{ |
|||
display: block; |
|||
position: relative; |
|||
left: -$icon-size; |
|||
border-right: $icon-size solid transparent; |
|||
filter: drop-shadow($icon-size 0 0 $color-brand); |
|||
} |
|||
} |
|||
|
|||
/* 字体省略号 */ |
|||
.text---{ |
|||
overflow: hidden; |
|||
text-overflow:ellipsis; |
|||
white-space: nowrap; |
|||
} |
|||
.break-all{ |
|||
word-break: break-all |
|||
} |
|||
/* 字体大小 */ |
|||
.fs6{font-size: 6px}.fs8{font-size: 8px}.fs10{font-size:10px;}.fs11{font-size:11px;}.fs12{font-size:12px;}.fs13{font-size:13px;}.fs14{font-size:14px;}.fs15{font-size:15px;}.fs16{font-size:16px;}.fs18{font-size:18px;}.fs20{font-size:20px;}.fs24{font-size:24px;}.fs28{font-size:28px;}.fs30{font-size:30px;}.fs32{font-size:32px;}.fs36{font-size:36px;}.fs40{font-size:40px;}.fs48{font-size:48px;}.fs50{font-size:50px;}.fs60{font-size:60px;} |
|||
.fw-400{ |
|||
font-weight: 400; |
|||
} |
|||
.fw-500{ |
|||
font-weight: 500; |
|||
} |
|||
.fw-600{ |
|||
font-weight: 600; |
|||
} |
|||
.fw-700{ |
|||
font-weight: 700; |
|||
} |
|||
|
|||
/* 字体颜色 */ |
|||
.c-111{ |
|||
color: #111; |
|||
} |
|||
.c-222{ |
|||
color: #222; |
|||
} |
|||
.c-333{ |
|||
color: #333; |
|||
} |
|||
.c-444{ |
|||
color: #444; |
|||
} |
|||
.c-555{ |
|||
color: #555; |
|||
} |
|||
.c-666{ |
|||
color: #666; |
|||
} |
|||
.c-777{ |
|||
color: #777; |
|||
} |
|||
.c-888{ |
|||
color: #888; |
|||
} |
|||
.c-999{ |
|||
color: #999; |
|||
} |
|||
.c-aaa{ |
|||
color: #aaa; |
|||
} |
|||
.c-ccc{ |
|||
color: #ccc; |
|||
} |
|||
.c-fff{ |
|||
color: #fff; |
|||
} |
|||
.c-b2{ |
|||
color: #b2b2b2; |
|||
} |
|||
.c-f00{ |
|||
color: #f00; |
|||
} |
|||
.c-m{ |
|||
color: #2F54FF; |
|||
} |
|||
.c-m-bg{ |
|||
background-color: #2F54FF; |
|||
} |
|||
.t-center{ |
|||
text-align: center; |
|||
} |
|||
|
|||
button::after{ |
|||
border: none; |
|||
} |
|||
|
|||
//背景色 |
|||
.bc-e00{ |
|||
background-color: #e00; |
|||
} |
|||
|
|||
/* 边距 */ |
|||
.pr10{ |
|||
padding-right: 10px; |
|||
} |
|||
.pt5{ |
|||
padding-top: 5px; |
|||
} |
|||
.pb30{ |
|||
padding-bottom: 30px; |
|||
} |
|||
.ptr120{ |
|||
padding-top: 120rpx; |
|||
} |
|||
.ptr160{ |
|||
padding-top: 160rpx; |
|||
} |
|||
|
|||
.pb10{ |
|||
padding-bottom: 10px; |
|||
} |
|||
.pl5{ |
|||
padding-left: 5px; |
|||
} |
|||
.pl10{ |
|||
padding-left: 10px; |
|||
} |
|||
.pl20{ |
|||
padding-left: 20px; |
|||
} |
|||
.pl30{ |
|||
padding-left: 30px; |
|||
} |
|||
.p-lr30{ |
|||
padding-left: 30px; |
|||
padding-right: 30px; |
|||
} |
|||
.mt7{ |
|||
margin-top: 7px; |
|||
} |
|||
.mt8{ |
|||
margin-top: 8px; |
|||
} |
|||
.mt10{ |
|||
margin-top: 10px; |
|||
} |
|||
.mt20{ |
|||
margin-top: 20px; |
|||
} |
|||
.mt30{ |
|||
margin-top: 30px; |
|||
} |
|||
.mt40{ |
|||
margin-top: 40px; |
|||
} |
|||
.mt60{ |
|||
margin-top: 60px; |
|||
} |
|||
.mt100{ |
|||
margin-top: 200px; |
|||
} |
|||
.mt200{ |
|||
margin-top: 200px; |
|||
} |
|||
.mt300{ |
|||
margin-top: 300px; |
|||
} |
|||
.mb8{ |
|||
margin-bottom: 8px; |
|||
} |
|||
.mb6{ |
|||
margin-bottom: 6px; |
|||
} |
|||
.mb10{ |
|||
margin-bottom: 10px; |
|||
} |
|||
.mb20{ |
|||
margin-bottom: 20px; |
|||
} |
|||
.mb30{ |
|||
margin-bottom: 30px; |
|||
} |
|||
.mb50{ |
|||
margin-bottom: 50px; |
|||
} |
|||
.ml10{ |
|||
margin-left: 10px; |
|||
} |
|||
.ml20{ |
|||
margin-left: 20px; |
|||
} |
|||
.ml30{ |
|||
margin-left: 30px; |
|||
} |
|||
.ml40{ |
|||
margin-left: 40px; |
|||
} |
|||
.ml50{ |
|||
margin-left: 50px; |
|||
} |
|||
.ml60{ |
|||
margin-left: 60px; |
|||
} |
|||
|
|||
//伪类阴影 父元素需设置z-index和position: relative; |
|||
.shadow{ |
|||
position: relative; |
|||
&::before{ |
|||
content:''; |
|||
position: absolute; |
|||
bottom: -20px; |
|||
left: 50%; |
|||
transform: translateX(-50%); |
|||
width:90%; |
|||
height:68px; |
|||
background:rgba(0,0,0,0.06); |
|||
border-radius:9px; |
|||
filter:blur(15px); |
|||
z-index: -2; |
|||
} |
|||
} |
|||
.loadmore{ |
|||
background: #F7F7FA; |
|||
} |
@ -0,0 +1,39 @@ |
|||
import React, { Component } from 'react'; |
|||
import { View, } from '@tarojs/components'; |
|||
import { AtCalendar } from 'taro-ui'; |
|||
|
|||
import './index.scss'; |
|||
|
|||
class MyCalenda extends Component { |
|||
constructor(props) { |
|||
super(props); |
|||
this.state = { |
|||
//date: null,
|
|||
}; |
|||
} |
|||
|
|||
|
|||
|
|||
selectDate(e) { |
|||
this.props.onSelectDate(e.value); |
|||
} |
|||
|
|||
render() { |
|||
const { date } = this.props; |
|||
return ( |
|||
<View> |
|||
<View> |
|||
<View className='fw-bold'>时间筛选</View> |
|||
<View className='time'> |
|||
<View className='w-200'>{date && date.start ? date.start : '起始时间'}</View> |
|||
<View>|</View> |
|||
<View className='w-200'>{date && date.end ? date.end : '结束时间'}</View> |
|||
</View> |
|||
</View> |
|||
<AtCalendar currentDate={date && date.start && date.end ? { start: date.start.replace(/-/g, '/'), end: date.end.replace(/-/g, '/') } : Date.now()} onSelectDate={this.selectDate.bind(this)} isMultiSelect /> |
|||
</View> |
|||
); |
|||
} |
|||
} |
|||
|
|||
export default MyCalenda; |
@ -0,0 +1,11 @@ |
|||
.w-200{ |
|||
width: 200rpx |
|||
} |
|||
.fw-bold{ |
|||
font-weight: bold |
|||
} |
|||
.time{ |
|||
display: flex; |
|||
justify-content: space-around; |
|||
text-align: center; |
|||
} |
@ -0,0 +1,24 @@ |
|||
import React, { Component } from 'react'; |
|||
import { View } from '@tarojs/components'; |
|||
|
|||
class Chart extends Component { |
|||
constructor(props) { |
|||
super(props); |
|||
this.state = { |
|||
|
|||
}; |
|||
} |
|||
componentDidMount() { |
|||
|
|||
} |
|||
|
|||
render() { |
|||
return ( |
|||
<View> |
|||
<chart-wx chartData={this.props.chartData} simpleChart={this.props.simpleChart}></chart-wx> |
|||
</View> |
|||
); |
|||
} |
|||
} |
|||
|
|||
export default Chart; |
@ -0,0 +1,90 @@ |
|||
/* |
|||
* create by Xumeng 2020/12/31 |
|||
* 时间日期筛选框包装 |
|||
*/ |
|||
import React, { Component } from 'react' |
|||
import { View } from '@tarojs/components'; |
|||
import { AtFloatLayout, AtList, AtListItem } from "taro-ui"; |
|||
import 'dayjs/locale/zh-cn' |
|||
import DatePicker from './index' |
|||
import './filterPicker.scss' |
|||
|
|||
export default class FilterPicker extends Component { |
|||
constructor() { |
|||
super(...arguments) |
|||
this.state = { |
|||
isOpened: false, |
|||
chevronDown: true, |
|||
showValue: '' |
|||
} |
|||
} |
|||
componentWillMount() { |
|||
} |
|||
|
|||
componentDidMount() { |
|||
} |
|||
|
|||
componentWillUnmount() {} |
|||
|
|||
componentDidShow() {} |
|||
|
|||
componentDidHide() {} |
|||
|
|||
|
|||
|
|||
handleInitial = () => { |
|||
|
|||
} |
|||
|
|||
handleConfirm = () => { |
|||
|
|||
} |
|||
|
|||
handleCancel = () => { |
|||
|
|||
} |
|||
|
|||
handleClose = () => { |
|||
this.setState({ |
|||
isOpened : false, |
|||
chevronDown: true |
|||
}) |
|||
} |
|||
|
|||
handleOpen = () => { |
|||
this.setState({ |
|||
isOpened : true, |
|||
chevronDown: false |
|||
}) |
|||
} |
|||
|
|||
|
|||
|
|||
render() { |
|||
const { isOpened, chevronDown, showValue } = this.state |
|||
const { dateTime = [{ mode: 'year', unit: '年' },{ mode: 'month', unit: '月' },{ mode: 'day', unit: '日' }] , mode, formatShow, value} = this.props |
|||
return ( |
|||
<View> |
|||
{/* <View className='picker-cell' onClick={this.handleOpen}> |
|||
<View className='label'>{showValue}</View> |
|||
{chevronDown ? <View className='at-icon at-icon-chevron-down' /> : <View className='at-icon at-icon-chevron-up' />} |
|||
</View> */} |
|||
<AtList hasBorder={false}> |
|||
<AtListItem title='请选择日期区间' extraText={showValue} onClick={this.handleOpen} /> |
|||
</AtList> |
|||
<AtFloatLayout isOpened={isOpened} onClose={this.handleClose}> |
|||
<DatePicker |
|||
dateTime={dateTime} |
|||
onInitial={this.handleInitial} |
|||
onConfirm={this.handleConfirm} |
|||
onCancel={this.handleCancel} |
|||
mode={mode || 'format'} |
|||
formatShow={formatShow ||'YYYY-MM-DD'} |
|||
value={value || [0, 0, 0]} |
|||
/> |
|||
</AtFloatLayout> |
|||
</View> |
|||
|
|||
) |
|||
} |
|||
} |
@ -0,0 +1,6 @@ |
|||
.picker-cell{ |
|||
min-width: 210px; |
|||
height: 44px; |
|||
display: flex; |
|||
align-items: center; |
|||
} |
@ -0,0 +1,126 @@ |
|||
// 输出的结果包含item为显示的可选项,value为默认显示值
|
|||
// res为返回的结果,time为返回的默认时间
|
|||
// 整个代码流程为:
|
|||
// 1.如果指定了offset,则根据offset属性将time做偏移作为time(为了实现当前8:30,需要默认选择9点这样的需求)
|
|||
// 2.如果指定了selected,则根据第一步的结果寻找大于或者等于time的可选时间点作为新的time,
|
|||
// 如果无法匹配到合适的selected(findIndex返回-1),则设定time为下个周期的开始时间
|
|||
// 如果同时指定了selectedDefault,则设定time为selected的selectedDefault对应的值
|
|||
// 3.根据mode来区分开年/日---月/时/分/秒---range,对三种mode分别进行单独的处理
|
|||
|
|||
const format = (dateTime, dayjs) => { |
|||
// res为返回的结果,time为返回的默认时间
|
|||
let res = { value: [], item: [] }, |
|||
time = dayjs.clone() |
|||
// 注释掉这里是因为这里判断fields的同时只做了增加到下一个时间点的操作,完全可以用fields和offset搭配实现select下一个时间点
|
|||
// dateTime
|
|||
// .filter((key) => key.fields > 1)
|
|||
// .map((item) => {
|
|||
// time = time.add(item.fields, item.mode)
|
|||
// })
|
|||
|
|||
// offset: 设定time偏移量(常用于设定下一小时或者下一天等)
|
|||
dateTime |
|||
.filter((key) => key.offset) |
|||
.map((item) => { |
|||
time = time.add(item.offset, item.mode) |
|||
}) |
|||
|
|||
// selected: 可指定当前模式下只选择有效范围内的哪几个元素,例如24小时内,只选择8点、12点、16点
|
|||
// 寻找大于或者等于time的最近的可选择时间点
|
|||
dateTime |
|||
.filter((key) => key.selected) |
|||
.map((item) => { |
|||
const { selected, mode, selectedDefault } = item |
|||
const index = selected.findIndex((value) => value >= time.get(mode)) |
|||
if (index === -1) { |
|||
// 如果未找到可选时间点,就设定time为下个周期(如第二天)的开始时间
|
|||
time = time.add(getInverval(mode) - time.get(mode), mode) |
|||
} else { |
|||
// 如果找到可选时间点,就设定time为这个时间点
|
|||
time = time.set(mode, selected[index]) |
|||
} |
|||
// 如果设定了selectedDefault,则将time设定为default对应的时间点
|
|||
if (selectedDefault !== undefined) { |
|||
time = time.set(mode, selected[selectedDefault]) |
|||
} |
|||
}) |
|||
// 此处针对offset、selected和selectedDefault属性处理完成
|
|||
|
|||
dateTime.map((item) => { |
|||
const { mode, selected, selectedDefault, unit = '', fields = 1 } = item |
|||
// 此处区分年、天有别于其他周期在于年和天都不是标准周期(每个月不同天,年一直在增长)
|
|||
if (['month', 'hour', 'minute', 'second'].includes(mode)) { |
|||
// 设定显示的可选项
|
|||
// [...Array(getInverval('hour')).keys()]获取24个小时的数字的数组:[0,1,...,23]
|
|||
res.item.push( |
|||
[...Array(getInverval(mode)).keys()] |
|||
// 按照fields来平均分割这些数字
|
|||
.filter((i) => i % fields === 0) |
|||
// 如果设置了selected属性,只返回特定的数字
|
|||
.filter((i) => { |
|||
if (selected) return selected.includes(i) |
|||
else return true |
|||
}) |
|||
// 在所有的数字后面添加单位(unit);由于月份为0-11,需要转换为1-12
|
|||
.map((i) => (mode === 'month' ? i + 1 : i) + unit) |
|||
) |
|||
// 根据time的值和selected的匹配来设定默认值为数组中第几个,当未指定selected或者定位到下个周期时,设定index为-1
|
|||
const index = selected && selected.findIndex((value) => value === time.get(mode)) |
|||
// 设定显示的默认选择项
|
|||
res.value.push(selected ? (index < 0 ? 0 : index) : ~~(time.get(mode) / fields)) |
|||
} else if (['year', 'day'].includes(mode)) { |
|||
// 根据
|
|||
res.item.push(timeFormat(item, dayjs)) |
|||
// 寻找time和dayjs时间点的差值作为默认选择值
|
|||
res.value.push(selectedDefault || time.startOf(mode).diff(dayjs.startOf(mode), mode)) |
|||
} else { |
|||
return |
|||
} |
|||
}) |
|||
return res |
|||
} |
|||
|
|||
// 对于年和日,获取显示的可选项
|
|||
const timeFormat = (time, dayjs) => { |
|||
const { mode, duration = 30, unit = '', humanity = false } = time, |
|||
res = [] |
|||
for (let i = 0; i < duration; i++) { |
|||
let timeItem |
|||
// 利用dayjs[convertDay(mode)]来动态实现dayjs.date()或dayjs.year()的方法,用computedTime来实现可选择项的生成
|
|||
const computedTime = dayjs.add(i, mode) |
|||
if (humanity && mode === 'day') { |
|||
timeItem = `${computedTime.month() + 1}月${computedTime.date()}日` |
|||
if (i < 3) { |
|||
// 今天明天后天这三天明确显示出来
|
|||
timeItem = (i === 0 ? '' : timeItem + ' ') + convertDate(i) |
|||
} else { |
|||
// 在日期后添加星期几,更人性化
|
|||
timeItem += ` ${convertWeek(computedTime.day())}` |
|||
} |
|||
} else { |
|||
timeItem = computedTime.get(convertDay(mode)) + unit |
|||
} |
|||
res.push(timeItem) |
|||
} |
|||
return res |
|||
} |
|||
|
|||
const convertWeek = (item) => { |
|||
return { 0: '周日', 1: '周一', 2: '周二', 3: '周三', 4: '周四', 5: '周五', 6: '周六' }[item] |
|||
} |
|||
|
|||
const convertDate = (item) => { |
|||
return { 0: '今天', 1: '明天', 2: '后天' }[item] |
|||
} |
|||
|
|||
// dayjs的get分为day of week和date of month,故需要将day转换为date使用
|
|||
const convertDay = (item) => { |
|||
return { day: 'date' }[item] || item |
|||
} |
|||
|
|||
const getInverval = (item) => { |
|||
return { hour: 24, minute: 60, second: 60, month: 12 }[item] |
|||
} |
|||
|
|||
export default format |
|||
|
@ -0,0 +1,188 @@ |
|||
/* |
|||
* create by Xumeng 2020/12/31 |
|||
* 时间日期选择器通用组件 |
|||
*/ |
|||
import React, { Component } from 'react' |
|||
import { View, PickerView, PickerViewColumn, Button } from '@tarojs/components'; |
|||
import dayjs from 'dayjs'; |
|||
import 'dayjs/locale/zh-cn'; |
|||
import customParseFormat from 'dayjs/plugin/customParseFormat' |
|||
import format from './format' |
|||
import './index.scss' |
|||
|
|||
dayjs.extend(customParseFormat) |
|||
export default class Index extends Component { |
|||
constructor() { |
|||
super(...arguments) |
|||
this.state = { |
|||
itemHeight: `height: 40px;`, |
|||
value: [], |
|||
source: [], |
|||
markMultiDateTime: false, |
|||
isOpened: false, |
|||
chevronDown: true, |
|||
showValue: this.props.paickerText ? this.props.paickerText : '请选择' |
|||
} |
|||
} |
|||
componentWillMount() { |
|||
const { dateTime, start, value: _value } = this.props |
|||
let markMultiDateTime = false |
|||
if (dateTime && Array.isArray(dateTime)) { |
|||
dateTime.map((dateTimeItem) => { |
|||
//判断一维数组还是二维数组,分别对应单组选择和两组选择 |
|||
if (Array.isArray(dateTimeItem)) { |
|||
markMultiDateTime = true |
|||
// 取得格式化计算之后的结果 |
|||
const source = dateTimeItem && format(dateTimeItem, dayjs(start)) |
|||
// 后续需要source而不需要item的原因是source可能是多纬数组,每个纬度里面包含自己的item和value |
|||
this.setState((state) => ({ |
|||
...state, |
|||
source: [...state.source, source], |
|||
value: [...state.value, source.value], |
|||
})) |
|||
// this.setState((state) => ({ ...state, value: [...state.value, source.value] })) |
|||
} |
|||
}) |
|||
if (!markMultiDateTime) { |
|||
const source = dateTime && format(dateTime, dayjs(start)) |
|||
|
|||
this.setState((state) => ({ |
|||
...state, |
|||
source: [...state.source, source], |
|||
value: [...state.value, source.value], |
|||
})) |
|||
// this.setState((state) => ({ ...state, source: [...state.source, source] })) |
|||
// this.setState((state) => ({ ...state, value: [...state.value, source.value] })) |
|||
} |
|||
this.setState((state) => ({ ...state, markMultiDateTime })) |
|||
} |
|||
|
|||
_value && this.setState((state) => ({ ...state, ...{ value: markMultiDateTime ? _value : [_value] } })) |
|||
} |
|||
|
|||
componentDidMount() { |
|||
this.onInitial() |
|||
} |
|||
|
|||
componentWillUnmount() {} |
|||
|
|||
componentDidShow() {} |
|||
|
|||
componentDidHide() {} |
|||
|
|||
onChange = (e, index) => { |
|||
console.log(e.detail.value) |
|||
const _value = [...this.state.value] |
|||
_value[index] = e.detail.value |
|||
this.setState({ value: _value }) |
|||
} |
|||
|
|||
onInitial = () => { |
|||
const { value, markMultiDateTime } = this.state |
|||
const { onInitial, mode, formatShow } = this.props |
|||
// 根据返回格式(mode)来格式化选中的时间 |
|||
onInitial && onInitial(this.getDayjs(mode), markMultiDateTime ? value : value[0]) |
|||
} |
|||
|
|||
onConfirm = () => { |
|||
const { value, markMultiDateTime } = this.state |
|||
const { onConfirm, mode, formatShow } = this.props; |
|||
const newValue = this.getDayjs(mode); |
|||
let formatNewValue = ''; |
|||
if(formatShow && mode == 'format'){ |
|||
|
|||
if(Array.isArray(newValue)){ |
|||
formatNewValue = newValue.map(v=> dayjs(v).format(formatShow)).join(' - '); |
|||
}else{ |
|||
formatNewValue = dayjs(newValue).format(formatShow); |
|||
|
|||
} |
|||
this.setState({ |
|||
showValue: formatNewValue |
|||
}) |
|||
} |
|||
onConfirm && onConfirm(formatNewValue, markMultiDateTime ? value : value[0]) |
|||
} |
|||
|
|||
// 根据可选项和当前选择索引返回已选中的时间 |
|||
getDayjs = (mode = 'unix') => { |
|||
let { source, value, markMultiDateTime } = this.state |
|||
const { dateTime } = this.props |
|||
const res = [] |
|||
// 此处遍历dateTime和遍历source的区别在于一维数组还是二维数组 |
|||
for (let i = 0; i < source.length; i++) { |
|||
let time = '', |
|||
token = '' |
|||
// source[i].item.length为可选项的列数 |
|||
for (let j = 0; j < source[i].item.length; j++) { |
|||
// source[i].item[j]为每一列的数据组成的数组,value[i][j]为对应这列数组的选中值 |
|||
const select = source[i].item[j][value[i][j]] |
|||
// 对'今天'这个值进行特殊处理,其他直接返回当前的选择字符串 |
|||
time += (select === '今天' ? dayjs().format('M月D日') : select) + '-' |
|||
// 对于二维数组取i、j;对于一维数组取j |
|||
const item = markMultiDateTime ? dateTime[i][j] : dateTime[j] |
|||
token += (item.format || this.getToken(item.mode)) + '-' |
|||
} |
|||
res.push(dayjs(time, token)[mode]()) |
|||
} |
|||
return markMultiDateTime ? res : res[0] |
|||
} |
|||
|
|||
onCancel = () => { |
|||
const { onCancel } = this.props |
|||
onCancel && onCancel() |
|||
} |
|||
|
|||
|
|||
// 标准格式化选择器 |
|||
getToken = (mode) => { |
|||
return { |
|||
year: 'YYYY年', |
|||
month: 'M月', |
|||
day: 'D日', |
|||
hour: 'H时', |
|||
minute: 'm分', |
|||
second: 's秒', |
|||
}[mode] |
|||
} |
|||
|
|||
render() { |
|||
const { source, value, itemHeight } = this.state; |
|||
//console.log(source) |
|||
//const { dateTime = [] } = this.props |
|||
return ( |
|||
<View |
|||
className='date-picker-page' |
|||
//style={{ width: dateTime.reduce((acc, val) => acc.concat(val), []).length < 4 ? '80%' : '100%' }} |
|||
> |
|||
<View className='section'> |
|||
{source.map((element, index) => ( |
|||
<PickerView |
|||
key={'element' + index} |
|||
indicator-style={itemHeight} |
|||
value={value[index]} |
|||
onChange={(e) => this.onChange(e, index)} |
|||
// 使用acc.concat将多维数组打平成一维数组再求数组长度 |
|||
> |
|||
{element.item.map((item, elementIndex) => ( |
|||
<PickerViewColumn key={elementIndex}> |
|||
{item.map((time) => ( |
|||
<View key={time}>{time}</View> |
|||
))} |
|||
</PickerViewColumn> |
|||
))} |
|||
</PickerView> |
|||
))} |
|||
</View> |
|||
<View className='handle'> |
|||
<Button className='cancel' type='default' size='default' onClick={this.onCancel}> |
|||
取消 |
|||
</Button> |
|||
<Button className='confirm' type='primary' size='default' onClick={this.onConfirm}> |
|||
确定 |
|||
</Button> |
|||
</View> |
|||
</View> |
|||
) |
|||
} |
|||
} |
@ -0,0 +1,47 @@ |
|||
|
|||
.date-picker-page{ |
|||
width: 100%; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
display: flex; |
|||
.section { |
|||
display: flex; |
|||
width: 100%; |
|||
justify-content: center; |
|||
align-items: center; |
|||
picker-view { |
|||
width: 100%; |
|||
height: 240Px; |
|||
picker-view-column { |
|||
text-align: center; |
|||
font-size: 26px; |
|||
view { |
|||
//height: 100Px; |
|||
line-height: 40Px; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
.handle { |
|||
display: flex; |
|||
flex-direction: row; |
|||
justify-content: center; |
|||
align-items: center; |
|||
height: 100px; |
|||
button { |
|||
height: 70px; |
|||
line-height: 70px; |
|||
font-size: 32px; |
|||
padding: 0 80px; |
|||
margin: 0 20px; |
|||
} |
|||
.confirm { |
|||
background-color: #07C160; |
|||
} |
|||
.cancel { |
|||
background-color: #F2F2F2; |
|||
color: #1AAD19; |
|||
} |
|||
} |
|||
} |
|||
|
@ -0,0 +1,870 @@ |
|||
import React,{ Component } from 'react' |
|||
import Taro from '@tarojs/taro' |
|||
import { View, Text, Block, ScrollView } from '@tarojs/components'; |
|||
import { DatePicker } from '@/components'; |
|||
import PropTypes from 'prop-types'; |
|||
import './index.scss' |
|||
|
|||
export default class FilterDropdown extends Component { |
|||
|
|||
state = { |
|||
subData: [], //菜单数据 |
|||
menu: [], //顶部横条数据 |
|||
showPage: -1, //菜单页面显示/隐藏动画控制 |
|||
pageState: [], //页面的状态 |
|||
activeMenuArr: [], //UI状态 |
|||
shadowActiveMenuArr: [], //记录选中 |
|||
defaultActive: [], |
|||
triangleDeg: [], //小三角形的翻转动画控制 |
|||
isShowMask: false, //遮罩层显示/隐藏动画控制 |
|||
maskVisibility: false, //遮罩层显示/隐藏状态 |
|||
//滚动区域定位 |
|||
firstScrollInto: 0, |
|||
secondScrollInto: 0, |
|||
componentTop: 0,//组件top |
|||
isReadNewSelect: false, |
|||
filterData: null, |
|||
defaultSelected: null |
|||
} |
|||
|
|||
componentWillMount() { |
|||
this.setState({ |
|||
filterData: this.props.filterData, |
|||
}, () => { |
|||
this.initMenu(); //filterData重新赋值初始化菜单 |
|||
}) |
|||
// if (this.props.defaultSelected.length == 0) { |
|||
// return; |
|||
// } |
|||
// this.setState({ |
|||
// defaultActive: JSON.parse(JSON.stringify(this.props.defaultSelected)), |
|||
// activeMenuArr: JSON.parse(JSON.stringify(this.props.defaultSelected)), |
|||
// shadowActiveMenuArr: JSON.parse(JSON.stringify(this.props.defaultSelected)) |
|||
// }, () => { |
|||
// }) |
|||
|
|||
} |
|||
|
|||
componentDidMount() { |
|||
|
|||
} |
|||
|
|||
componentWillUnmount() { } |
|||
|
|||
componentDidShow() { } |
|||
|
|||
componentDidHide() { } |
|||
|
|||
// componentDidUpdate(prevProps,prevState) { |
|||
// if (prevProps.filterData !== this.state.filterData) { |
|||
// // this.initData(); |
|||
// this.setState({ |
|||
// filterData: prevProps.filterData, |
|||
// }, () => { |
|||
// this.initMenu(); //filterData重新赋值初始化菜单 |
|||
// }) |
|||
// } |
|||
// if (prevProps.defaultSelected !== this.state.defaultSelected) { |
|||
// this.setState({ |
|||
// defaultSelected: prevProps.defaultSelected, |
|||
// }, () => { |
|||
// // this.initData(); |
|||
// if (prevProps.defaultSelected.length == 0) { |
|||
// return; |
|||
// } |
|||
// this.setState({ |
|||
// defaultActive: JSON.parse(JSON.stringify(prevProps.defaultSelected)), |
|||
// activeMenuArr: JSON.parse(JSON.stringify(prevProps.defaultSelected)), |
|||
// shadowActiveMenuArr: JSON.parse(JSON.stringify(prevProps.defaultSelected)) |
|||
// }, () => { |
|||
// if (this.props.updateMenuName) { |
|||
// this.setMenuName(); |
|||
// } |
|||
// }) |
|||
|
|||
// }) |
|||
|
|||
// } |
|||
// } |
|||
|
|||
initMenu = () => { |
|||
let tmpMenuActiveArr = []; |
|||
let tmpMenu = []; |
|||
let { filterData, triangleDeg, pageState, activeMenuArr } = this.state; |
|||
const { defaultSelected } = this.props; |
|||
let newFilterData = filterData.map(item=>{ |
|||
tmpMenu.push({ |
|||
name: item.name || item?.submenu[0]?.name, |
|||
type: item.type |
|||
}); |
|||
//初始化选中项数组-ui状态 |
|||
tmpMenuActiveArr.push(this.processActive(item)); |
|||
triangleDeg.push(0); |
|||
pageState.push(false); |
|||
//递归处理子菜单数据 |
|||
let tmpitem = this.processSubMenu(item); |
|||
return tmpitem; |
|||
|
|||
}) |
|||
//初始化选中项数组 |
|||
tmpMenuActiveArr = defaultSelected?.length > 0 ? defaultSelected : activeMenuArr.length > 0 ? activeMenuArr : tmpMenuActiveArr; |
|||
//console.log(tmpMenuActiveArr) |
|||
this.setState({ |
|||
menu: tmpMenu, |
|||
triangleDeg, |
|||
pageState, |
|||
filterData: newFilterData, |
|||
//加载菜单数据 |
|||
subData: newFilterData, |
|||
activeMenuArr: JSON.parse(JSON.stringify(tmpMenuActiveArr)), |
|||
shadowActiveMenuArr: JSON.parse(JSON.stringify(tmpMenuActiveArr)), |
|||
}, () => { |
|||
if (this.props.updateMenuName) { |
|||
this.setMenuName(); |
|||
} |
|||
}) |
|||
} |
|||
|
|||
setMenuName = () => { |
|||
const { activeMenuArr, subData } = this.state; |
|||
let menu = this.state.menu |
|||
activeMenuArr.map((item,i)=>{ |
|||
if (Array.isArray(item) && typeof (item[0]) !== 'object') { |
|||
let tmpsub = false; |
|||
if (item.length > 0 && item[0] != null) { |
|||
tmpsub = subData[i].submenu[item[0]]; |
|||
if (item.length > 1 && item[1] != null) { |
|||
tmpsub = tmpsub.submenu[item[1]]; |
|||
if (item.length > 2 && item[2] != null) { |
|||
tmpsub = tmpsub.submenu[item[2]]; |
|||
} |
|||
} |
|||
} else { |
|||
tmpsub = false; |
|||
} |
|||
if (tmpsub) { |
|||
menu[i].name = tmpsub.name |
|||
} |
|||
} |
|||
}) |
|||
this.setState({ |
|||
menu |
|||
}) |
|||
} |
|||
//展开更多 |
|||
showMoreSub = (index) => { |
|||
let subData = this.subData |
|||
subData[this.state.showPage].submenu[this.state.activeMenuArr[this.state.showPage][0]].submenu[index].showAllSub = true; |
|||
this.setState({ |
|||
subData |
|||
}, () => { |
|||
this.forceUpdate(); |
|||
}) |
|||
} |
|||
|
|||
//选中 |
|||
selectHierarchyMenu = (page_index, level1_index, level2_index, level3_index) => { |
|||
let activeMenuArr = this.state.activeMenuArr |
|||
//读取记录 |
|||
if (level1_index != null && level2_index == null && level3_index == null && this.state.shadowActiveMenuArr[page_index][0] == |
|||
level1_index) { |
|||
activeMenuArr.splice(page_index, 1, JSON.parse(JSON.stringify(this.state.shadowActiveMenuArr[page_index]))); |
|||
this.setState({ |
|||
activeMenuArr |
|||
}) |
|||
} else { |
|||
activeMenuArr[page_index].splice(0, 1, level1_index); |
|||
this.setState({ |
|||
activeMenuArr |
|||
}, () => { |
|||
activeMenuArr = this.state.activeMenuArr; |
|||
if (level2_index != null || activeMenuArr[page_index].length >= 2) { |
|||
activeMenuArr[page_index].splice(1, 1, level2_index) |
|||
} else { |
|||
activeMenuArr[page_index].splice(1, 1) |
|||
} |
|||
if (level3_index != null || activeMenuArr[page_index].length >= 3) { |
|||
activeMenuArr[page_index].splice(2, 1, level3_index) |
|||
} else { |
|||
activeMenuArr[page_index].splice(2, 1) |
|||
} |
|||
this.setState({ |
|||
activeMenuArr |
|||
}) |
|||
}) |
|||
} |
|||
this.setState({ |
|||
activeMenuArr |
|||
}, () => { |
|||
console.log(this.state.activeMenuArr) |
|||
//写入结果 |
|||
if (level3_index != null || level2_index != null || (level1_index != null && this.state.subData[page_index].submenu[level1_index].submenu.length == 0) |
|||
) { |
|||
let sub = this.state.subData[page_index].submenu[level1_index].submenu[level2_index]; |
|||
if (this.props.updateMenuName) { |
|||
var menu = this.state.menu |
|||
menu[page_index].name = (level3_index != null && sub.submenu[level3_index].name) || (level2_index != null && sub.name) || this.state.subData[page_index].submenu[level1_index].name; |
|||
this.setState({ |
|||
menu |
|||
}) |
|||
} |
|||
var shadowActiveMenuArr = this.state.shadowActiveMenuArr |
|||
shadowActiveMenuArr[page_index] = this.state.activeMenuArr[page_index]; |
|||
this.setState({ |
|||
shadowActiveMenuArr: shadowActiveMenuArr |
|||
}, () => { |
|||
console.log(this.state.shadowActiveMenuArr) |
|||
this.togglePage(this.state.showPage); |
|||
}) |
|||
|
|||
} |
|||
}) |
|||
|
|||
} |
|||
|
|||
//写入结果,筛选 |
|||
setFilterData = (page_index) => { |
|||
var shadowActiveMenuArr = this.state.shadowActiveMenuArr |
|||
shadowActiveMenuArr[page_index] = JSON.parse(JSON.stringify(this.state.activeMenuArr[page_index])); |
|||
this.setState({ |
|||
shadowActiveMenuArr |
|||
}, () => { |
|||
this.togglePage(this.state.showPage); |
|||
}) |
|||
|
|||
} |
|||
|
|||
//重置结果和ui,筛选 |
|||
resetFilterData = (page_index) => { |
|||
let tmpArr = []; |
|||
let level = this.state.shadowActiveMenuArr[page_index].length; |
|||
while (level > 0) { |
|||
tmpArr.push([]); |
|||
let box = this.state.subData[page_index].submenu[level - 1].submenu; |
|||
for (let i = 0; i < box.length; i++) { |
|||
let subData = this.state.subData |
|||
subData[page_index].submenu[level - 1].submenu[i].selected = false; |
|||
this.setState({ |
|||
subData |
|||
}) |
|||
} |
|||
level--; |
|||
} |
|||
var activeMenuArr = this.state.activeMenuArr |
|||
activeMenuArr[page_index] = JSON.parse(JSON.stringify(tmpArr)); |
|||
this.setState({ |
|||
activeMenuArr |
|||
}, () => { |
|||
this.forceUpdate(); |
|||
}) |
|||
} |
|||
|
|||
//选中筛选类label-UI状态 |
|||
selectFilterLabel = (page_index, box_index, label_index) => { |
|||
let find_index = this.state.activeMenuArr[page_index][box_index].indexOf(label_index); |
|||
let activeMenuArr, subData |
|||
activeMenuArr = this.state.activeMenuArr |
|||
subData = this.state.subData |
|||
if (find_index > -1) { |
|||
activeMenuArr[page_index][box_index].splice(find_index, 1); |
|||
subData[page_index].submenu[box_index].submenu[label_index].selected = false; |
|||
this.setState({ |
|||
activeMenuArr, |
|||
subData |
|||
}, () => { |
|||
this.forceUpdate(); |
|||
}) |
|||
} else { |
|||
activeMenuArr[page_index][box_index].push(label_index); |
|||
subData[page_index].submenu[box_index].submenu[label_index].selected = true; |
|||
this.setState({ |
|||
activeMenuArr, |
|||
subData |
|||
}, () => { |
|||
this.forceUpdate(); |
|||
}) |
|||
} |
|||
} |
|||
|
|||
//选中单选类label-UI状态 |
|||
selectRadioLabel = (page_index, box_index, label_index) => { |
|||
let activeIndex = this.state.activeMenuArr[page_index][box_index][0]; |
|||
let activeMenuArr, subData |
|||
activeMenuArr = this.state.activeMenuArr |
|||
subData = this.state.subData |
|||
if (activeIndex == label_index) { |
|||
subData[page_index].submenu[box_index].submenu[activeIndex].selected = false; |
|||
activeMenuArr[page_index][box_index][0] = null; |
|||
this.setState({ |
|||
activeMenuArr, |
|||
subData |
|||
}, () => { |
|||
this.forceUpdate(); |
|||
}) |
|||
} else { |
|||
if (activeIndex != null && activeIndex < this.state.subData[page_index].submenu[box_index].submenu.length) { |
|||
subData[page_index].submenu[box_index].submenu[activeIndex].selected = false; |
|||
} |
|||
subData[page_index].submenu[box_index].submenu[label_index].selected = true; |
|||
activeMenuArr[page_index][box_index][0] = label_index; |
|||
this.setState({ |
|||
activeMenuArr, |
|||
subData |
|||
}, () => { |
|||
this.forceUpdate(); |
|||
}) |
|||
} |
|||
} |
|||
|
|||
//菜单开关 |
|||
togglePage = (index) => { |
|||
|
|||
if (index == this.state.showPage) { |
|||
this.hidePageLayer(true); |
|||
this.hideMask(); |
|||
this.setState({ |
|||
showPage: -1 |
|||
}) |
|||
} else { |
|||
if (this.state.showPage > -1) { |
|||
this.hidePageLayer(false); |
|||
} |
|||
this.showPageLayer(index); |
|||
this.showMask(); |
|||
} |
|||
} |
|||
|
|||
//hide遮罩层 |
|||
hideMask = () => { |
|||
this.setState({ |
|||
isShowMask: false |
|||
}, () => { |
|||
setTimeout(() => { |
|||
this.setState({ |
|||
maskVisibility: false |
|||
}) |
|||
}, 200); |
|||
}) |
|||
} |
|||
|
|||
//show遮罩层 |
|||
showMask = () => { |
|||
this.setState({ |
|||
maskVisibility: true |
|||
}, () => { |
|||
Taro.nextTick(() => { |
|||
setTimeout(() => { |
|||
this.setState({ |
|||
isShowMask: true |
|||
}) |
|||
}, 0); |
|||
}) |
|||
}) |
|||
} |
|||
//hide菜单页 |
|||
hidePageLayer = (isAnimation) => { |
|||
let { triangleDeg, showPage, pageState } = this.state; |
|||
triangleDeg[showPage] = 0; |
|||
if (isAnimation) { |
|||
setTimeout(() => { |
|||
pageState.splice(showPage, 1, false); |
|||
this.setState({ |
|||
pageState, |
|||
triangleDeg, |
|||
firstScrollInto: null, |
|||
secondScrollInto: null |
|||
}) |
|||
}, 200); |
|||
this.confirm(); |
|||
} else { |
|||
pageState.splice(showPage, 1, false) |
|||
this.setState({ |
|||
pageState, |
|||
triangleDeg, |
|||
firstScrollInto: null, |
|||
secondScrollInto: null |
|||
}) |
|||
} |
|||
// this.setState({ |
|||
// triangleDeg, |
|||
// firstScrollInto: null, |
|||
// secondScrollInto: null |
|||
// }, () => { |
|||
// let tmpIndex = this.state.showPage; |
|||
// let pageState = this.state.pageState |
|||
// if (isAnimation) { |
|||
// setTimeout(() => { |
|||
// pageState.splice(tmpIndex, 1, false); |
|||
// this.setState({ |
|||
// pageState |
|||
// }) |
|||
// }, 200); |
|||
// this.confirm(); |
|||
// } else { |
|||
// pageState.splice(tmpIndex, 1, false) |
|||
// this.setState({ |
|||
// pageState |
|||
// }) |
|||
// } |
|||
|
|||
// }) |
|||
} |
|||
|
|||
confirm = () => { |
|||
let index = JSON.parse(JSON.stringify(this.state.shadowActiveMenuArr)); |
|||
let value = JSON.parse(JSON.stringify(this.state.shadowActiveMenuArr)); |
|||
//对结果做一下处理 |
|||
const { menu } = this.state; |
|||
index.forEach((item, i) => { |
|||
|
|||
if (typeof (item[0]) == 'object') { |
|||
//针对筛选结果过一个排序 |
|||
item.forEach((s, j) => { |
|||
if (s != null) { |
|||
s.sort((val1, val2) => { |
|||
return val1 - val2; |
|||
}); |
|||
item[j] = s; |
|||
s.forEach((v, k) => { |
|||
value[i][j][k] = (v == null || v >= this.state.subData[i].submenu[j].submenu.length) ? null : this.state.subData[i].submenu[j].submenu[v].value; |
|||
if (this.state.subData[i].type == 'radio' && value[i][j][k] == null) { |
|||
value[i][j] = []; |
|||
index[i][j] = []; |
|||
} |
|||
}); |
|||
} |
|||
}); |
|||
} else { |
|||
if(menu[i].type != 'date'){ |
|||
let submenu = this.state.subData[i].submenu[item[0]]; |
|||
value[i][0] = submenu.value; |
|||
if (value[i].length >= 2 && item[1] != null) { |
|||
if (submenu.submenu.length > 0) { |
|||
submenu = submenu.submenu[item[1]]; |
|||
value[i][1] = submenu.hasOwnProperty('value') ? submenu.value : null; |
|||
} else { |
|||
value[i][1] = null |
|||
} |
|||
if (value[i].length >= 3 && item[2] != null) { |
|||
if (submenu.submenu.length > 0) { |
|||
submenu = submenu.submenu[item[2]]; |
|||
value[i][2] = submenu.hasOwnProperty('value') ? submenu.value : null; |
|||
} else { |
|||
value[i][2] = null; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
index[i] = item; |
|||
}); |
|||
// 输出 |
|||
this.props.confirm({ |
|||
index: index, |
|||
value: value |
|||
}) |
|||
} |
|||
|
|||
//show菜单页 |
|||
showPageLayer = (index) => { |
|||
let pageState, showPage, triangleDeg |
|||
this.processPage(index); |
|||
pageState = this.state.pageState |
|||
pageState.splice(index, 1, true); |
|||
this.setState({ |
|||
pageState |
|||
}, () => { |
|||
Taro.nextTick(() => { |
|||
setTimeout(() => { |
|||
showPage = index; |
|||
this.setState({ |
|||
showPage |
|||
}) |
|||
}, 0); |
|||
}) |
|||
triangleDeg = this.state.triangleDeg |
|||
triangleDeg[index] = 180; |
|||
this.setState({ |
|||
triangleDeg |
|||
}) |
|||
}) |
|||
} |
|||
|
|||
reloadActiveMenuArr = () => { |
|||
let filterData, activeMenuArr, shadowActiveMenuArr, subData |
|||
for (let i = 0; i < this.state.filterData.length; i++) { |
|||
let tmpitem = this.state.filterData[i]; |
|||
let tmpArr = this.processActive(tmpitem); |
|||
tmpitem = this.processSubMenu(tmpitem); |
|||
if (this.state.activeMenuArr[i].length != tmpArr.length) { |
|||
filterData = this.state.filterData |
|||
activeMenuArr = this.state.activeMenuArr |
|||
shadowActiveMenuArr = this.state.shadowActiveMenuArr |
|||
filterData[i] = tmpitem; |
|||
activeMenuArr.splice(i, 1, JSON.parse(JSON.stringify(tmpArr))); |
|||
shadowActiveMenuArr.splice(i, 1, JSON.parse(JSON.stringify(tmpArr))); |
|||
this.setState({ |
|||
filterData, |
|||
activeMenuArr, |
|||
shadowActiveMenuArr |
|||
}) |
|||
} |
|||
} |
|||
subData = this.state.subData |
|||
subData = this.state.filterData; |
|||
this.setState({ |
|||
subData |
|||
}, () => { |
|||
this.forceUpdate(); |
|||
}) |
|||
} |
|||
|
|||
processPage = (index) => { |
|||
let activeMenuArr, subData, firstScrollInto, secondScrollInto |
|||
//check UI控制数组,结果数组,防止传入数据层级和UI控制数组不同步 |
|||
this.reloadActiveMenuArr(); |
|||
//重置UI控制数组 |
|||
activeMenuArr = this.state.activeMenuArr; |
|||
console.log(activeMenuArr,index) |
|||
activeMenuArr.splice(index, 1, JSON.parse(JSON.stringify(this.state.shadowActiveMenuArr[index]))); |
|||
this.setState({ |
|||
activeMenuArr |
|||
}, () => { |
|||
if (this.state.menu[index].type == 'filter') { |
|||
//重载筛选页选中状态 |
|||
let level = this.state.shadowActiveMenuArr[index].length; |
|||
for (let i = 0; i < level; i++) { |
|||
let box = this.state.subData[index].submenu[i].submenu; |
|||
for (let j = 0; j < box.length; j++) { |
|||
subData = this.state.subData |
|||
if (this.state.shadowActiveMenuArr[index][i].indexOf(j) > -1) { |
|||
subData[index].submenu[i].submenu[j].selected = true; |
|||
} else { |
|||
subData[index].submenu[i].submenu[j].selected = false; |
|||
} |
|||
this.setState({ |
|||
subData |
|||
}) |
|||
} |
|||
} |
|||
} else if (this.state.menu[index].type == 'hierarchy') { |
|||
Taro.nextTick(() => { |
|||
setTimeout(() => { |
|||
//滚动到选中项 |
|||
firstScrollInto = this.state.firstScrollInto |
|||
secondScrollInto = this.state.secondScrollInto |
|||
firstScrollInto = parseInt(this.state.activeMenuArr[index][0]); |
|||
secondScrollInto = parseInt(this.state.activeMenuArr[index][1]); |
|||
this.setState({ |
|||
firstScrollInto, |
|||
secondScrollInto |
|||
}) |
|||
}, 0); |
|||
}) |
|||
} else if (this.state.menu[index].type == 'radio') { |
|||
//重载筛选页选中状态 |
|||
let level = this.state.shadowActiveMenuArr[index].length; |
|||
for (let i = 0; i < level; i++) { |
|||
let box = this.state.subData[index].submenu[i].submenu; |
|||
for (let j = 0; j < box.length; j++) { |
|||
subData = this.state.subData |
|||
if (this.state.shadowActiveMenuArr[index][i].indexOf(j) > -1) { |
|||
subData[index].submenu[i].submenu[j].selected = true; |
|||
} else { |
|||
subData[index].submenu[i].submenu[j].selected = false; |
|||
} |
|||
this.setState({ |
|||
subData |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
}) |
|||
} |
|||
|
|||
processActive = (tmpitem) => { |
|||
let tmpArr = [] |
|||
if (tmpitem.type == 'hierarchy' && tmpitem.hasOwnProperty('submenu') && tmpitem.submenu.length > 0) { |
|||
let level = this.getMaxFloor(tmpitem.submenu); |
|||
while (level > 0) { |
|||
tmpArr.push(0); |
|||
level--; |
|||
} |
|||
} else if (tmpitem.type == 'filter') { |
|||
let level = tmpitem.submenu.length; |
|||
while (level > 0) { |
|||
tmpArr.push([]); |
|||
level--; |
|||
} |
|||
} else if (tmpitem.type == 'radio') { |
|||
let level = tmpitem.submenu.length; |
|||
while (level > 0) { |
|||
tmpArr.push([]); |
|||
level--; |
|||
} |
|||
} |
|||
return tmpArr; |
|||
} |
|||
|
|||
processSubMenu = (menu) => { |
|||
if (menu.hasOwnProperty('submenu') && menu.submenu.length > 0) { |
|||
for (let i = 0; i < menu.submenu.length; i++) { |
|||
menu.submenu[i] = this.processSubMenu(menu.submenu[i]); |
|||
} |
|||
} else { |
|||
menu.submenu = []; |
|||
} |
|||
return menu; |
|||
} |
|||
|
|||
//计算菜单层级 |
|||
getMaxFloor = (treeData) => { |
|||
let max = 0 |
|||
function each(data, floor) { |
|||
data.forEach(e => { |
|||
max = floor > max ? floor : max; |
|||
if (e.hasOwnProperty('submenu') && e.submenu.length > 0) { |
|||
each(e.submenu, floor + 1) |
|||
} |
|||
}) |
|||
} |
|||
each(treeData, 1) |
|||
return max; |
|||
} |
|||
|
|||
handleInitial = (value, index) => { |
|||
// console.log('initial value: ', value, ', selected index: ', index) |
|||
} |
|||
|
|||
handleConfirm = (page_index, value, index) => { |
|||
console.log(page_index,'confirm value: ', value, ', selected index: ', index) |
|||
let { shadowActiveMenuArr, menu } = this.state; |
|||
shadowActiveMenuArr[page_index] = [value,index] |
|||
this.setState({ |
|||
shadowActiveMenuArr |
|||
}, () => { |
|||
this.togglePage(this.state.showPage); |
|||
}) |
|||
|
|||
} |
|||
|
|||
handleCancel = (page_index) => { |
|||
console.log('cancel action') |
|||
let { shadowActiveMenuArr } = this.state; |
|||
shadowActiveMenuArr[page_index] = [] |
|||
this.setState({ |
|||
shadowActiveMenuArr |
|||
}, () => { |
|||
this.togglePage(this.state.showPage); |
|||
}) |
|||
|
|||
} |
|||
render() { |
|||
const { menu, subData, showPage, pageState, activeMenuArr, triangleDeg, isShowMask, maskVisibility, firstScrollInto, secondScrollInto, shadowActiveMenuArr } = this.state; |
|||
//console.log(shadowActiveMenuArr) |
|||
return ( |
|||
<View className='index'> |
|||
<View className='HMfilterDropdown' catchtouchmove> |
|||
<View className='nav'> |
|||
{ |
|||
menu.map((item, index) => { |
|||
return (<Block key={index}> |
|||
<View className={'first-menu' + (showPage == index ? ' on' : '')} onClick={this.togglePage.bind(this, index)}> |
|||
<Text className='name'>{item.name}</Text> |
|||
<Text className='iconfont triangle' style={'transform:rotate(' + triangleDeg[index] + 'deg);'}></Text> |
|||
</View> |
|||
</Block>) |
|||
}) |
|||
} |
|||
</View> |
|||
|
|||
<View className={'mask' + (isShowMask ? ' show' : '') + (maskVisibility != true ? ' hide' : '')} onClick={this.togglePage.bind(this, showPage)}></View> |
|||
{ |
|||
subData.map((page, page_index) => { |
|||
return (<Block key={page_index}> |
|||
<View className={'sub-menu-class' + (showPage == page_index ? ' show' : '') + (pageState[page_index] != true ? ' hide' : '')}> |
|||
{ |
|||
(page.type == 'hierarchy' && page.submenu.length > 0) ? ( |
|||
<Block> |
|||
<ScrollView |
|||
className={'sub-menu-list' + (activeMenuArr[page_index].length > 1 ? ' first' : ' alone')} |
|||
scrollY |
|||
scrollIntoView={'first_id' + firstScrollInto} |
|||
> |
|||
{ |
|||
page.submenu.map((sub, index) => { |
|||
return ( |
|||
<Block key={index}> |
|||
<View className={'sub-menu' + (activeMenuArr[page_index][0] == index ? ' on' : '')} id={'first_id' + index} onTap={this.selectHierarchyMenu.bind(this, page_index, index, null, null)}> |
|||
<View className='menu-name'> |
|||
<Text>{sub.name}</Text> |
|||
<Text className='iconfont selected'></Text> |
|||
</View> |
|||
</View> |
|||
</Block>) |
|||
}) |
|||
} |
|||
</ScrollView> |
|||
<Block> |
|||
{ |
|||
page.submenu.map((sub, index) => { |
|||
return (<Block key={index}> |
|||
{ |
|||
(activeMenuArr[page_index][0] == index && sub.submenu.length > 0) ? ( |
|||
<ScrollView |
|||
className='sub-menu-list not-first' |
|||
scrollY |
|||
scrollIntoView={'second_id' + secondScrollInto} |
|||
> |
|||
{ |
|||
sub.submenu.map((sub_second, second_index) => { |
|||
return (<Block key='second_index'> |
|||
<View className={'sub-menu' + (activeMenuArr[page_index][1] == second_index ? ' on' : '')} id={'second_id' + second_index}> |
|||
<View className='menu-name' onTap={this.selectHierarchyMenu.bind(this, page_index, activeMenuArr[page_index][0], second_index, null)}> |
|||
<Text>{sub_second.name}</Text> |
|||
<Text className='iconfont selected'></Text> |
|||
</View> |
|||
{ |
|||
(sub_second.submenu && sub.submenu.length > 0 && sub_second.submenu.length > 0) ? ( |
|||
<View className='more-sub-menu'> |
|||
{ |
|||
sub_second.submenu.map((sub2, sub2_index) => { |
|||
return (<Block key='sub2_index'> |
|||
{ |
|||
(sub_second.showAllSub || (sub2_index < 8)) ? ( |
|||
<Text |
|||
className={(activeMenuArr[page_index][1] == second_index && activeMenuArr[page_index][2] == sub2_index) ? 'on' : ''} |
|||
onTap={this.selectHierarchyMenu.bind(this, page_index, activeMenuArr[page_index][0], second_index, sub2_index)} |
|||
>{sub2.name} |
|||
</Text> |
|||
) : null |
|||
} |
|||
{ |
|||
(sub_second.showAllSub != true && sub2_index == 8 && sub_second.submenu.length > 9) ? ( |
|||
<Text onTap={this.showMoreSub.bind(this, second_index)}>更多<Text className='iconfont triangle'></Text></Text> |
|||
) : null |
|||
} |
|||
</Block>) |
|||
}) |
|||
} |
|||
</View> |
|||
) : null |
|||
} |
|||
</View> |
|||
</Block> |
|||
) |
|||
}) |
|||
} |
|||
</ScrollView> |
|||
) : null |
|||
} |
|||
</Block>) |
|||
}) |
|||
} |
|||
</Block> |
|||
</Block> |
|||
) : null |
|||
} |
|||
{ |
|||
(page.type == 'filter') ? ( |
|||
<Block> |
|||
<View className='filter'> |
|||
<ScrollView className='menu-box' scrollY > |
|||
{ |
|||
page.submenu.map((box, box_index) => { |
|||
return (<View className='box' key={box_index}> |
|||
<View className='title'>{box.name}</View> |
|||
<View className='labels'> |
|||
{ |
|||
box.submenu.map((label, label_index) => { |
|||
return (<View |
|||
key={label_index} |
|||
onTap={this.selectFilterLabel.bind(this, page_index, box_index, label_index)} |
|||
className={label.selected ? 'on' : ''} |
|||
>{label.name} |
|||
</View>) |
|||
}) |
|||
} |
|||
</View> |
|||
</View>) |
|||
}) |
|||
} |
|||
</ScrollView> |
|||
<View className='btn-box'> |
|||
<View className='reset' onTap={this.resetFilterData.bind(this, page_index)}>重置</View> |
|||
<View className='submit' onTap={this.setFilterData.bind(this, page_index)}>确定</View> |
|||
</View> |
|||
</View> |
|||
</Block> |
|||
) : null |
|||
} |
|||
{ |
|||
(page.type == 'radio') ? ( |
|||
<Block> |
|||
<View className='filter'> |
|||
<ScrollView className='menu-box' scrollY > |
|||
{ |
|||
page.submenu.map((box, box_index) => { |
|||
return (<View className='box' key={box_index}> |
|||
<View className='title'>{box.name}</View> |
|||
<View className='labels'> |
|||
{ |
|||
box.submenu.map((label, label_index) => { |
|||
return (<View key={label_index} onTap={this.selectRadioLabel.bind(this, page_index, box_index, label_index)} |
|||
className={label.selected ? 'on' : ''} |
|||
>{label.name}</View>) |
|||
}) |
|||
} |
|||
</View> |
|||
</View>) |
|||
}) |
|||
} |
|||
</ScrollView> |
|||
<View className='btn-box'> |
|||
<View className='reset' onTap={this.resetFilterData.bind(this, page_index)}>重置</View> |
|||
<View className='submit' onTap={this.setFilterData.bind(this, page_index)}>确定</View> |
|||
</View> |
|||
</View> |
|||
</Block> |
|||
) : null |
|||
} |
|||
{ |
|||
(page.type == 'date') ? ( |
|||
<Block> |
|||
<View className='filter'> |
|||
<DatePicker |
|||
dateTime={page.dateTime} |
|||
onInitial={this.handleInitial} |
|||
onConfirm={this.handleConfirm.bind(this,page_index)} |
|||
onCancel={this.handleCancel.bind(this,page_index)} |
|||
mode='format' |
|||
formatShow='YYYY-MM-DD' |
|||
value={[0,0,0]} |
|||
start={946656000000} |
|||
/> |
|||
</View> |
|||
</Block> |
|||
) : null |
|||
} |
|||
</View> |
|||
</Block>) |
|||
}) |
|||
} |
|||
</View> |
|||
</View > |
|||
|
|||
) |
|||
} |
|||
} |
|||
FilterDropdown.propTypes = { |
|||
filterData: PropTypes.array, |
|||
defaultSelected: PropTypes.array, |
|||
updateMenuName: PropTypes.bool, |
|||
dataFormat: PropTypes.string, |
|||
} |
|||
FilterDropdown.defaultProps = { |
|||
filterData: [], |
|||
defaultSelected: [], |
|||
updateMenuName: true, |
|||
dataFormat: 'Array', |
|||
} |
@ -0,0 +1,348 @@ |
|||
@import 'src/styles/theme'; |
|||
.HMfilterDropdown { |
|||
flex-shrink: 0; |
|||
width: 100%; |
|||
height: 88px; |
|||
position: fixed; |
|||
z-index: 997; |
|||
flex-wrap: nowrap; |
|||
display: flex; |
|||
flex-direction: row; |
|||
top: var(--window-top); |
|||
left: 0; |
|||
|
|||
// view { |
|||
// display: flex; |
|||
// flex-wrap: nowrap; |
|||
// } |
|||
} |
|||
|
|||
.region { |
|||
flex: 1; |
|||
height: 44px; |
|||
} |
|||
|
|||
.nav { |
|||
display: flex; |
|||
flex-wrap: nowrap; |
|||
width: 100%; |
|||
height: 88px; |
|||
border-bottom: solid 1rpx #eee; |
|||
z-index: 12; |
|||
background-color: #ffffff; |
|||
flex-direction: row; |
|||
.first-menu { |
|||
display: flex; |
|||
flex-wrap: nowrap; |
|||
width: 100%; |
|||
font-size: 26px; |
|||
color: #757575; |
|||
flex-direction: row; |
|||
align-items: center; |
|||
justify-content: center; |
|||
transition: color .2s linear; |
|||
.name { |
|||
height: 40px; |
|||
text-align: center; |
|||
text-overflow: clip; |
|||
overflow: hidden; |
|||
} |
|||
.iconfont { |
|||
width: 26px; |
|||
height: 26px; |
|||
align-items: center; |
|||
justify-content: center; |
|||
transition: transform .2s linear, color .2s linear; |
|||
} |
|||
&.on { |
|||
color: $primary-color; |
|||
.iconfont { |
|||
color: $primary-color; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
.sub-menu-class { |
|||
display: flex; |
|||
flex-wrap: nowrap; |
|||
width: 100%; |
|||
position: absolute; |
|||
left: 0; |
|||
transform: translate3d(0, - 100%, 0); |
|||
max-height: 690px; |
|||
background-color: #ffffff; |
|||
z-index: 11; |
|||
box-shadow: 0 10px 10px rgba(0, 0, 0, .1); |
|||
overflow: hidden; |
|||
flex-direction: row; |
|||
transition: transform .15s linear; |
|||
|
|||
&.hide { |
|||
display: none; |
|||
} |
|||
|
|||
&.show { |
|||
transform: translate3d(0, calc(89px), 0); |
|||
} |
|||
} |
|||
|
|||
.sub-menu-list { |
|||
width: 100%; |
|||
height: 690px; |
|||
flex-direction: column; |
|||
view { |
|||
display: flex; |
|||
flex-wrap: nowrap; |
|||
} |
|||
.sub-menu { |
|||
min-height: 88px; |
|||
font-size: 26px; |
|||
flex-direction: column; |
|||
padding-right: 30px; |
|||
|
|||
>.menu-name { |
|||
height: 88px; |
|||
flex-direction: row; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
|
|||
>.iconfont { |
|||
display: none; |
|||
font-size: 36px; |
|||
color: $primary-color; |
|||
} |
|||
} |
|||
} |
|||
|
|||
&.first { |
|||
flex-shrink: 0; |
|||
width: 236rpx; |
|||
background-color: #f0f0f0; |
|||
|
|||
.sub-menu { |
|||
padding-left: 30px; |
|||
|
|||
&.on { |
|||
background-color: #fff; |
|||
} |
|||
} |
|||
} |
|||
|
|||
&.alone { |
|||
max-height: 690px; |
|||
min-height: 340px; |
|||
height: auto; |
|||
|
|||
.sub-menu { |
|||
min-height: calc(88px - 1rpx); |
|||
margin-left: 30px; |
|||
border-bottom: solid 1rpx #e5e5e5; |
|||
|
|||
&.on { |
|||
color: $primary-color; |
|||
|
|||
>.menu-name { |
|||
>.iconfont { |
|||
display: block; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
&.not-first { |
|||
.sub-menu { |
|||
min-height: calc(88px - 1rpx); |
|||
margin-left: 30px; |
|||
border-bottom: solid 1rpx #e5e5e5; |
|||
|
|||
>.menu-name { |
|||
height: calc(88px - 1rpx); |
|||
|
|||
>.iconfont { |
|||
display: none; |
|||
font-size: 36px; |
|||
color: $primary-color; |
|||
} |
|||
} |
|||
|
|||
&.on { |
|||
color: $primary-color; |
|||
|
|||
>.menu-name { |
|||
>.iconfont { |
|||
display: block; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.more-sub-menu { |
|||
flex-direction: row; |
|||
flex-wrap: wrap; |
|||
padding-bottom: 18px; |
|||
|
|||
>text { |
|||
height: 60px; |
|||
border-radius: 6px; |
|||
background-color: #f5f5f5; |
|||
color: #9b9b9b; |
|||
margin-bottom: 12px; |
|||
margin-right: 12px; |
|||
text-align: center; |
|||
line-height: 60px; |
|||
border: solid #f5f5f5 1rpx; |
|||
flex: 0 0 calc(33.33% - 12px); |
|||
overflow: hidden; |
|||
font-size: 24px; |
|||
|
|||
&:nth-child(3n) { |
|||
margin-right: 0; |
|||
} |
|||
|
|||
&.on { |
|||
border-color: #f6c8ac; |
|||
color: $primary-color; |
|||
} |
|||
|
|||
.iconfont { |
|||
color: #9b9b9b; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
.filter { |
|||
width: 100%; |
|||
height: 690px; |
|||
display: flex; |
|||
flex-direction: column; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
|
|||
.menu-box { |
|||
width: 698rpx; |
|||
height: calc(690px - 150px); |
|||
flex-shrink: 1; |
|||
view { |
|||
display: flex; |
|||
flex-wrap: nowrap; |
|||
} |
|||
.box { |
|||
width: 100%; |
|||
margin-top: 32px; |
|||
flex-direction: column; |
|||
|
|||
.title { |
|||
width: 100%; |
|||
font-size: 26px; |
|||
color: #888; |
|||
} |
|||
|
|||
.labels { |
|||
flex-direction: row; |
|||
flex-wrap: wrap; |
|||
|
|||
.on { |
|||
border-color: $primary-color; |
|||
background-color: $primary-color; |
|||
color: #fff; |
|||
} |
|||
|
|||
>view { |
|||
width: 148rpx; |
|||
height: 60px; |
|||
border: solid 1rpx #adadad; |
|||
border-radius: 4px; |
|||
margin-right: 30px; |
|||
margin-top: 16px; |
|||
font-size: 24px; |
|||
flex-direction: row; |
|||
justify-content: center; |
|||
align-items: center; |
|||
|
|||
&:nth-child(4n) { |
|||
margin-right: 0; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
.btn-box { |
|||
flex-shrink: 0; |
|||
width: 698rpx; |
|||
height: 150px; |
|||
flex-direction: row !important; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
display: flex; |
|||
flex-wrap: nowrap; |
|||
|
|||
>view { |
|||
display: flex; |
|||
flex-wrap: nowrap; |
|||
width: 320rpx; |
|||
height: 80px; |
|||
border-radius: 80px; |
|||
border: solid 1rpx $primary-color; |
|||
align-items: center; |
|||
justify-content: center; |
|||
} |
|||
|
|||
.reset { |
|||
color: $primary-color; |
|||
} |
|||
|
|||
.submit { |
|||
color: #fff; |
|||
background-color: $primary-color; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.mask { |
|||
z-index: 10; |
|||
position: fixed; |
|||
top: 0; |
|||
left: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
background-color: rgba(0, 0, 0, 0); |
|||
transition: background-color .15s linear; |
|||
|
|||
&.show { |
|||
background-color: rgba(0, 0, 0, 0.5); |
|||
} |
|||
|
|||
&.hide { |
|||
display: none; |
|||
} |
|||
} |
|||
|
|||
/* 字体图标 */ |
|||
@font-face { |
|||
font-family: "HM-FD-font"; |
|||
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAALAAAsAAAAABpQAAAJzAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCDBgp4gQIBNgIkAwwLCAAEIAWEbQc5G8sFERWMIbIfCbbzqA4hp7InSBibVsYGb4J42o82b3e/nJlHMw/NHbGOlwKJRCRpwzPtpAECCOZubdqxjYpQLMlVg+70/08edrgQOtx2ukpVyApZn+dyehPoQObHo3O85rYx9vOjXoBxQIHugW2yIkqIW2QXcScu4jwE8CSWbKSmrqUHFwOaJoCsLM5P4haSGIxRcRHshrUGucLCVcfqI3AZfV/+USguKCwNmtsxVztDxU/n55C+3W0Z4QQpEOTNFqCBbMCAjDUWB9CIwWk87aa70cYgqLkyd3dEmm+18R8eKATEBrV7A5CulBT8dKiWOYZk412XNcDdKSEKSGODnyKIDl+dmVt9/Dx4pu/xyeutkMlHISGPTsPCnoTNP9nOT6wTtDdlO6dPr47efvj942lkYuQzrhMKEjq9N6y98P3340gmlJ/RStUD6F31CAEEPtUW94/7rf+7XgaAz57X0ZHXAGsFFwVgw38yALuMb0IBbVyNamFYEw4oKMDTj3AHRQP5Pt4dci9VwSVkRNQh5r7CLskZadhsWHhRDBsXczk8ZYk3ewnCxmQeQKa3BOHvA8XXO2j+vqRhf7CE+sPmn4anvoL29JLa4qqaUQkmoK+QG2osCckq7txi2leK86aIPyJ3eQZ8xytXYmyQ51jQndJAxIJlqiGSLsOqImiZCjTiZCJt6Lq26U2OoXqwUo0hRaAE0K5AziANy/uLVeXzWyjVqyjcoeupjxDr5MMDn8MDkLG9Aenu5ZrOSSoghAUsRmogkkahSoWAtnlUARnCkY3It0Iu7mWhdmd9Z/19BwBP6GidEi0G56opckXTGZVSPxgAAAA='); |
|||
} |
|||
|
|||
.iconfont { |
|||
font-family: "HM-FD-font" !important; |
|||
font-size: 26px; |
|||
font-style: normal; |
|||
color: #757575; |
|||
|
|||
&.triangle { |
|||
&:before { |
|||
content: "\e65a"; |
|||
} |
|||
} |
|||
|
|||
&.selected { |
|||
&:before { |
|||
content: "\e607"; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,250 @@ |
|||
import WxCanvas from './wx-canvas'; |
|||
import * as echarts from './echarts'; |
|||
|
|||
let ctx; |
|||
|
|||
function compareVersion(v1, v2) { |
|||
v1 = v1.split('.') |
|||
v2 = v2.split('.') |
|||
const len = Math.max(v1.length, v2.length) |
|||
|
|||
while (v1.length < len) { |
|||
v1.push('0') |
|||
} |
|||
while (v2.length < len) { |
|||
v2.push('0') |
|||
} |
|||
|
|||
for (let i = 0; i < len; i++) { |
|||
const num1 = parseInt(v1[i]) |
|||
const num2 = parseInt(v2[i]) |
|||
|
|||
if (num1 > num2) { |
|||
return 1 |
|||
} else if (num1 < num2) { |
|||
return -1 |
|||
} |
|||
} |
|||
return 0 |
|||
} |
|||
|
|||
Component({ |
|||
properties: { |
|||
canvasId: { |
|||
type: String, |
|||
value: 'ec-canvas' |
|||
}, |
|||
|
|||
ec: { |
|||
type: Object |
|||
}, |
|||
|
|||
forceUseOldCanvas: { |
|||
type: Boolean, |
|||
value: false |
|||
} |
|||
}, |
|||
|
|||
data: { |
|||
isUseNewCanvas: false |
|||
}, |
|||
|
|||
ready: function () { |
|||
// Disable prograssive because drawImage doesn't support DOM as parameter
|
|||
// See https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.drawImage.html
|
|||
echarts.registerPreprocessor(option => { |
|||
if (option && option.series) { |
|||
if (option.series.length > 0) { |
|||
option.series.forEach(series => { |
|||
series.progressive = 0; |
|||
}); |
|||
} |
|||
else if (typeof option.series === 'object') { |
|||
option.series.progressive = 0; |
|||
} |
|||
} |
|||
}); |
|||
|
|||
if (!this.data.ec) { |
|||
console.warn('组件需绑定 ec 变量,例:<ec-canvas id="mychart-dom-bar" ' |
|||
+ 'canvas-id="mychart-bar" ec="{{ ec }}"></ec-canvas>'); |
|||
return; |
|||
} |
|||
|
|||
if (!this.data.ec.lazyLoad) { |
|||
this.init(); |
|||
} |
|||
}, |
|||
|
|||
methods: { |
|||
init: function (callback) { |
|||
const version = wx.getSystemInfoSync().SDKVersion |
|||
|
|||
const canUseNewCanvas = compareVersion(version, '2.9.0') >= 0; |
|||
const forceUseOldCanvas = this.data.forceUseOldCanvas; |
|||
const isUseNewCanvas = canUseNewCanvas && !forceUseOldCanvas; |
|||
this.setData({ isUseNewCanvas }); |
|||
|
|||
if (forceUseOldCanvas && canUseNewCanvas) { |
|||
console.warn('开发者强制使用旧canvas,建议关闭'); |
|||
} |
|||
|
|||
if (isUseNewCanvas) { |
|||
// console.log('微信基础库版本大于2.9.0,开始使用<canvas type="2d"/>');
|
|||
// 2.9.0 可以使用 <canvas type="2d"></canvas>
|
|||
this.initByNewWay(callback); |
|||
} else { |
|||
const isValid = compareVersion(version, '1.9.91') >= 0 |
|||
if (!isValid) { |
|||
console.error('微信基础库版本过低,需大于等于 1.9.91。' |
|||
+ '参见:https://github.com/ecomfe/echarts-for-weixin' |
|||
+ '#%E5%BE%AE%E4%BF%A1%E7%89%88%E6%9C%AC%E8%A6%81%E6%B1%82'); |
|||
return; |
|||
} else { |
|||
console.warn('建议将微信基础库调整大于等于2.9.0版本。升级后绘图将有更好性能'); |
|||
this.initByOldWay(callback); |
|||
} |
|||
} |
|||
}, |
|||
|
|||
initByOldWay(callback) { |
|||
// 1.9.91 <= version < 2.9.0:原来的方式初始化
|
|||
ctx = wx.createCanvasContext(this.data.canvasId, this); |
|||
const canvas = new WxCanvas(ctx, this.data.canvasId, false); |
|||
|
|||
echarts.setCanvasCreator(() => { |
|||
return canvas; |
|||
}); |
|||
// const canvasDpr = wx.getSystemInfoSync().pixelRatio // 微信旧的canvas不能传入dpr
|
|||
const canvasDpr = 1 |
|||
var query = wx.createSelectorQuery().in(this); |
|||
query.select('.ec-canvas').boundingClientRect(res => { |
|||
if (typeof callback === 'function') { |
|||
this.chart = callback(canvas, res.width, res.height, canvasDpr); |
|||
} |
|||
else if (this.data.ec && typeof this.data.ec.onInit === 'function') { |
|||
this.chart = this.data.ec.onInit(canvas, res.width, res.height, canvasDpr); |
|||
} |
|||
else { |
|||
this.triggerEvent('init', { |
|||
canvas: canvas, |
|||
width: res.width, |
|||
height: res.height, |
|||
canvasDpr: canvasDpr // 增加了dpr,可方便外面echarts.init
|
|||
}); |
|||
} |
|||
}).exec(); |
|||
}, |
|||
|
|||
initByNewWay(callback) { |
|||
// version >= 2.9.0:使用新的方式初始化
|
|||
const query = wx.createSelectorQuery().in(this) |
|||
query |
|||
.select('.ec-canvas') |
|||
.fields({ node: true, size: true }) |
|||
.exec(res => { |
|||
const canvasNode = res[0].node |
|||
this.canvasNode = canvasNode |
|||
|
|||
const canvasDpr = wx.getSystemInfoSync().pixelRatio |
|||
const canvasWidth = res[0].width |
|||
const canvasHeight = res[0].height |
|||
|
|||
const ctx = canvasNode.getContext('2d') |
|||
|
|||
const canvas = new WxCanvas(ctx, this.data.canvasId, true, canvasNode) |
|||
echarts.setCanvasCreator(() => { |
|||
return canvas |
|||
}) |
|||
|
|||
if (typeof callback === 'function') { |
|||
this.chart = callback(canvas, canvasWidth, canvasHeight, canvasDpr) |
|||
} else if (this.data.ec && typeof this.data.ec.onInit === 'function') { |
|||
this.chart = this.data.ec.onInit(canvas, canvasWidth, canvasHeight, canvasDpr) |
|||
} else { |
|||
this.triggerEvent('init', { |
|||
canvas: canvas, |
|||
width: canvasWidth, |
|||
height: canvasHeight, |
|||
dpr: canvasDpr |
|||
}) |
|||
} |
|||
}) |
|||
}, |
|||
canvasToTempFilePath(opt) { |
|||
if (this.data.isUseNewCanvas) { |
|||
// 新版
|
|||
const query = wx.createSelectorQuery().in(this) |
|||
query |
|||
.select('.ec-canvas') |
|||
.fields({ node: true, size: true }) |
|||
.exec(res => { |
|||
const canvasNode = res[0].node |
|||
opt.canvas = canvasNode |
|||
wx.canvasToTempFilePath(opt) |
|||
}) |
|||
} else { |
|||
// 旧的
|
|||
if (!opt.canvasId) { |
|||
opt.canvasId = this.data.canvasId; |
|||
} |
|||
ctx.draw(true, () => { |
|||
wx.canvasToTempFilePath(opt, this); |
|||
}); |
|||
} |
|||
}, |
|||
|
|||
touchStart(e) { |
|||
if (this.chart && e.touches.length > 0) { |
|||
var touch = e.touches[0]; |
|||
var handler = this.chart.getZr().handler; |
|||
handler.dispatch('mousedown', { |
|||
zrX: touch.x, |
|||
zrY: touch.y |
|||
}); |
|||
handler.dispatch('mousemove', { |
|||
zrX: touch.x, |
|||
zrY: touch.y |
|||
}); |
|||
handler.processGesture(wrapTouch(e), 'start'); |
|||
} |
|||
}, |
|||
|
|||
touchMove(e) { |
|||
if (this.chart && e.touches.length > 0) { |
|||
var touch = e.touches[0]; |
|||
var handler = this.chart.getZr().handler; |
|||
handler.dispatch('mousemove', { |
|||
zrX: touch.x, |
|||
zrY: touch.y |
|||
}); |
|||
handler.processGesture(wrapTouch(e), 'change'); |
|||
} |
|||
}, |
|||
|
|||
touchEnd(e) { |
|||
if (this.chart) { |
|||
const touch = e.changedTouches ? e.changedTouches[0] : {}; |
|||
var handler = this.chart.getZr().handler; |
|||
handler.dispatch('mouseup', { |
|||
zrX: touch.x, |
|||
zrY: touch.y |
|||
}); |
|||
handler.dispatch('click', { |
|||
zrX: touch.x, |
|||
zrY: touch.y |
|||
}); |
|||
handler.processGesture(wrapTouch(e), 'end'); |
|||
} |
|||
} |
|||
} |
|||
}); |
|||
|
|||
function wrapTouch(event) { |
|||
for (let i = 0; i < event.touches.length; ++i) { |
|||
const touch = event.touches[i]; |
|||
touch.offsetX = touch.x; |
|||
touch.offsetY = touch.y; |
|||
} |
|||
return event; |
|||
} |
@ -0,0 +1,4 @@ |
|||
{ |
|||
"component": true, |
|||
"usingComponents": {} |
|||
} |
@ -0,0 +1,4 @@ |
|||
<!-- 新的:接口对其了H5 --> |
|||
<canvas wx:if="{{isUseNewCanvas}}" type="2d" class="ec-canvas" canvas-id="{{ canvasId }}" bindinit="init" bindtouchstart="{{ ec.disableTouch ? '' : 'touchStart' }}" bindtouchmove="{{ ec.disableTouch ? '' : 'touchMove' }}" bindtouchend="{{ ec.disableTouch ? '' : 'touchEnd' }}"></canvas> |
|||
<!-- 旧的 --> |
|||
<canvas wx:else class="ec-canvas" canvas-id="{{ canvasId }}" bindinit="init" bindtouchstart="{{ ec.disableTouch ? '' : 'touchStart' }}" bindtouchmove="{{ ec.disableTouch ? '' : 'touchMove' }}" bindtouchend="{{ ec.disableTouch ? '' : 'touchEnd' }}"></canvas> |
@ -0,0 +1,4 @@ |
|||
.ec-canvas { |
|||
width: 100%; |
|||
height: 100%; |
|||
} |
File diff suppressed because one or more lines are too long
@ -0,0 +1,121 @@ |
|||
export default class WxCanvas { |
|||
constructor(ctx, canvasId, isNew, canvasNode) { |
|||
this.ctx = ctx; |
|||
this.canvasId = canvasId; |
|||
this.chart = null; |
|||
this.isNew = isNew |
|||
if (isNew) { |
|||
this.canvasNode = canvasNode; |
|||
} |
|||
else { |
|||
this._initStyle(ctx); |
|||
} |
|||
|
|||
// this._initCanvas(zrender, ctx);
|
|||
|
|||
this._initEvent(); |
|||
} |
|||
|
|||
getContext(contextType) { |
|||
if (contextType === '2d') { |
|||
return this.ctx; |
|||
} |
|||
} |
|||
|
|||
// canvasToTempFilePath(opt) {
|
|||
// if (!opt.canvasId) {
|
|||
// opt.canvasId = this.canvasId;
|
|||
// }
|
|||
// return wx.canvasToTempFilePath(opt, this);
|
|||
// }
|
|||
|
|||
setChart(chart) { |
|||
this.chart = chart; |
|||
} |
|||
|
|||
attachEvent() { |
|||
// noop
|
|||
} |
|||
|
|||
detachEvent() { |
|||
// noop
|
|||
} |
|||
|
|||
_initCanvas(zrender, ctx) { |
|||
zrender.util.getContext = function () { |
|||
return ctx; |
|||
}; |
|||
|
|||
zrender.util.$override('measureText', function (text, font) { |
|||
ctx.font = font || '12px sans-serif'; |
|||
return ctx.measureText(text); |
|||
}); |
|||
} |
|||
|
|||
_initStyle(ctx) { |
|||
var styles = ['fillStyle', 'strokeStyle', 'globalAlpha', |
|||
'textAlign', 'textBaseAlign', 'shadow', 'lineWidth', |
|||
'lineCap', 'lineJoin', 'lineDash', 'miterLimit', 'fontSize']; |
|||
|
|||
styles.forEach(style => { |
|||
Object.defineProperty(ctx, style, { |
|||
set: value => { |
|||
if (style !== 'fillStyle' && style !== 'strokeStyle' |
|||
|| value !== 'none' && value !== null |
|||
) { |
|||
ctx['set' + style.charAt(0).toUpperCase() + style.slice(1)](value); |
|||
} |
|||
} |
|||
}); |
|||
}); |
|||
|
|||
ctx.createRadialGradient = () => { |
|||
return ctx.createCircularGradient(arguments); |
|||
}; |
|||
} |
|||
|
|||
_initEvent() { |
|||
this.event = {}; |
|||
const eventNames = [{ |
|||
wxName: 'touchStart', |
|||
ecName: 'mousedown' |
|||
}, { |
|||
wxName: 'touchMove', |
|||
ecName: 'mousemove' |
|||
}, { |
|||
wxName: 'touchEnd', |
|||
ecName: 'mouseup' |
|||
}, { |
|||
wxName: 'touchEnd', |
|||
ecName: 'click' |
|||
}]; |
|||
|
|||
eventNames.forEach(name => { |
|||
this.event[name.wxName] = e => { |
|||
const touch = e.touches[0]; |
|||
this.chart.getZr().handler.dispatch(name.ecName, { |
|||
zrX: name.wxName === 'tap' ? touch.clientX : touch.x, |
|||
zrY: name.wxName === 'tap' ? touch.clientY : touch.y |
|||
}); |
|||
}; |
|||
}); |
|||
} |
|||
|
|||
set width(w) { |
|||
if (this.canvasNode) this.canvasNode.width = w |
|||
} |
|||
set height(h) { |
|||
if (this.canvasNode) this.canvasNode.height = h |
|||
} |
|||
|
|||
get width() { |
|||
if (this.canvasNode) |
|||
return this.canvasNode.width |
|||
return 0 |
|||
} |
|||
get height() { |
|||
if (this.canvasNode) |
|||
return this.canvasNode.height |
|||
return 0 |
|||
} |
|||
} |
@ -0,0 +1,29 @@ |
|||
import React, { useState, useEffect } from 'react'; |
|||
import { View, CoverView } from '@tarojs/components'; |
|||
import * as echarts from "../ec-canvas/echarts"; |
|||
import './common.scss' |
|||
|
|||
const Common = ({ ...props }) => { |
|||
const { option } = props; |
|||
const ec = { |
|||
onInit: function (canvas, width, height, dpr) { |
|||
const chart = echarts.init(canvas, null, { |
|||
width: width, |
|||
height: height, |
|||
devicePixelRatio: dpr |
|||
}); |
|||
canvas.setChart(chart); |
|||
|
|||
chart.setOption(option); |
|||
|
|||
return chart; |
|||
} |
|||
} |
|||
|
|||
return ( |
|||
<View style={{ width: '100%', height: '100%' }}> |
|||
<ec-canvas ec={ec} force-use-old-canvas='true'></ec-canvas> |
|||
</View> |
|||
); |
|||
} |
|||
export default Common; |
@ -0,0 +1,28 @@ |
|||
.tooltipContainer { |
|||
z-index: 1000; |
|||
background-color: rgba(0, 0, 0, 0.4); |
|||
position: absolute; |
|||
top: 30rpx; |
|||
color: white; |
|||
font-size: 20rpx; |
|||
border-radius: 6rpx; |
|||
.tooltip { |
|||
display: flex; |
|||
padding: 6rpx 16rpx; |
|||
justify-content: flex-start; |
|||
&:first-child { |
|||
padding-top: 10rpx; |
|||
} |
|||
&:last-child { |
|||
padding-bottom: 10rpx; |
|||
} |
|||
.color { |
|||
margin-right: 8rpx; |
|||
display: inline-block; |
|||
height: 22rpx; |
|||
width: 22rpx; |
|||
border-radius: 50%; |
|||
background-color: black; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,26 @@ |
|||
import Chart from './chart/chart'; |
|||
import DatePicker from './datePicker/index'; |
|||
import FilterPicker from './datePicker/filterPicker'; |
|||
import ListView from './list-view/index'; |
|||
import ListVirtual from './virtual-list/index'; |
|||
import NoData from './no-data/noData'; |
|||
import ResultPage from './result-page/index'; |
|||
import LoadingPage from './loading-page/index'; |
|||
import SearchBar from './searchBar/index'; |
|||
import Tabs from './tabs/tabs'; |
|||
import FilterDropdown from './dropdown/index'; |
|||
|
|||
export { |
|||
Chart, |
|||
DatePicker, |
|||
FilterPicker, |
|||
//Card,
|
|||
ListView, |
|||
ListVirtual, |
|||
NoData, |
|||
ResultPage, |
|||
LoadingPage, |
|||
SearchBar, |
|||
Tabs, |
|||
FilterDropdown, |
|||
} |
@ -0,0 +1,27 @@ |
|||
import React, { useState } from 'react'; |
|||
import { AtIcon } from "taro-ui"; |
|||
import { View } from '@tarojs/components'; |
|||
import './infoCard.scss'; |
|||
|
|||
function Card({ title, children, onClick }) { |
|||
const [showChildren, setShow] = useState(true) |
|||
function onTitleClick() { |
|||
setShow(!showChildren) |
|||
} |
|||
function onCardClick(e) { |
|||
if (onClick) { |
|||
onClick(e) |
|||
} |
|||
} |
|||
return ( |
|||
<View className='info-card info-card-shadow' onClick={onCardClick}> |
|||
{title ? <View className='title' onClick={onTitleClick}> |
|||
<View className='text'>{title}</View> |
|||
<AtIcon value={showChildren ? 'chevron-down' : 'chevron-up'} size='16' color='#999'></AtIcon> |
|||
</View> : null} |
|||
{showChildren ? <View>{children}</View> : null} |
|||
</View> |
|||
); |
|||
} |
|||
|
|||
export default Card; |
@ -0,0 +1,41 @@ |
|||
.info-card { |
|||
padding: 40rpx; |
|||
background: #fff; |
|||
margin-bottom: 30px; |
|||
border-radius: 20px; |
|||
box-shadow: 0 4px 15px 0 rgba($color: #000000, $alpha: 0.06); |
|||
.title{ |
|||
display: flex; |
|||
font-weight: 500; |
|||
padding-right: 10px; |
|||
padding-bottom: 20px; |
|||
.text{ |
|||
padding-right: 10px; |
|||
} |
|||
} |
|||
.row { |
|||
font-size: 26px; |
|||
display: flex; |
|||
margin: 10px 0; |
|||
.color { |
|||
display: inline; |
|||
color: #2F54FF; |
|||
} |
|||
} |
|||
} |
|||
.info-card-shadow{ |
|||
position: relative; |
|||
&::before{ |
|||
content:''; |
|||
position: absolute; |
|||
bottom: -20px; |
|||
left: 50%; |
|||
transform: translateX(-50%); |
|||
width:90%; |
|||
height:68px; |
|||
background:rgba(0,0,0,0.06); |
|||
border-radius:9px; |
|||
filter:blur(15px); |
|||
z-index: -2; |
|||
} |
|||
} |
@ -0,0 +1,90 @@ |
|||
|
|||
/* |
|||
* create by Xumeng 2020/12/29 |
|||
* 默认无限滚动列表组件 |
|||
*/ |
|||
import React from 'react'; |
|||
import Taro,{ useReachBottom,usePullDownRefresh } from '@tarojs/taro'; |
|||
import { View, Text } from '@tarojs/components'; |
|||
import { ResultPage, LoadingPage, NoData } from '@/components'; |
|||
import { AtDivider, AtActivityIndicator, AtFab } from 'taro-ui'; |
|||
import './index.scss'; |
|||
|
|||
const ListView = ({...props}) => { |
|||
const { |
|||
//swr hoosk return |
|||
data, |
|||
isLoadingInitialData, |
|||
isLoadingMore, |
|||
isEmpty, |
|||
isReachingEnd, |
|||
isRefreshing, |
|||
isValidating, |
|||
error, |
|||
size, |
|||
mutate, |
|||
setSize, |
|||
// item render function |
|||
row, |
|||
// 是否开启滚到到顶部浮动按钮 |
|||
isScrollToTop, |
|||
ReachingEndText //没有更多的文本提示 |
|||
} = props; |
|||
console.log(data,isLoadingInitialData,size,isLoadingMore,isValidating,isReachingEnd) |
|||
//下拉刷新 |
|||
usePullDownRefresh(() => { |
|||
//在标题栏中显示加载 |
|||
Taro.showNavigationBarLoading() |
|||
//延迟加载 |
|||
setTimeout(function() |
|||
{ |
|||
// complete |
|||
setSize(1) |
|||
mutate() |
|||
//滚动到初始位置 |
|||
Taro.hideNavigationBarLoading() //完成停止加载 |
|||
Taro.stopPullDownRefresh() //停止下拉刷新 |
|||
|
|||
}, 1000); |
|||
}) |
|||
useReachBottom(()=>{ |
|||
console.log('我要更新了') |
|||
if(!isReachingEnd && !isLoadingMore){ |
|||
setSize(size + 1) |
|||
} |
|||
}) |
|||
const scrollToTop = () => { |
|||
Taro.pageScrollTo({ |
|||
scrollTop: 0, |
|||
duration: 300 |
|||
}) |
|||
} |
|||
if(error) { |
|||
return <ResultPage isError /> |
|||
} |
|||
if(isLoadingInitialData) { |
|||
return <LoadingPage text='加载中...' /> |
|||
} |
|||
if(isEmpty){ |
|||
return <NoData /> |
|||
} |
|||
|
|||
return ( |
|||
<View > |
|||
{data.map(row)} |
|||
{ isLoadingMore && <AtDivider fontColor='#2d8cf0' lineColor='#2d8cf0' > |
|||
<AtActivityIndicator content='加载中...'></AtActivityIndicator> |
|||
</AtDivider>} |
|||
{ isReachingEnd && <AtDivider content={ReachingEndText ? ReachingEndText : '我是有底线的'} fontColor='#2d8cf0' lineColor='#2d8cf0' />} |
|||
{ |
|||
isScrollToTop && |
|||
<View className='list-view-atfab' onClick={scrollToTop}> |
|||
<AtFab size='small'> |
|||
<Text className='at-fab__icon at-icon at-icon-menu'></Text> |
|||
</AtFab> |
|||
</View> |
|||
} |
|||
</View> |
|||
); |
|||
} |
|||
export default ListView; |
@ -0,0 +1,5 @@ |
|||
.list-view-atfab{ |
|||
position: fixed; |
|||
bottom: 50px; |
|||
right: 30px; |
|||
} |
@ -0,0 +1,18 @@ |
|||
import React, { Component } from 'react'; |
|||
import { View } from '@tarojs/components'; |
|||
import { AtActivityIndicator } from 'taro-ui' |
|||
import './index.scss' |
|||
|
|||
|
|||
class LoadingPage extends Component { |
|||
render() { |
|||
const { text, pageStyle} = this.props; |
|||
return ( |
|||
<View style={pageStyle ? pageStyle : ''} className='page-loading'> |
|||
<AtActivityIndicator mode='center' content={text || ''}></AtActivityIndicator> |
|||
</View> |
|||
) |
|||
} |
|||
} |
|||
|
|||
export default LoadingPage; |
@ -0,0 +1,4 @@ |
|||
.page-loading { |
|||
height: 100vh; |
|||
position: relative; |
|||
} |
@ -0,0 +1,29 @@ |
|||
import React, { Component } from 'react'; |
|||
import { View,Image } from '@tarojs/components'; |
|||
import './noData.scss'; |
|||
import img from '../../static/img/no-data.png'; |
|||
|
|||
|
|||
class NoData extends Component { |
|||
constructor(props) { |
|||
super(props); |
|||
this.state = { |
|||
|
|||
}; |
|||
} |
|||
|
|||
externalClasses= ['out-class'] |
|||
render() { |
|||
const {top} = this.props; |
|||
return ( |
|||
<View className='out-class' style={`margin-top: ${top}`}> |
|||
<View className='no-data-box'> |
|||
<Image className='img' src={img}></Image> |
|||
<View className='text'>暂无数据</View> |
|||
</View> |
|||
</View> |
|||
); |
|||
} |
|||
} |
|||
|
|||
export default NoData; |
@ -0,0 +1,16 @@ |
|||
.no-data-box{ |
|||
display: flex; |
|||
flex-direction: column; |
|||
justify-content: center; |
|||
align-items: center; |
|||
.img{ |
|||
width: 200px; |
|||
height: 150px; |
|||
} |
|||
.text{ |
|||
margin-top: 20px; |
|||
text-align: center; |
|||
font-size: 28px; |
|||
color: #999; |
|||
} |
|||
} |
After Width: | Height: | Size: 9.7 KiB |
@ -0,0 +1,42 @@ |
|||
import React, { Component } from 'react'; |
|||
import { View } from '@tarojs/components'; |
|||
import './index.scss' |
|||
|
|||
|
|||
class Page extends Component { |
|||
render() { |
|||
const { isError, launchError, launchEmpty, isEmpty, emptyText, fetchInit } = this.props; |
|||
const showError = isError; // isErrorUI权重最高 |
|||
const showErrorText = showError && !launchError; // 渲染ErrorText |
|||
const showRenderError = showError && launchError; // 渲染renderError |
|||
|
|||
const showEmpty = !isError && isEmpty; // isErrorUI权重最高 |
|||
const showEmptyText = showEmpty && !launchEmpty; // 渲染emptyText |
|||
const showRenderEmpty = showEmpty && launchEmpty; // 渲染renderEmpty |
|||
return ( |
|||
<View> |
|||
{showErrorText && ( |
|||
<View className='errorPage'> |
|||
<View className='marginBottom30'>网络似乎开小差了~</View> |
|||
{ |
|||
fetchInit && <View className='button' onClick={fetchInit}> |
|||
重新加载 |
|||
</View> |
|||
} |
|||
</View> |
|||
)} |
|||
{/* custom error page */} |
|||
{showRenderError ? this.props.renderError : ''} |
|||
{/* default blank page */} |
|||
{showEmptyText && ( |
|||
<View className='noContentTips'> |
|||
{emptyText} |
|||
</View> |
|||
)} |
|||
{/* custom blank page */} |
|||
{showRenderEmpty ? this.props.renderEmpty : ''} |
|||
</View> |
|||
)} |
|||
} |
|||
|
|||
export default Page; |
@ -0,0 +1,28 @@ |
|||
.errorPage { |
|||
text-align: center; |
|||
padding: 40px; |
|||
font-size: 30px; |
|||
.button { |
|||
border-radius: 10px; |
|||
margin-top: 10px; |
|||
display: inline-block; |
|||
border: 1px solid cornflowerblue; |
|||
color: cornflowerblue; |
|||
padding: 10px; |
|||
} |
|||
} |
|||
.noContentTips { |
|||
display: flex; |
|||
padding: 100px 20px 20px; |
|||
text-align: center; |
|||
flex-direction: column; |
|||
.emptyBanner { |
|||
width: 250px; |
|||
height: 170px; |
|||
display: inline-block; |
|||
margin: 0 auto 20px; |
|||
} |
|||
} |
|||
.marginBottom30 { |
|||
margin-bottom: 30px; |
|||
} |
@ -0,0 +1,212 @@ |
|||
/* |
|||
* create by Xumeng 2020/01/07 |
|||
* 通用搜索栏组件,可选筛选抽屉 filterOptions 暂支持 select 日期、时间、日历 |
|||
* filterOptions = [ |
|||
{ |
|||
field: 'name', |
|||
type: 'SELECT', |
|||
label: '业务类型', |
|||
list: [{id: 1, name: '张三'},{ id:2, name: '李四'}], |
|||
}, |
|||
{ |
|||
field: 'createDate', |
|||
type: 'DATE', |
|||
label: '申请日期', |
|||
}, |
|||
{ |
|||
field: 'applyTime', |
|||
type: 'TIME', |
|||
label: '申请时间', |
|||
}, |
|||
{ |
|||
field: 'endTime', |
|||
type: 'CALENDAR', |
|||
label: '结束时间', |
|||
}, |
|||
] |
|||
*/ |
|||
import React, { useState } from 'react'; |
|||
import { View, Picker, Button } from '@tarojs/components'; |
|||
import { AtButton, AtSearchBar, AtDrawer, AtList, AtListItem, AtCalendar, AtModal, AtModalContent, AtModalAction } from 'taro-ui'; |
|||
import { useVisible } from '@/hooks/useCommon'; |
|||
import './index.scss'; |
|||
|
|||
const SearchBar = ({...props}) => { |
|||
const { btnName, filterOptions, onFilterSubmit, onSearch } = props; |
|||
const [value, setValue] = useState(''); |
|||
const [filterValues, setFilterValues] = useState({}); |
|||
const { visible, show, close } = useVisible(); |
|||
|
|||
const { visible: calendarVisble, show: calendarShow, close: calendarClose } = useVisible(); |
|||
const [calendarFiled, setCalendarFiled] = useState(''); |
|||
const [calendarSelectedDate, setCalendarSelectedDate] = useState(''); |
|||
const handerChange = (e) => { |
|||
setValue(e) |
|||
} |
|||
const handerActionClick = () => { |
|||
onSearch && onSearch(value) |
|||
} |
|||
const handerShowFilter = () => { |
|||
show(); |
|||
} |
|||
const handerHideFilter = () => { |
|||
close(); |
|||
} |
|||
|
|||
const handlerShowCalendar = (filed) => { |
|||
calendarShow(); |
|||
setCalendarFiled(filed) |
|||
} |
|||
//重置filter |
|||
const handerReSet = () => { |
|||
setFilterValues({}) |
|||
} |
|||
//提交filter |
|||
const handerSubmit = () => { |
|||
close(); |
|||
onFilterSubmit && onFilterSubmit(filterValues); |
|||
} |
|||
//时间选择 |
|||
const handerTimeChange = (e,filed) => { |
|||
setFilterValues({...filterValues, [filed]: e.detail.value}) |
|||
} |
|||
//日期选择 |
|||
const handerDateChange = (e,filed) => { |
|||
setFilterValues({...filterValues, [filed]: e.detail.value}) |
|||
} |
|||
//普通单选选择 |
|||
const handerSelectChange = (e,filed,list) => { |
|||
setFilterValues({...filterValues, [filed]: list[e.detail.value]}) |
|||
|
|||
} |
|||
//日历日期选择 |
|||
const handeSelectDate = (selectDate) => { |
|||
console.log(selectDate) |
|||
setCalendarSelectedDate(selectDate) |
|||
} |
|||
const handleModalConfirm = () => { |
|||
setFilterValues({...filterValues, [calendarFiled]: calendarSelectedDate?.value}) |
|||
calendarClose() |
|||
} |
|||
//根据传入的filter列表渲染item |
|||
const renderFields = () => { |
|||
const fieldItemList = []; |
|||
|
|||
if (filterOptions && filterOptions.length > 0) { |
|||
filterOptions.forEach((item, index) => { |
|||
const { label, field, type, list } = item; |
|||
switch (type) { |
|||
case 'TIME': |
|||
const TIME = ( |
|||
<View className='search-drawer-item' key={`${field}-${index}`}> |
|||
<Picker mode='time' onChange={(e) => handerTimeChange(e,field)}> |
|||
<AtList hasBorder={false}> |
|||
<AtListItem title={label || '请选择时间'} extraText={filterValues[field] || ''} /> |
|||
</AtList> |
|||
</Picker> |
|||
</View> |
|||
); |
|||
fieldItemList.push(TIME); |
|||
break; |
|||
case 'DATE': |
|||
const DATE = ( |
|||
<View className='search-drawer-item' key={`${field}-${index}`}> |
|||
<Picker mode='date' onChange={(e) => handerDateChange(e,field)}> |
|||
<AtList hasBorder={false}> |
|||
<AtListItem title={label || '请选择日期'} extraText={filterValues[field] || ''} /> |
|||
</AtList> |
|||
</Picker> |
|||
</View> |
|||
); |
|||
fieldItemList.push(DATE); |
|||
break; |
|||
case 'SELECT': |
|||
const SELECT = ( |
|||
<View className='search-drawer-item' key={`${field}-${index}`}> |
|||
<Picker mode='selector' range={Array.isArray(list) && list.map(s => s.name)} onChange={(e) =>handerSelectChange(e,field,list)}> |
|||
<AtList hasBorder={false}> |
|||
<AtListItem |
|||
title={label || '请选择'} |
|||
extraText={filterValues[field] && filterValues[field].name || ''} |
|||
/> |
|||
</AtList> |
|||
</Picker> |
|||
</View> |
|||
); |
|||
fieldItemList.push(SELECT); |
|||
break; |
|||
case 'CALENDAR': |
|||
const CALENDAR = ( |
|||
<View className='search-drawer-item' key={`${field}-${index}`}> |
|||
<AtList hasBorder={false}> |
|||
<AtListItem title={label || '请选择日期区间'} extraText={filterValues[field] && `${filterValues[field]?.start} - ${filterValues[field]?.end}` || ''} onClick={()=> handlerShowCalendar(field)} /> |
|||
</AtList> |
|||
</View> |
|||
); |
|||
fieldItemList.push(CALENDAR); |
|||
break; |
|||
default: |
|||
break; |
|||
} |
|||
}); |
|||
} |
|||
return fieldItemList; |
|||
} |
|||
|
|||
return ( |
|||
<View className='search-bar'> |
|||
{ |
|||
filterOptions && Array.isArray(filterOptions) ? |
|||
<View className='at-row at-row__align--center'> |
|||
<View className='at-col at-col-11'> |
|||
<AtSearchBar |
|||
actionName={btnName || '搜索'} |
|||
value={value} |
|||
onChange={handerChange} |
|||
onActionClick={handerActionClick} |
|||
/> |
|||
</View> |
|||
<View className='at-col at-col-1'> |
|||
<View className='at-icon at-icon-list search-filter' onClick={handerShowFilter}></View> |
|||
</View> |
|||
</View> : |
|||
<AtSearchBar |
|||
actionName={btnName || '搜索'} |
|||
value={value} |
|||
onChange={handerChange} |
|||
onActionClick={handerActionClick} |
|||
/> |
|||
} |
|||
<AtDrawer |
|||
show={visible} |
|||
onClose={handerHideFilter} |
|||
right |
|||
mask |
|||
width='70%' |
|||
> |
|||
{renderFields()} |
|||
<View className='search-drawer-item-btn'> |
|||
<View className='at-row at-row__justify--around'> |
|||
<View className='at-col at-col-5'> |
|||
<AtButton type='primary' size='small' onClick={handerReSet}>重置</AtButton> |
|||
</View> |
|||
<View className='at-col at-col-5'> |
|||
<AtButton type='primary' size='small' onClick={handerSubmit}>确定</AtButton> |
|||
</View> |
|||
</View> |
|||
</View> |
|||
</AtDrawer> |
|||
<AtModal |
|||
isOpened={calendarVisble} |
|||
onClose={()=> {calendarClose()}} |
|||
> |
|||
<AtModalContent> |
|||
<AtCalendar isMultiSelect onSelectDate={handeSelectDate} currentDate={filterValues[calendarFiled] || Date.now()} /> |
|||
</AtModalContent> |
|||
<AtModalAction > <Button onClick={()=> {calendarClose()}}>取消</Button> <Button onClick={handleModalConfirm}>确定</Button> </AtModalAction> |
|||
</AtModal> |
|||
</View> |
|||
|
|||
); |
|||
} |
|||
export default SearchBar; |
@ -0,0 +1,14 @@ |
|||
@import 'src/styles/theme'; |
|||
.search-bar { |
|||
.search-filter { |
|||
font-size: 48px; |
|||
color: $primary-color; |
|||
} |
|||
.search-drawer-item-btn { |
|||
margin-top: 20px; |
|||
} |
|||
|
|||
// .search-drawer-item { |
|||
// margin: 5px 10px; |
|||
// } |
|||
} |
@ -0,0 +1,38 @@ |
|||
import React, { Component } from 'react'; |
|||
import { CoverView } from '@tarojs/components'; |
|||
import './tabs.scss'; |
|||
|
|||
class Tabs extends Component { |
|||
constructor(props) { |
|||
super(props); |
|||
this.state = { |
|||
activeIdx:props.current || 0 |
|||
}; |
|||
} |
|||
|
|||
onCellClick(val,idx){ |
|||
this.setState({ |
|||
activeIdx: idx |
|||
}) |
|||
this.props.onChange(val,idx) |
|||
} |
|||
|
|||
render() { |
|||
const {list=[], padding} = this.props; |
|||
const {activeIdx} = this.state; |
|||
const cell = list.length===2?'cell half':'cell' |
|||
|
|||
return ( |
|||
<CoverView className='tabs-box' style={`padding: ${padding}`}> |
|||
<CoverView className='bg'></CoverView> |
|||
<CoverView className='tabs-row' style={`${list.length===2?'justify-content: space-between':''}`}> |
|||
{list.map((val,idx)=>{ |
|||
return <CoverView className={`${cell} ${activeIdx ===idx?'active':''}`} key={val.id} onClick={this.onCellClick.bind(this,val,idx)}>{val.title}</CoverView> |
|||
})} |
|||
</CoverView> |
|||
</CoverView> |
|||
); |
|||
} |
|||
} |
|||
|
|||
export default Tabs; |
@ -0,0 +1,87 @@ |
|||
.tabs-box{ |
|||
position: fixed; |
|||
top: 0; |
|||
left: 0; |
|||
width: 100%; |
|||
box-sizing: border-box; |
|||
z-index: 99; |
|||
.bg{ |
|||
position: absolute; |
|||
bottom: 50%; |
|||
left: 0; |
|||
width: 100%; |
|||
height: 100%; |
|||
background-color: #f7f7f7; |
|||
z-index: -1; |
|||
} |
|||
} |
|||
.tabs-row{ |
|||
position: relative; |
|||
display: flex; |
|||
// justify-content: space-between; |
|||
align-items: center; |
|||
height: 80px; |
|||
width: 684px; |
|||
margin: 0 auto; |
|||
box-sizing: border-box; |
|||
background-color:transparen; |
|||
font-size: 28px; |
|||
border-radius: 40px; |
|||
// box-shadow:0px 10px 10px 4px rgba(0,0,0,0.06); |
|||
border: 1px solid #00000006; |
|||
background-color: #fff; |
|||
overflow-x: scroll; |
|||
overflow-y: hidden; |
|||
z-index: 99; |
|||
// .bg{ |
|||
// position: absolute; |
|||
// top: 50%; |
|||
// transform: translateY(-50%); |
|||
// bottom: 0px; |
|||
// width: 100%; |
|||
// height: 64px; |
|||
// background-color: #fff; |
|||
// // box-shadow:0px 10px 10px 10px rgba(0,0,0,0.06); |
|||
// z-index: -1; |
|||
// } |
|||
.cell{ |
|||
// border-radius: 36px; |
|||
color: #B2B2B2; |
|||
// padding: 0px 50px; |
|||
height: 80px; |
|||
line-height: 80px; |
|||
width: 228px; |
|||
text-align: center; |
|||
flex-shrink: 0; |
|||
background-color: #fff; |
|||
} |
|||
|
|||
.active{ |
|||
background-color: #2F54FF; |
|||
color: #fff; |
|||
box-sizing: content-box; |
|||
font-size: 30px; |
|||
border-radius: 40px; |
|||
} |
|||
.half{ |
|||
width: 342px; |
|||
} |
|||
} |
|||
|
|||
//伪类阴影 父元素需设置z-index和position: relative; |
|||
// .shadow{ |
|||
// position: relative; |
|||
// &::before{ |
|||
// content:''; |
|||
// position: absolute; |
|||
// bottom: -20px; |
|||
// left: 50%; |
|||
// transform: translateX(-50%); |
|||
// width:90%; |
|||
// height:68px; |
|||
// background:rgba(0,0,0,0.06); |
|||
// border-radius:9px; |
|||
// filter:blur(15px); |
|||
// z-index: -2; |
|||
// } |
|||
// } |
@ -0,0 +1 @@ |
|||
export {}; |
@ -0,0 +1,58 @@ |
|||
import { VantComponent } from '../common/component'; |
|||
import { button } from '../mixins/button'; |
|||
import { openType } from '../mixins/open-type'; |
|||
import { canIUseFormFieldButton } from '../common/version'; |
|||
const mixins = [button, openType]; |
|||
if (canIUseFormFieldButton()) { |
|||
mixins.push('wx://form-field-button'); |
|||
} |
|||
VantComponent({ |
|||
mixins, |
|||
classes: ['hover-class', 'loading-class'], |
|||
data: { |
|||
baseStyle: '', |
|||
}, |
|||
props: { |
|||
formType: String, |
|||
icon: String, |
|||
classPrefix: { |
|||
type: String, |
|||
value: 'van-icon', |
|||
}, |
|||
plain: Boolean, |
|||
block: Boolean, |
|||
round: Boolean, |
|||
square: Boolean, |
|||
loading: Boolean, |
|||
hairline: Boolean, |
|||
disabled: Boolean, |
|||
loadingText: String, |
|||
customStyle: String, |
|||
loadingType: { |
|||
type: String, |
|||
value: 'circular', |
|||
}, |
|||
type: { |
|||
type: String, |
|||
value: 'default', |
|||
}, |
|||
dataset: null, |
|||
size: { |
|||
type: String, |
|||
value: 'normal', |
|||
}, |
|||
loadingSize: { |
|||
type: String, |
|||
value: '20px', |
|||
}, |
|||
color: String, |
|||
}, |
|||
methods: { |
|||
onClick() { |
|||
if (!this.data.loading) { |
|||
this.$emit('click'); |
|||
} |
|||
}, |
|||
noop() {}, |
|||
}, |
|||
}); |
@ -0,0 +1,7 @@ |
|||
{ |
|||
"component": true, |
|||
"usingComponents": { |
|||
"van-icon": "../icon/index", |
|||
"van-loading": "../loading/index" |
|||
} |
|||
} |
@ -0,0 +1,53 @@ |
|||
<wxs src="../wxs/utils.wxs" module="utils" /> |
|||
<wxs src="./index.wxs" module="computed" /> |
|||
|
|||
<button |
|||
id="{{ id }}" |
|||
data-detail="{{ dataset }}" |
|||
class="custom-class {{ utils.bem('button', [type, size, { block, round, plain, square, loading, disabled, hairline, unclickable: disabled || loading }]) }} {{ hairline ? 'van-hairline--surround' : '' }}" |
|||
hover-class="van-button--active hover-class" |
|||
lang="{{ lang }}" |
|||
form-type="{{ formType }}" |
|||
style="{{ computed.rootStyle({ plain, color, customStyle }) }}" |
|||
open-type="{{ disabled ? '' : openType }}" |
|||
business-id="{{ businessId }}" |
|||
session-from="{{ sessionFrom }}" |
|||
send-message-title="{{ sendMessageTitle }}" |
|||
send-message-path="{{ sendMessagePath }}" |
|||
send-message-img="{{ sendMessageImg }}" |
|||
show-message-card="{{ showMessageCard }}" |
|||
app-parameter="{{ appParameter }}" |
|||
aria-label="{{ ariaLabel }}" |
|||
bindtap="{{ !disabled ? 'onClick' : 'noop' }}" |
|||
bindgetuserinfo="bindGetUserInfo" |
|||
bindcontact="bindContact" |
|||
bindgetphonenumber="bindGetPhoneNumber" |
|||
binderror="bindError" |
|||
bindlaunchapp="bindLaunchApp" |
|||
bindopensetting="bindOpenSetting" |
|||
> |
|||
<block wx:if="{{ loading }}"> |
|||
<van-loading |
|||
custom-class="loading-class" |
|||
size="{{ loadingSize }}" |
|||
type="{{ loadingType }}" |
|||
color="{{ computed.loadingColor({ type, color, plain }) }}" |
|||
/> |
|||
<view wx:if="{{ loadingText }}" class="van-button__loading-text"> |
|||
{{ loadingText }} |
|||
</view> |
|||
</block> |
|||
<block wx:else> |
|||
<van-icon |
|||
wx:if="{{ icon }}" |
|||
size="1.2em" |
|||
name="{{ icon }}" |
|||
class-prefix="{{ classPrefix }}" |
|||
class="van-button__icon" |
|||
custom-style="line-height: inherit;" |
|||
/> |
|||
<view class="van-button__text"> |
|||
<slot /> |
|||
</view> |
|||
</block> |
|||
</button> |
@ -0,0 +1,39 @@ |
|||
/* eslint-disable */ |
|||
var style = require('../wxs/style.wxs'); |
|||
|
|||
function rootStyle(data) { |
|||
if (!data.color) { |
|||
return data.customStyle; |
|||
} |
|||
|
|||
var properties = { |
|||
color: data.plain ? data.color : '#fff', |
|||
background: data.plain ? null : data.color, |
|||
}; |
|||
|
|||
// hide border when color is linear-gradient |
|||
if (data.color.indexOf('gradient') !== -1) { |
|||
properties.border = 0; |
|||
} else { |
|||
properties['border-color'] = data.color; |
|||
} |
|||
|
|||
return style([properties, data.customStyle]); |
|||
} |
|||
|
|||
function loadingColor(data) { |
|||
if (data.plain) { |
|||
return data.color ? data.color : '#c9c9c9'; |
|||
} |
|||
|
|||
if (data.type === 'default') { |
|||
return '#c9c9c9'; |
|||
} |
|||
|
|||
return '#fff'; |
|||
} |
|||
|
|||
module.exports = { |
|||
rootStyle: rootStyle, |
|||
loadingColor: loadingColor, |
|||
}; |
@ -0,0 +1 @@ |
|||
@import '../common/index.wxss';.van-button{position:relative;display:-webkit-inline-flex;display:inline-flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:center;justify-content:center;box-sizing:border-box;padding:0;text-align:center;vertical-align:middle;-webkit-appearance:none;-webkit-text-size-adjust:100%;height:44px;height:var(--button-default-height,44px);line-height:20px;line-height:var(--button-line-height,20px);font-size:16px;font-size:var(--button-default-font-size,16px);transition:opacity .2s;transition:opacity var(--animation-duration-fast,.2s);border-radius:2px;border-radius:var(--button-border-radius,2px)}.van-button:before{position:absolute;top:50%;left:50%;width:100%;height:100%;border:inherit;border-radius:inherit;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);opacity:0;content:" ";background-color:#000;background-color:var(--black,#000);border-color:#000;border-color:var(--black,#000)}.van-button:after{border-width:0}.van-button--active:before{opacity:.15}.van-button--unclickable:after{display:none}.van-button--default{color:#323233;color:var(--button-default-color,#323233);background:#fff;background:var(--button-default-background-color,#fff);border:1px solid #ebedf0;border:var(--button-border-width,1px) solid var(--button-default-border-color,#ebedf0)}.van-button--primary{color:#fff;color:var(--button-primary-color,#fff);background:#07c160;background:var(--button-primary-background-color,#07c160);border:1px solid #07c160;border:var(--button-border-width,1px) solid var(--button-primary-border-color,#07c160)}.van-button--info{color:#fff;color:var(--button-info-color,#fff);background:#1989fa;background:var(--button-info-background-color,#1989fa);border:1px solid #1989fa;border:var(--button-border-width,1px) solid var(--button-info-border-color,#1989fa)}.van-button--danger{color:#fff;color:var(--button-danger-color,#fff);background:#ee0a24;background:var(--button-danger-background-color,#ee0a24);border:1px solid #ee0a24;border:var(--button-border-width,1px) solid var(--button-danger-border-color,#ee0a24)}.van-button--warning{color:#fff;color:var(--button-warning-color,#fff);background:#ff976a;background:var(--button-warning-background-color,#ff976a);border:1px solid #ff976a;border:var(--button-border-width,1px) solid var(--button-warning-border-color,#ff976a)}.van-button--plain{background:#fff;background:var(--button-plain-background-color,#fff)}.van-button--plain.van-button--primary{color:#07c160;color:var(--button-primary-background-color,#07c160)}.van-button--plain.van-button--info{color:#1989fa;color:var(--button-info-background-color,#1989fa)}.van-button--plain.van-button--danger{color:#ee0a24;color:var(--button-danger-background-color,#ee0a24)}.van-button--plain.van-button--warning{color:#ff976a;color:var(--button-warning-background-color,#ff976a)}.van-button--large{width:100%;height:50px;height:var(--button-large-height,50px)}.van-button--normal{padding:0 15px;font-size:14px;font-size:var(--button-normal-font-size,14px)}.van-button--small{min-width:60px;min-width:var(--button-small-min-width,60px);height:30px;height:var(--button-small-height,30px);padding:0 8px;padding:0 var(--padding-xs,8px);font-size:12px;font-size:var(--button-small-font-size,12px)}.van-button--mini{display:inline-block;min-width:50px;min-width:var(--button-mini-min-width,50px);height:22px;height:var(--button-mini-height,22px);font-size:10px;font-size:var(--button-mini-font-size,10px)}.van-button--mini+.van-button--mini{margin-left:5px}.van-button--block{display:-webkit-flex;display:flex;width:100%}.van-button--round{border-radius:999px;border-radius:var(--button-round-border-radius,999px)}.van-button--square{border-radius:0}.van-button--disabled{opacity:.5;opacity:var(--button-disabled-opacity,.5)}.van-button__text{display:inline}.van-button__icon+.van-button__text:not(:empty),.van-button__loading-text{margin-left:4px}.van-button__icon{min-width:1em;line-height:inherit!important;vertical-align:top}.van-button--hairline{padding-top:1px;border-width:0}.van-button--hairline:after{border-color:inherit;border-width:1px;border-radius:4px;border-radius:calc(var(--button-border-radius, 2px)*2)}.van-button--hairline.van-button--round:after{border-radius:999px;border-radius:var(--button-round-border-radius,999px)}.van-button--hairline.van-button--square:after{border-radius:0} |
@ -0,0 +1 @@ |
|||
export {}; |
@ -0,0 +1,10 @@ |
|||
import { VantComponent } from '../common/component'; |
|||
VantComponent({ |
|||
props: { |
|||
title: String, |
|||
border: { |
|||
type: Boolean, |
|||
value: true, |
|||
}, |
|||
}, |
|||
}); |
@ -0,0 +1,3 @@ |
|||
{ |
|||
"component": true |
|||
} |
@ -0,0 +1,9 @@ |
|||
<view |
|||
wx:if="{{ title }}" |
|||
class="van-cell-group__title" |
|||
> |
|||
{{ title }} |
|||
</view> |
|||
<view class="custom-class van-cell-group {{ border ? 'van-hairline--top-bottom' : '' }}"> |
|||
<slot /> |
|||
</view> |
@ -0,0 +1 @@ |
|||
@import '../common/index.wxss';.van-cell-group__title{padding:16px 16px 8px;padding:var(--cell-group-title-padding,16px 16px 8px);font-size:14px;font-size:var(--cell-group-title-font-size,14px);line-height:16px;line-height:var(--cell-group-title-line-height,16px);color:#969799;color:var(--cell-group-title-color,#969799)} |
@ -0,0 +1 @@ |
|||
export {}; |
@ -0,0 +1,38 @@ |
|||
import { link } from '../mixins/link'; |
|||
import { VantComponent } from '../common/component'; |
|||
VantComponent({ |
|||
classes: [ |
|||
'title-class', |
|||
'label-class', |
|||
'value-class', |
|||
'right-icon-class', |
|||
'hover-class', |
|||
], |
|||
mixins: [link], |
|||
props: { |
|||
title: null, |
|||
value: null, |
|||
icon: String, |
|||
size: String, |
|||
label: String, |
|||
center: Boolean, |
|||
isLink: Boolean, |
|||
required: Boolean, |
|||
clickable: Boolean, |
|||
titleWidth: String, |
|||
customStyle: String, |
|||
arrowDirection: String, |
|||
useLabelSlot: Boolean, |
|||
border: { |
|||
type: Boolean, |
|||
value: true, |
|||
}, |
|||
titleStyle: String, |
|||
}, |
|||
methods: { |
|||
onClick(event) { |
|||
this.$emit('click', event.detail); |
|||
this.jumpLink(); |
|||
}, |
|||
}, |
|||
}); |
@ -0,0 +1,6 @@ |
|||
{ |
|||
"component": true, |
|||
"usingComponents": { |
|||
"van-icon": "../icon/index" |
|||
} |
|||
} |
@ -0,0 +1,46 @@ |
|||
<wxs src="../wxs/utils.wxs" module="utils" /> |
|||
<wxs src="./index.wxs" module="computed" /> |
|||
|
|||
<view |
|||
class="custom-class {{ utils.bem('cell', [size, { center, required, borderless: !border, clickable: isLink || clickable }]) }}" |
|||
hover-class="van-cell--hover hover-class" |
|||
hover-stay-time="70" |
|||
style="{{ customStyle }}" |
|||
bind:tap="onClick" |
|||
> |
|||
<van-icon |
|||
wx:if="{{ icon }}" |
|||
name="{{ icon }}" |
|||
class="van-cell__left-icon-wrap" |
|||
custom-class="van-cell__left-icon" |
|||
/> |
|||
<slot wx:else name="icon" /> |
|||
|
|||
<view |
|||
style="{{ computed.titleStyle({ titleWidth, titleStyle }) }}" |
|||
class="van-cell__title title-class" |
|||
> |
|||
<block wx:if="{{ title }}">{{ title }}</block> |
|||
<slot wx:else name="title" /> |
|||
|
|||
<view wx:if="{{ label || useLabelSlot }}" class="van-cell__label label-class"> |
|||
<slot wx:if="{{ useLabelSlot }}" name="label" /> |
|||
<block wx:elif="{{ label }}">{{ label }}</block> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="van-cell__value value-class"> |
|||
<block wx:if="{{ value || value === 0 }}">{{ value }}</block> |
|||
<slot wx:else /> |
|||
</view> |
|||
|
|||
<van-icon |
|||
wx:if="{{ isLink }}" |
|||
name="{{ arrowDirection ? 'arrow' + '-' + arrowDirection : 'arrow' }}" |
|||
class="van-cell__right-icon-wrap right-icon-class" |
|||
custom-class="van-cell__right-icon" |
|||
/> |
|||
<slot wx:else name="right-icon" /> |
|||
|
|||
<slot name="extra" /> |
|||
</view> |
@ -0,0 +1,17 @@ |
|||
/* eslint-disable */ |
|||
var style = require('../wxs/style.wxs'); |
|||
var addUnit = require('../wxs/add-unit.wxs'); |
|||
|
|||
function titleStyle(data) { |
|||
return style([ |
|||
{ |
|||
'max-width': addUnit(data.titleWidth), |
|||
'min-width': addUnit(data.titleWidth), |
|||
}, |
|||
data.titleStyle, |
|||
]); |
|||
} |
|||
|
|||
module.exports = { |
|||
titleStyle: titleStyle, |
|||
}; |
@ -0,0 +1 @@ |
|||
@import '../common/index.wxss';.van-cell{position:relative;display:-webkit-flex;display:flex;box-sizing:border-box;width:100%;padding:10px 16px;padding:var(--cell-vertical-padding,10px) var(--cell-horizontal-padding,16px);font-size:14px;font-size:var(--cell-font-size,14px);line-height:24px;line-height:var(--cell-line-height,24px);color:#323233;color:var(--cell-text-color,#323233);background-color:#fff;background-color:var(--cell-background-color,#fff)}.van-cell:after{position:absolute;box-sizing:border-box;-webkit-transform-origin:center;transform-origin:center;content:" ";pointer-events:none;right:16px;bottom:0;left:16px;border-bottom:1px solid #ebedf0;-webkit-transform:scaleY(.5);transform:scaleY(.5)}.van-cell--borderless:after{display:none}.van-cell-group{background-color:#fff;background-color:var(--cell-background-color,#fff)}.van-cell__label{margin-top:3px;margin-top:var(--cell-label-margin-top,3px);font-size:12px;font-size:var(--cell-label-font-size,12px);line-height:18px;line-height:var(--cell-label-line-height,18px);color:#969799;color:var(--cell-label-color,#969799)}.van-cell__value{overflow:hidden;text-align:right;vertical-align:middle;color:#969799;color:var(--cell-value-color,#969799)}.van-cell__title,.van-cell__value{-webkit-flex:1;flex:1}.van-cell__title:empty,.van-cell__value:empty{display:none}.van-cell__left-icon-wrap,.van-cell__right-icon-wrap{display:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;height:24px;height:var(--cell-line-height,24px);font-size:16px;font-size:var(--cell-icon-size,16px)}.van-cell__left-icon-wrap{margin-right:4px;margin-right:var(--padding-base,4px)}.van-cell__right-icon-wrap{margin-left:4px;margin-left:var(--padding-base,4px);color:#969799;color:var(--cell-right-icon-color,#969799)}.van-cell__left-icon{vertical-align:middle}.van-cell__left-icon,.van-cell__right-icon{line-height:24px;line-height:var(--cell-line-height,24px)}.van-cell--clickable.van-cell--hover{background-color:#f2f3f5;background-color:var(--cell-active-color,#f2f3f5)}.van-cell--required{overflow:visible}.van-cell--required:before{position:absolute;content:"*";left:8px;left:var(--padding-xs,8px);font-size:14px;font-size:var(--cell-font-size,14px);color:#ee0a24;color:var(--cell-required-color,#ee0a24)}.van-cell--center{-webkit-align-items:center;align-items:center}.van-cell--large{padding-top:12px;padding-top:var(--cell-large-vertical-padding,12px);padding-bottom:12px;padding-bottom:var(--cell-large-vertical-padding,12px)}.van-cell--large .van-cell__title{font-size:16px;font-size:var(--cell-large-title-font-size,16px)}.van-cell--large .van-cell__value{font-size:16px;font-size:var(--cell-large-value-font-size,16px)}.van-cell--large .van-cell__label{font-size:14px;font-size:var(--cell-large-label-font-size,14px)} |
@ -0,0 +1 @@ |
|||
export {}; |
@ -0,0 +1,9 @@ |
|||
import { useParent } from '../common/relation'; |
|||
import { VantComponent } from '../common/component'; |
|||
VantComponent({ |
|||
relation: useParent('row'), |
|||
props: { |
|||
span: Number, |
|||
offset: Number, |
|||
}, |
|||
}); |
@ -0,0 +1,3 @@ |
|||
{ |
|||
"component": true |
|||
} |
@ -0,0 +1,9 @@ |
|||
<wxs src="../wxs/utils.wxs" module="utils" /> |
|||
<wxs src="./index.wxs" module="computed" /> |
|||
|
|||
<view |
|||
class="custom-class {{ utils.bem('col', [span]) }} {{ offset ? 'van-col--offset-' + offset : '' }}" |
|||
style="{{ computed.rootStyle({ gutter }) }}" |
|||
> |
|||
<slot /> |
|||
</view> |
@ -0,0 +1,18 @@ |
|||
/* eslint-disable */ |
|||
var style = require('../wxs/style.wxs'); |
|||
var addUnit = require('../wxs/add-unit.wxs'); |
|||
|
|||
function rootStyle(data) { |
|||
if (!data.gutter) { |
|||
return ''; |
|||
} |
|||
|
|||
return style({ |
|||
'padding-right': addUnit(data.gutter / 2), |
|||
'padding-left': addUnit(data.gutter / 2), |
|||
}); |
|||
} |
|||
|
|||
module.exports = { |
|||
rootStyle: rootStyle, |
|||
}; |
@ -0,0 +1 @@ |
|||
@import '../common/index.wxss';.van-col{float:left;box-sizing:border-box}.van-col--1{width:4.16666667%}.van-col--offset-1{margin-left:4.16666667%}.van-col--2{width:8.33333333%}.van-col--offset-2{margin-left:8.33333333%}.van-col--3{width:12.5%}.van-col--offset-3{margin-left:12.5%}.van-col--4{width:16.66666667%}.van-col--offset-4{margin-left:16.66666667%}.van-col--5{width:20.83333333%}.van-col--offset-5{margin-left:20.83333333%}.van-col--6{width:25%}.van-col--offset-6{margin-left:25%}.van-col--7{width:29.16666667%}.van-col--offset-7{margin-left:29.16666667%}.van-col--8{width:33.33333333%}.van-col--offset-8{margin-left:33.33333333%}.van-col--9{width:37.5%}.van-col--offset-9{margin-left:37.5%}.van-col--10{width:41.66666667%}.van-col--offset-10{margin-left:41.66666667%}.van-col--11{width:45.83333333%}.van-col--offset-11{margin-left:45.83333333%}.van-col--12{width:50%}.van-col--offset-12{margin-left:50%}.van-col--13{width:54.16666667%}.van-col--offset-13{margin-left:54.16666667%}.van-col--14{width:58.33333333%}.van-col--offset-14{margin-left:58.33333333%}.van-col--15{width:62.5%}.van-col--offset-15{margin-left:62.5%}.van-col--16{width:66.66666667%}.van-col--offset-16{margin-left:66.66666667%}.van-col--17{width:70.83333333%}.van-col--offset-17{margin-left:70.83333333%}.van-col--18{width:75%}.van-col--offset-18{margin-left:75%}.van-col--19{width:79.16666667%}.van-col--offset-19{margin-left:79.16666667%}.van-col--20{width:83.33333333%}.van-col--offset-20{margin-left:83.33333333%}.van-col--21{width:87.5%}.van-col--offset-21{margin-left:87.5%}.van-col--22{width:91.66666667%}.van-col--offset-22{margin-left:91.66666667%}.van-col--23{width:95.83333333%}.van-col--offset-23{margin-left:95.83333333%}.van-col--24{width:100%}.van-col--offset-24{margin-left:100%} |
@ -0,0 +1,7 @@ |
|||
export declare const RED = "#ee0a24"; |
|||
export declare const BLUE = "#1989fa"; |
|||
export declare const WHITE = "#fff"; |
|||
export declare const GREEN = "#07c160"; |
|||
export declare const ORANGE = "#ff976a"; |
|||
export declare const GRAY = "#323233"; |
|||
export declare const GRAY_DARK = "#969799"; |
@ -0,0 +1,7 @@ |
|||
export const RED = '#ee0a24'; |
|||
export const BLUE = '#1989fa'; |
|||
export const WHITE = '#fff'; |
|||
export const GREEN = '#07c160'; |
|||
export const ORANGE = '#ff976a'; |
|||
export const GRAY = '#323233'; |
|||
export const GRAY_DARK = '#969799'; |
@ -0,0 +1,8 @@ |
|||
/// <reference types="miniprogram-api-typings" />
|
|||
import { VantComponentOptions } from '../definitions/index'; |
|||
declare function VantComponent< |
|||
Data extends WechatMiniprogram.Component.DataOption, |
|||
Props extends WechatMiniprogram.Component.PropertyOption, |
|||
Methods extends WechatMiniprogram.Component.MethodOption |
|||
>(vantOptions: VantComponentOptions<Data, Props, Methods>): void; |
|||
export { VantComponent }; |
@ -0,0 +1,45 @@ |
|||
import { basic } from '../mixins/basic'; |
|||
function mapKeys(source, target, map) { |
|||
Object.keys(map).forEach((key) => { |
|||
if (source[key]) { |
|||
target[map[key]] = source[key]; |
|||
} |
|||
}); |
|||
} |
|||
function VantComponent(vantOptions) { |
|||
const options = {}; |
|||
mapKeys(vantOptions, options, { |
|||
data: 'data', |
|||
props: 'properties', |
|||
mixins: 'behaviors', |
|||
methods: 'methods', |
|||
beforeCreate: 'created', |
|||
created: 'attached', |
|||
mounted: 'ready', |
|||
destroyed: 'detached', |
|||
classes: 'externalClasses', |
|||
}); |
|||
// add default externalClasses
|
|||
options.externalClasses = options.externalClasses || []; |
|||
options.externalClasses.push('custom-class'); |
|||
// add default behaviors
|
|||
options.behaviors = options.behaviors || []; |
|||
options.behaviors.push(basic); |
|||
// add relations
|
|||
const { relation } = vantOptions; |
|||
if (relation) { |
|||
options.relations = relation.relations; |
|||
options.behaviors.push(relation.mixin); |
|||
} |
|||
// map field to form-field behavior
|
|||
if (vantOptions.field) { |
|||
options.behaviors.push('wx://form-field'); |
|||
} |
|||
// add default options
|
|||
options.options = { |
|||
multipleSlots: true, |
|||
addGlobalClass: true, |
|||
}; |
|||
Component(options); |
|||
} |
|||
export { VantComponent }; |
@ -0,0 +1 @@ |
|||
.van-ellipsis{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.van-multi-ellipsis--l2{-webkit-line-clamp:2}.van-multi-ellipsis--l2,.van-multi-ellipsis--l3{display:-webkit-box;overflow:hidden;text-overflow:ellipsis;-webkit-box-orient:vertical}.van-multi-ellipsis--l3{-webkit-line-clamp:3}.van-clearfix:after{display:table;clear:both;content:""}.van-hairline,.van-hairline--bottom,.van-hairline--left,.van-hairline--right,.van-hairline--surround,.van-hairline--top,.van-hairline--top-bottom{position:relative}.van-hairline--bottom:after,.van-hairline--left:after,.van-hairline--right:after,.van-hairline--surround:after,.van-hairline--top-bottom:after,.van-hairline--top:after,.van-hairline:after{position:absolute;box-sizing:border-box;-webkit-transform-origin:center;transform-origin:center;content:" ";pointer-events:none;top:-50%;right:-50%;bottom:-50%;left:-50%;border:0 solid #ebedf0;-webkit-transform:scale(.5);transform:scale(.5)}.van-hairline--top:after{border-top-width:1px}.van-hairline--left:after{border-left-width:1px}.van-hairline--right:after{border-right-width:1px}.van-hairline--bottom:after{border-bottom-width:1px}.van-hairline--top-bottom:after{border-width:1px 0}.van-hairline--surround:after{border-width:1px} |
@ -0,0 +1,21 @@ |
|||
/// <reference types="miniprogram-api-typings" />
|
|||
declare type TrivialInstance = WechatMiniprogram.Component.TrivialInstance; |
|||
export declare function useParent( |
|||
name: string, |
|||
onEffect?: (this: TrivialInstance) => void |
|||
): { |
|||
relations: { |
|||
[x: string]: WechatMiniprogram.Component.RelationOption; |
|||
}; |
|||
mixin: string; |
|||
}; |
|||
export declare function useChildren( |
|||
name: string, |
|||
onEffect?: (this: TrivialInstance, target: TrivialInstance) => void |
|||
): { |
|||
relations: { |
|||
[x: string]: WechatMiniprogram.Component.RelationOption; |
|||
}; |
|||
mixin: string; |
|||
}; |
|||
export {}; |
@ -0,0 +1,64 @@ |
|||
export function useParent(name, onEffect) { |
|||
const path = `../${name}/index`; |
|||
return { |
|||
relations: { |
|||
[path]: { |
|||
type: 'ancestor', |
|||
linked() { |
|||
onEffect && onEffect.call(this); |
|||
}, |
|||
linkChanged() { |
|||
onEffect && onEffect.call(this); |
|||
}, |
|||
unlinked() { |
|||
onEffect && onEffect.call(this); |
|||
}, |
|||
}, |
|||
}, |
|||
mixin: Behavior({ |
|||
created() { |
|||
Object.defineProperty(this, 'parent', { |
|||
get: () => this.getRelationNodes(path)[0], |
|||
}); |
|||
Object.defineProperty(this, 'index', { |
|||
// @ts-ignore
|
|||
get: () => { |
|||
var _a, _b; |
|||
return (_b = |
|||
(_a = this.parent) === null || _a === void 0 |
|||
? void 0 |
|||
: _a.children) === null || _b === void 0 |
|||
? void 0 |
|||
: _b.indexOf(this); |
|||
}, |
|||
}); |
|||
}, |
|||
}), |
|||
}; |
|||
} |
|||
export function useChildren(name, onEffect) { |
|||
const path = `../${name}/index`; |
|||
return { |
|||
relations: { |
|||
[path]: { |
|||
type: 'descendant', |
|||
linked(target) { |
|||
onEffect && onEffect.call(this, target); |
|||
}, |
|||
linkChanged(target) { |
|||
onEffect && onEffect.call(this, target); |
|||
}, |
|||
unlinked(target) { |
|||
onEffect && onEffect.call(this, target); |
|||
}, |
|||
}, |
|||
}, |
|||
mixin: Behavior({ |
|||
created() { |
|||
Object.defineProperty(this, 'children', { |
|||
get: () => this.getRelationNodes(path) || [], |
|||
}); |
|||
}, |
|||
}), |
|||
}; |
|||
} |
@ -0,0 +1 @@ |
|||
.van-clearfix:after{display:table;clear:both;content:""} |
@ -0,0 +1 @@ |
|||
.van-ellipsis{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.van-multi-ellipsis--l2{-webkit-line-clamp:2}.van-multi-ellipsis--l2,.van-multi-ellipsis--l3{display:-webkit-box;overflow:hidden;text-overflow:ellipsis;-webkit-box-orient:vertical}.van-multi-ellipsis--l3{-webkit-line-clamp:3} |
@ -0,0 +1 @@ |
|||
.van-hairline,.van-hairline--bottom,.van-hairline--left,.van-hairline--right,.van-hairline--surround,.van-hairline--top,.van-hairline--top-bottom{position:relative}.van-hairline--bottom:after,.van-hairline--left:after,.van-hairline--right:after,.van-hairline--surround:after,.van-hairline--top-bottom:after,.van-hairline--top:after,.van-hairline:after{position:absolute;box-sizing:border-box;-webkit-transform-origin:center;transform-origin:center;content:" ";pointer-events:none;top:-50%;right:-50%;bottom:-50%;left:-50%;border:0 solid #ebedf0;-webkit-transform:scale(.5);transform:scale(.5)}.van-hairline--top:after{border-top-width:1px}.van-hairline--left:after{border-left-width:1px}.van-hairline--right:after{border-right-width:1px}.van-hairline--bottom:after{border-bottom-width:1px}.van-hairline--top-bottom:after{border-width:1px 0}.van-hairline--surround:after{border-width:1px} |
@ -0,0 +1,30 @@ |
|||
/// <reference types="miniprogram-api-typings" />
|
|||
export declare function range(num: number, min: number, max: number): number; |
|||
export declare function nextTick(cb: (...args: any[]) => void): void; |
|||
export declare function getSystemInfoSync(): WechatMiniprogram.GetSystemInfoSyncResult; |
|||
export declare function addUnit(value?: string | number): string | undefined; |
|||
export declare function requestAnimationFrame( |
|||
cb: () => void |
|||
): number | WechatMiniprogram.NodesRef; |
|||
export declare function pickExclude(obj: unknown, keys: string[]): {}; |
|||
export declare function getRect( |
|||
context: WechatMiniprogram.Component.TrivialInstance, |
|||
selector: string |
|||
): Promise<WechatMiniprogram.BoundingClientRectCallbackResult>; |
|||
export declare function getAllRect( |
|||
context: WechatMiniprogram.Component.TrivialInstance, |
|||
selector: string |
|||
): Promise<WechatMiniprogram.BoundingClientRectCallbackResult[]>; |
|||
export declare function groupSetData( |
|||
context: WechatMiniprogram.Component.TrivialInstance, |
|||
cb: () => void |
|||
): void; |
|||
export declare function toPromise( |
|||
promiseLike: Promise<unknown> | unknown |
|||
): Promise<unknown>; |
|||
export declare function getCurrentPage<T>(): T & |
|||
WechatMiniprogram.OptionalInterface<WechatMiniprogram.Page.ILifetime> & |
|||
WechatMiniprogram.Page.InstanceProperties & |
|||
WechatMiniprogram.Page.InstanceMethods<Record<string, any>> & |
|||
WechatMiniprogram.Page.Data<Record<string, any>> & |
|||
Record<string, any>; |
@ -0,0 +1,89 @@ |
|||
import { isDef, isNumber, isPlainObject, isPromise } from './validator'; |
|||
import { canIUseGroupSetData, canIUseNextTick } from './version'; |
|||
export function range(num, min, max) { |
|||
return Math.min(Math.max(num, min), max); |
|||
} |
|||
export function nextTick(cb) { |
|||
if (canIUseNextTick()) { |
|||
wx.nextTick(cb); |
|||
} else { |
|||
setTimeout(() => { |
|||
cb(); |
|||
}, 1000 / 30); |
|||
} |
|||
} |
|||
let systemInfo; |
|||
export function getSystemInfoSync() { |
|||
if (systemInfo == null) { |
|||
systemInfo = wx.getSystemInfoSync(); |
|||
} |
|||
return systemInfo; |
|||
} |
|||
export function addUnit(value) { |
|||
if (!isDef(value)) { |
|||
return undefined; |
|||
} |
|||
value = String(value); |
|||
return isNumber(value) ? `${value}px` : value; |
|||
} |
|||
export function requestAnimationFrame(cb) { |
|||
const systemInfo = getSystemInfoSync(); |
|||
if (systemInfo.platform === 'devtools') { |
|||
return setTimeout(() => { |
|||
cb(); |
|||
}, 1000 / 30); |
|||
} |
|||
return wx |
|||
.createSelectorQuery() |
|||
.selectViewport() |
|||
.boundingClientRect() |
|||
.exec(() => { |
|||
cb(); |
|||
}); |
|||
} |
|||
export function pickExclude(obj, keys) { |
|||
if (!isPlainObject(obj)) { |
|||
return {}; |
|||
} |
|||
return Object.keys(obj).reduce((prev, key) => { |
|||
if (!keys.includes(key)) { |
|||
prev[key] = obj[key]; |
|||
} |
|||
return prev; |
|||
}, {}); |
|||
} |
|||
export function getRect(context, selector) { |
|||
return new Promise((resolve) => { |
|||
wx.createSelectorQuery() |
|||
.in(context) |
|||
.select(selector) |
|||
.boundingClientRect() |
|||
.exec((rect = []) => resolve(rect[0])); |
|||
}); |
|||
} |
|||
export function getAllRect(context, selector) { |
|||
return new Promise((resolve) => { |
|||
wx.createSelectorQuery() |
|||
.in(context) |
|||
.selectAll(selector) |
|||
.boundingClientRect() |
|||
.exec((rect = []) => resolve(rect[0])); |
|||
}); |
|||
} |
|||
export function groupSetData(context, cb) { |
|||
if (canIUseGroupSetData()) { |
|||
context.groupSetData(cb); |
|||
} else { |
|||
cb(); |
|||
} |
|||
} |
|||
export function toPromise(promiseLike) { |
|||
if (isPromise(promiseLike)) { |
|||
return promiseLike; |
|||
} |
|||
return Promise.resolve(promiseLike); |
|||
} |
|||
export function getCurrentPage() { |
|||
const pages = getCurrentPages(); |
|||
return pages[pages.length - 1]; |
|||
} |
@ -0,0 +1,11 @@ |
|||
export declare function isFunction(val: unknown): val is Function; |
|||
export declare function isPlainObject( |
|||
val: unknown |
|||
): val is Record<string, unknown>; |
|||
export declare function isPromise<T = unknown>(val: unknown): val is Promise<T>; |
|||
export declare function isDef(value: unknown): boolean; |
|||
export declare function isObj(x: unknown): x is Record<string, unknown>; |
|||
export declare function isNumber(value: string): boolean; |
|||
export declare function isBoolean(value: unknown): value is boolean; |
|||
export declare function isImageUrl(url: string): boolean; |
|||
export declare function isVideoUrl(url: string): boolean; |
@ -0,0 +1,30 @@ |
|||
export function isFunction(val) { |
|||
return typeof val === 'function'; |
|||
} |
|||
export function isPlainObject(val) { |
|||
return val !== null && typeof val === 'object' && !Array.isArray(val); |
|||
} |
|||
export function isPromise(val) { |
|||
return isPlainObject(val) && isFunction(val.then) && isFunction(val.catch); |
|||
} |
|||
export function isDef(value) { |
|||
return value !== undefined && value !== null; |
|||
} |
|||
export function isObj(x) { |
|||
const type = typeof x; |
|||
return x !== null && (type === 'object' || type === 'function'); |
|||
} |
|||
export function isNumber(value) { |
|||
return /^\d+(\.\d+)?$/.test(value); |
|||
} |
|||
export function isBoolean(value) { |
|||
return typeof value === 'boolean'; |
|||
} |
|||
const IMAGE_REGEXP = /\.(jpeg|jpg|gif|png|svg|webp|jfif|bmp|dpg)/i; |
|||
const VIDEO_REGEXP = /\.(mp4|mpg|mpeg|dat|asf|avi|rm|rmvb|mov|wmv|flv|mkv)/i; |
|||
export function isImageUrl(url) { |
|||
return IMAGE_REGEXP.test(url); |
|||
} |
|||
export function isVideoUrl(url) { |
|||
return VIDEO_REGEXP.test(url); |
|||
} |
@ -0,0 +1,6 @@ |
|||
export declare function canIUseModel(): boolean; |
|||
export declare function canIUseFormFieldButton(): boolean; |
|||
export declare function canIUseAnimate(): boolean; |
|||
export declare function canIUseGroupSetData(): boolean; |
|||
export declare function canIUseNextTick(): boolean; |
|||
export declare function canIUseCanvas2d(): boolean; |
@ -0,0 +1,45 @@ |
|||
import { getSystemInfoSync } from './utils'; |
|||
function compareVersion(v1, v2) { |
|||
v1 = v1.split('.'); |
|||
v2 = v2.split('.'); |
|||
const len = Math.max(v1.length, v2.length); |
|||
while (v1.length < len) { |
|||
v1.push('0'); |
|||
} |
|||
while (v2.length < len) { |
|||
v2.push('0'); |
|||
} |
|||
for (let i = 0; i < len; i++) { |
|||
const num1 = parseInt(v1[i], 10); |
|||
const num2 = parseInt(v2[i], 10); |
|||
if (num1 > num2) { |
|||
return 1; |
|||
} |
|||
if (num1 < num2) { |
|||
return -1; |
|||
} |
|||
} |
|||
return 0; |
|||
} |
|||
function gte(version) { |
|||
const system = getSystemInfoSync(); |
|||
return compareVersion(system.SDKVersion, version) >= 0; |
|||
} |
|||
export function canIUseModel() { |
|||
return gte('2.9.3'); |
|||
} |
|||
export function canIUseFormFieldButton() { |
|||
return gte('2.10.3'); |
|||
} |
|||
export function canIUseAnimate() { |
|||
return gte('2.9.0'); |
|||
} |
|||
export function canIUseGroupSetData() { |
|||
return gte('2.4.0'); |
|||
} |
|||
export function canIUseNextTick() { |
|||
return wx.canIUse('nextTick'); |
|||
} |
|||
export function canIUseCanvas2d() { |
|||
return gte('2.9.0'); |
|||
} |
@ -0,0 +1 @@ |
|||
export {}; |
@ -0,0 +1,12 @@ |
|||
import { VantComponent } from '../common/component'; |
|||
VantComponent({ |
|||
props: { |
|||
dashed: Boolean, |
|||
hairline: Boolean, |
|||
contentPosition: String, |
|||
fontSize: String, |
|||
borderColor: String, |
|||
textColor: String, |
|||
customStyle: String, |
|||
}, |
|||
}); |
@ -0,0 +1,4 @@ |
|||
{ |
|||
"component": true, |
|||
"usingComponents": {} |
|||
} |
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue