Skip to content
This repository was archived by the owner on Oct 27, 2021. It is now read-only.

Commit bacd574

Browse files
committed
feat: add Indexes
1 parent 0ebf1cb commit bacd574

File tree

5 files changed

+307
-6
lines changed

5 files changed

+307
-6
lines changed

src/components/index.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import SegmentedControl from './segmented-control/index'
1010
import AtTabs from './tabs/index'
1111
import AtTabsPane from './tabs-pane/index'
1212
import AtPagination from './pagination/index'
13+
import AtIndexes from './indexes/index'
1314

1415
export {
1516
Grid,
@@ -23,5 +24,6 @@ export {
2324
SegmentedControl,
2425
AtTabs,
2526
AtTabsPane,
26-
AtPagination
27+
AtPagination,
28+
AtIndexes
2729
}

src/components/indexes/index.jsx

+261
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
import classNames from 'classnames'
2+
import Taro from '@tarojs/taro'
3+
import mixins from '../mixins'
4+
import { delayQuerySelector, isTest, uuid } from '../../utils/common'
5+
import AtList from '../list/index'
6+
import AtListItem from '../list/item/index'
7+
import AtToast from '../toast/index'
8+
9+
const ENV = Taro.getEnv()
10+
11+
export default {
12+
name: 'AtIndexes',
13+
mixins: [mixins],
14+
props: {
15+
customStyle: {
16+
type: [Object, String],
17+
default: '',
18+
},
19+
className: {
20+
type: [Array, String],
21+
default: '',
22+
},
23+
animation: {
24+
type: Boolean,
25+
default: false,
26+
},
27+
isVibrate: {
28+
type: Boolean,
29+
default: true,
30+
},
31+
isShowToast: {
32+
type: Boolean,
33+
default: true,
34+
},
35+
list: {
36+
type: Array,
37+
default: () => [],
38+
},
39+
topKey: {
40+
type: String,
41+
default: 'Top',
42+
},
43+
onClick: {
44+
type: Function,
45+
default: () => () => {},
46+
},
47+
onScrollIntoView: {
48+
type: Function,
49+
default: () => () => {},
50+
},
51+
},
52+
data() {
53+
return {
54+
// 右侧导航高度
55+
menuHeight: 0,
56+
// 右侧导航距离顶部高度
57+
startTop: 0,
58+
// 右侧导航元素高度
59+
itemHeight: 0,
60+
// 当前索引
61+
currentIndex: -1,
62+
listId: isTest() ? 'indexes-list-AOTU2018' : `list-${uuid()}`,
63+
timeoutTimerL: null,
64+
state: {
65+
_scrollIntoView: '',
66+
_scrollTop: 0,
67+
_tipText: '',
68+
_isShowToast: false,
69+
isWEB: Taro.getEnv() === Taro.ENV_TYPE.WEB,
70+
},
71+
}
72+
},
73+
computed: {
74+
listLen() {
75+
return this.list.length
76+
},
77+
},
78+
79+
watch: {
80+
listLen() {
81+
this.initData()
82+
},
83+
},
84+
beforeMount() {
85+
this.onScrollIntoView && this.onScrollIntoView(this.__jumpTarget.bind(this))
86+
},
87+
mounted() {
88+
if (ENV === Taro.ENV_TYPE.WEB) {
89+
this.listRef = document.getElementById(this.listId)
90+
}
91+
this.initData()
92+
},
93+
methods: {
94+
handleClick(item) {
95+
this.onClick && this.onClick(item)
96+
},
97+
handleTouchMove(event) {
98+
event.stopPropagation()
99+
event.preventDefault()
100+
101+
const { list } = this
102+
const pageY = event.touches[0].pageY
103+
const index = Math.floor((pageY - this.startTop) / this.itemHeight)
104+
105+
if (index >= 0 && index <= list.length && this.currentIndex !== index) {
106+
this.currentIndex = index
107+
const key = index > 0 ? list[index - 1].key : 'top'
108+
const touchView = `at-indexes__list-${key}`
109+
this.jumpTarget(touchView, index)
110+
}
111+
},
112+
handleTouchEnd() {
113+
this.currentIndex = -1
114+
},
115+
/**
116+
*
117+
* @param {string} _scrollIntoView
118+
* @param {number} idx
119+
*/
120+
jumpTarget(_scrollIntoView, idx) {
121+
const { topKey, list } = this
122+
const _tipText = idx === 0 ? topKey : list[idx - 1].key
123+
124+
if (ENV === Taro.ENV_TYPE.WEB) {
125+
delayQuerySelector(this, '.at-indexes', 0).then((rect) => {
126+
const targetOffsetTop = this.listRef.childNodes[idx].offsetTop
127+
const _scrollTop = targetOffsetTop - rect[0].top
128+
this.updateState({
129+
_scrollTop,
130+
_scrollIntoView,
131+
_tipText,
132+
})
133+
})
134+
return
135+
}
136+
137+
this.updateState({
138+
_scrollIntoView,
139+
_tipText,
140+
})
141+
},
142+
/**
143+
*
144+
* @param {string} key
145+
*/
146+
__jumpTarget(key) {
147+
const { list } = this
148+
// const index = _findIndex(list, ['key', key])
149+
const index = list.findIndex((item) => item.key === key)
150+
const targetView = `at-indexes__list-${key}`
151+
this.jumpTarget(targetView, index + 1)
152+
},
153+
updateState(state) {
154+
const { isShowToast, isVibrate } = this
155+
const { _scrollIntoView, _tipText, _scrollTop } = state
156+
// TODO: Fix dirty hack
157+
this.setState(
158+
{
159+
_scrollIntoView: _scrollIntoView,
160+
_tipText: _tipText,
161+
_scrollTop: _scrollTop,
162+
_isShowToast: isShowToast,
163+
},
164+
() => {
165+
clearTimeout(this.timeoutTimer)
166+
this.timeoutTimer = setTimeout(() => {
167+
this.setState({
168+
_tipText: '',
169+
_isShowToast: false,
170+
})
171+
}, 3000)
172+
}
173+
)
174+
175+
if (isVibrate) {
176+
Taro.vibrateShort()
177+
}
178+
},
179+
initData() {
180+
delayQuerySelector(this, '.at-indexes__menu').then((rect) => {
181+
const len = this.list.length
182+
this.menuHeight = rect[0].height
183+
this.startTop = rect[0].top
184+
this.itemHeight = Math.floor(this.menuHeight / (len + 1))
185+
})
186+
},
187+
handleScroll(e) {
188+
if (e && e.detail) {
189+
this.setState({
190+
_scrollTop: e.detail.scrollTop,
191+
})
192+
}
193+
},
194+
},
195+
render() {
196+
const { className, customStyle, animation, topKey, list } = this
197+
const { _scrollTop, _scrollIntoView, _tipText, _isShowToast, isWEB } = this.state
198+
199+
const toastStyle = { minWidth: Taro.pxTransform(100) }
200+
const rootCls = classNames('at-indexes', className)
201+
202+
const menuList = list.map((dataList, i) => {
203+
const { key } = dataList
204+
const targetView = `at-indexes__list-${key}`
205+
return (
206+
<view
207+
class="at-indexes__menu-item"
208+
key={key}
209+
onTap={this.jumpTarget.bind(this, targetView, i + 1)}>
210+
{key}
211+
</view>
212+
)
213+
})
214+
215+
const indexesList = list.map((dataList) => (
216+
<view id={`at-indexes__list-${dataList.key}`} class="at-indexes__list" key={dataList.key}>
217+
<view class="at-indexes__list-title">{dataList.title}</view>
218+
<AtList>
219+
{dataList.items &&
220+
dataList.items.map((item) => (
221+
<AtListItem
222+
key={item.name}
223+
title={item.name}
224+
onTap={this.handleClick.bind(this, item)}
225+
/>
226+
))}
227+
</AtList>
228+
</view>
229+
))
230+
231+
return (
232+
<view class={rootCls} style={customStyle}>
233+
<AtToast customStyle={toastStyle} isOpened={_isShowToast} text={_tipText} duration={2000} />
234+
<view
235+
class="at-indexes__menu"
236+
onTouchMove={this.handleTouchMove}
237+
onTouchEnd={this.handleTouchEnd}>
238+
<view
239+
class="at-indexes__menu-item"
240+
onTap={this.jumpTarget.bind(this, 'at-indexes__top', 0)}>
241+
{topKey}
242+
</view>
243+
{menuList}
244+
</view>
245+
<scroll-view
246+
class="at-indexes__body"
247+
id={this.listId}
248+
scrollY
249+
scrollWithAnimation={animation}
250+
scrollTop={isWEB ? _scrollTop : undefined}
251+
scrollIntoView={!isWEB ? _scrollIntoView : ''}
252+
onScroll={this.handleScroll.bind(this)}>
253+
<view class="at-indexes__content" id="at-indexes__top">
254+
{this.$slots.default}
255+
</view>
256+
{indexesList}
257+
</scroll-view>
258+
</view>
259+
)
260+
},
261+
}

src/components/notes.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
- Noticebar marquee 属性 还不可用
22
- Button 未完成,Invalid handler for event "getPhoneNumber": got undefined 等错误
3-
- Accordion 动画不顺滑
3+
- Accordion 动画不顺滑
4+
- Indexes 跳转动画有问题

src/pages/index/index.vue

+9-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22
<view
33
class="index"
44
>
5+
<view style="height:100vh">
6+
<AtIndexes
7+
:list="list"
8+
:on-click="clickIndex"
9+
>
10+
<view>自定义内容</view>
11+
</AtIndexes>
12+
</view>
513
<AtPagination
614
:total="50"
715
:page-size="10"
@@ -15,7 +23,7 @@
1523
:values="segmentedList"
1624
/>
1725
<TabBar
18-
:tab-list="tabList"
26+
:tab-list="tabBarList"
1927
:on-click="onTabClick"
2028
:current="tabIndex"
2129
/>

src/pages/index/indexMixins.ts

+32-3
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {
3434
AtTabs,
3535
AtTabsPane,
3636
AtPagination,
37+
AtIndexes,
3738
} from '../../components/index'
3839

3940
export default {
@@ -73,6 +74,7 @@ export default {
7374
AtTabs,
7475
AtTabsPane,
7576
AtPagination,
77+
AtIndexes,
7678
},
7779
data() {
7880
return {
@@ -127,15 +129,39 @@ export default {
127129
accordionIcon: { value: 'chevron-down', color: 'red', size: '15' },
128130
openAccordion: false,
129131
tabIndex: 0,
130-
tabList: [
132+
tabBarList: [
131133
{ title: '待办事项', iconType: 'bullet-list', text: 'new' },
132134
{ title: '拍照', iconType: 'camera' },
133135
{ title: '文件夹', iconType: 'folder', text: '100', max: 99 },
134136
],
135137
segmentedIndex: 0,
136138
segmentedList: ['标签页1', '标签页2', '标签页3'],
137139
currentTab: 0,
138-
tabList: [{ title: '标签页1' }, { title: '标签页2' }, { title: '标签页3' }]
140+
tabList: [{ title: '标签页1' }, { title: '标签页2' }, { title: '标签页3' }],
141+
list: [{
142+
title: 'A',
143+
key: 'A',
144+
items: [
145+
{
146+
'name': '阿坝'
147+
// 此处可加其他业务字段
148+
},
149+
{
150+
'name': '阿拉善'
151+
}]
152+
},
153+
{
154+
title: 'B',
155+
key: 'B',
156+
items: [
157+
{
158+
'name': '北京'
159+
},
160+
{
161+
'name': '保定'
162+
}]
163+
}
164+
]
139165
}
140166
},
141167
methods: {
@@ -165,6 +191,9 @@ export default {
165191
},
166192
changeCurrentTab(val) {
167193
this.currentTab = val
168-
}
194+
},
195+
clickIndex(e) {
196+
console.log(e)
197+
},
169198
},
170199
}

0 commit comments

Comments
 (0)