Initial commit
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
## 1.0.1(2026-04-30)
|
||||
- 更新文档
|
||||
## 1.0.0(2026-04-30)
|
||||
- 支持 uni-index-bar 组件
|
||||
@@ -0,0 +1,384 @@
|
||||
<template>
|
||||
<!-- #ifdef MP-ALIPAY -->
|
||||
<view class="index-text" ref="indexTextRef" v-bind="$attrs" @touchstart="onTouchStart" @touchmove.stop.prevent="onTouchMove"
|
||||
@touchend="onTouchEnd">
|
||||
<text v-for="(item, index) in indexList" :key="item" class="index-item">{{ item }}</text>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef MP-ALIPAY -->
|
||||
<text class="index-text" ref="indexTextRef" v-bind="$attrs" @touchstart="onTouchStart" @touchmove.stop.prevent="onTouchMove"
|
||||
@touchend="onTouchEnd">{{ indexs }}</text>
|
||||
<!-- #endif -->
|
||||
<!-- indicator with arrow -->
|
||||
<view v-show="touching" class="indicator" :class="indicatorViewClass" :style="indicatorStyle">
|
||||
<text class="indicator-text" :class="indicatorTextClass">{{ currentIndex }}</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
/**
|
||||
* 索引列表
|
||||
* @description 索引列表组件
|
||||
* @uniPlatform {
|
||||
* "app": {
|
||||
* "android": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.07",
|
||||
* "unixVaporVer": "5.21"
|
||||
* },
|
||||
* "ios": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.07",
|
||||
* "unixVaporVer": "5.11"
|
||||
* },
|
||||
* "harmony": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.07",
|
||||
* "unixVaporVer": "5.07"
|
||||
* }
|
||||
* },
|
||||
* "mp": {
|
||||
* "weixin": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.08"
|
||||
* },
|
||||
* "alipay": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.31"
|
||||
* }
|
||||
* },
|
||||
* "web": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.07"
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
|
||||
defineOptions({
|
||||
externalClasses: ['indicator-view-class', 'indicator-text-class']
|
||||
})
|
||||
|
||||
type Props = {
|
||||
/**
|
||||
* @description 索引字符列表,使用换行符 \n 分隔,每行作为一个索引项
|
||||
* @default "A\nB\nC\nD\nE\nF\nG\nH\nI\nJ\nK\nL\nM\nN\nO\nP\nQ\nR\nS\nT\nU\nV\nW\nX\nY\nZ"
|
||||
* @uniPlatform {
|
||||
* "app": {
|
||||
* "android": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.07",
|
||||
* "unixVaporVer": "5.21"
|
||||
* },
|
||||
* "ios": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.07",
|
||||
* "unixVaporVer": "5.11"
|
||||
* },
|
||||
* "harmony": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.07",
|
||||
* "unixVaporVer": "5.07"
|
||||
* }
|
||||
* },
|
||||
* "mp": {
|
||||
* "weixin": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.08"
|
||||
* },
|
||||
* "alipay": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.31"
|
||||
* }
|
||||
* },
|
||||
* "web": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.07"
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
indexs? : string,
|
||||
/**
|
||||
* @description 指示器容器的自定义样式类,触摸时显示当前索引的气泡
|
||||
* @default ""
|
||||
* @uniPlatform {
|
||||
* "app": {
|
||||
* "android": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.07",
|
||||
* "unixVaporVer": "5.21"
|
||||
* },
|
||||
* "ios": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.07",
|
||||
* "unixVaporVer": "5.11"
|
||||
* },
|
||||
* "harmony": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.07",
|
||||
* "unixVaporVer": "5.07"
|
||||
* }
|
||||
* },
|
||||
* "mp": {
|
||||
* "weixin": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.08"
|
||||
* },
|
||||
* "alipay": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.31"
|
||||
* }
|
||||
* },
|
||||
* "web": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.07"
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
indicatorViewClass? : string.ClassString,
|
||||
/**
|
||||
* @description 指示器内文字的自定义样式类
|
||||
* @default ""
|
||||
* @uniPlatform {
|
||||
* "app": {
|
||||
* "android": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.07",
|
||||
* "unixVaporVer": "5.21"
|
||||
* },
|
||||
* "ios": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.07",
|
||||
* "unixVaporVer": "5.11"
|
||||
* },
|
||||
* "harmony": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.07",
|
||||
* "unixVaporVer": "5.07"
|
||||
* }
|
||||
* },
|
||||
* "mp": {
|
||||
* "weixin": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.08"
|
||||
* },
|
||||
* "alipay": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.31"
|
||||
* }
|
||||
* },
|
||||
* "web": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.07"
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
indicatorTextClass? : string.ClassString
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
indexs: 'A\nB\nC\nD\nE\nF\nG\nH\nI\nJ\nK\nL\nM\nN\nO\nP\nQ\nR\nS\nT\nU\nV\nW\nX\nY\nZ',
|
||||
indicatorViewClass: '',
|
||||
indicatorTextClass: ''
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
/**
|
||||
* @description 选择索引时触发,参数为当前选择的索引字符,类型为 string
|
||||
* @uniPlatform {
|
||||
* "app": {
|
||||
* "android": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.07",
|
||||
* "unixVaporVer": "5.21"
|
||||
* },
|
||||
* "ios": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.07",
|
||||
* "unixVaporVer": "5.11"
|
||||
* },
|
||||
* "harmony": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.07",
|
||||
* "unixVaporVer": "5.07"
|
||||
* }
|
||||
* },
|
||||
* "mp": {
|
||||
* "weixin": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.08"
|
||||
* },
|
||||
* "alipay": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.31"
|
||||
* }
|
||||
* },
|
||||
* "web": {
|
||||
* "uniVer": "x",
|
||||
* "unixVer": "5.07"
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
select: [index: string]
|
||||
}>()
|
||||
|
||||
// 将索引字符串按换行分割为数组
|
||||
const indexList = computed(() : string[] => props.indexs.split('\n'))
|
||||
|
||||
const currentIndex = ref('')
|
||||
const touching = ref(false)
|
||||
const indicatorTop = ref(0)
|
||||
|
||||
const indicatorStyle = computed(() : string => {
|
||||
return `top: ${indicatorTop.value}px;`
|
||||
})
|
||||
|
||||
// 布局缓存变量
|
||||
let textTop = 0
|
||||
let itemHeight = 0
|
||||
let indexCount = 0
|
||||
let cachedIndexList : string[] = []
|
||||
const navBarHeight = uni.getWindowInfo().windowTop
|
||||
|
||||
// #ifndef MP
|
||||
const indexTextRef = ref<UniTextElement | null>(null)
|
||||
// #endif
|
||||
|
||||
// 更新布局信息
|
||||
const updateLayout = () => {
|
||||
// 缓存索引数组和长度,避免在 touch 事件中重复访问 computed
|
||||
cachedIndexList = indexList.value
|
||||
indexCount = cachedIndexList.length
|
||||
|
||||
// #ifndef MP
|
||||
// App/Web 端使用 getBoundingClientRect 同步获取
|
||||
const textEl = indexTextRef.value
|
||||
if (textEl != null) {
|
||||
const rect = textEl.getBoundingClientRect()
|
||||
textTop = rect.top
|
||||
itemHeight = rect.height / indexCount
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
|
||||
// #ifdef MP
|
||||
// 小程序需要在 setup 阶段保存 instance,因为异步回调中 getCurrentInstance() 返回 null
|
||||
const instance = getCurrentInstance()
|
||||
|
||||
// 小程序端使用 createSelectorQuery 异步获取布局
|
||||
const updateLayoutAsync = () => {
|
||||
cachedIndexList = indexList.value
|
||||
indexCount = cachedIndexList.length
|
||||
|
||||
if (instance == null) return
|
||||
|
||||
uni.createSelectorQuery()
|
||||
.in(instance.proxy)
|
||||
.select('.index-text')
|
||||
.boundingClientRect((rect) => {
|
||||
if (rect != null) {
|
||||
textTop = rect.top as number
|
||||
itemHeight = (rect.height as number) / indexCount
|
||||
}
|
||||
})
|
||||
.exec()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
updateLayoutAsync()
|
||||
})
|
||||
})
|
||||
|
||||
watch(() => props.indexs, () => {
|
||||
nextTick(() => {
|
||||
updateLayoutAsync()
|
||||
})
|
||||
})
|
||||
// #endif
|
||||
|
||||
const getIndexFromY = (y : number) : number => {
|
||||
const relY = y - textTop
|
||||
let idx = Math.floor(relY / itemHeight)
|
||||
if (idx < 0) idx = 0
|
||||
if (idx >= indexCount) idx = indexCount - 1
|
||||
return idx
|
||||
}
|
||||
|
||||
// 更新指示器位置
|
||||
const updateIndicator = (idx : number) => {
|
||||
indicatorTop.value = textTop + idx * itemHeight + itemHeight / 2 - 25 - navBarHeight
|
||||
}
|
||||
|
||||
const onTouchStart = (e : UniTouchEvent) => {
|
||||
updateLayout()
|
||||
touching.value = true
|
||||
const touch = e.touches[0]
|
||||
const idx = getIndexFromY(touch.clientY)
|
||||
const selectedIndex = cachedIndexList[idx]
|
||||
currentIndex.value = selectedIndex
|
||||
updateIndicator(idx)
|
||||
emit('select', selectedIndex)
|
||||
}
|
||||
|
||||
const onTouchMove = (e : UniTouchEvent) => {
|
||||
// #ifdef APP && UNI-APP-X
|
||||
e.preventDefault()
|
||||
// #endif
|
||||
// #ifdef WEB && UNI-APP-X
|
||||
if (e.cancelable) {
|
||||
e.preventDefault()
|
||||
}
|
||||
// #endif
|
||||
const touch = e.touches[0]
|
||||
const idx = getIndexFromY(touch.clientY)
|
||||
const newIndex = cachedIndexList[idx]
|
||||
if (newIndex != currentIndex.value) {
|
||||
currentIndex.value = newIndex
|
||||
updateIndicator(idx)
|
||||
emit('select', newIndex)
|
||||
}
|
||||
}
|
||||
|
||||
const onTouchEnd = (_e : UniTouchEvent) => {
|
||||
touching.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.index-text {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 28px;
|
||||
font-size: 11px;
|
||||
color: #757575;
|
||||
text-align: center;
|
||||
line-height: 1.4;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.index-item {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.indicator {
|
||||
position: absolute;
|
||||
right: 50px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
background-color: #c9c9c9;
|
||||
border-radius: 25px 25px 25px 0;
|
||||
transform: rotate(225deg);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 99;
|
||||
}
|
||||
|
||||
.indicator-text {
|
||||
font-size: 22px;
|
||||
color: #ffffff;
|
||||
text-align: center;
|
||||
transform: rotate(-225deg);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,75 @@
|
||||
{
|
||||
"id": "uni-index-bar",
|
||||
"displayName": "uni-index-bar 索引栏",
|
||||
"version": "1.0.1",
|
||||
"description": "uni-index-bar 索引栏组件,用于显示索引栏,支持自定义索引栏的样式和内容。",
|
||||
"isExt": true,
|
||||
"extUrl": "https://ext.dcloud.net.cn/plugin?id=27847",
|
||||
"keywords": [
|
||||
"uni-index-bar",
|
||||
"索引栏",
|
||||
"index-bar",
|
||||
"uni-app-x",
|
||||
"组件"
|
||||
],
|
||||
"repository": "https://gitcode.com/dcloud/uni-ui-x",
|
||||
"engines": {
|
||||
"HBuilderX": "^5.08",
|
||||
"uni-app-x": "^5.08",
|
||||
"uni-app": ""
|
||||
},
|
||||
"directories": {
|
||||
"example": "../../temps/example_temps"
|
||||
},
|
||||
"dcloudext": {
|
||||
"type": "component-vue",
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "无",
|
||||
"permissions": "无"
|
||||
},
|
||||
"darkmode": "x",
|
||||
"i18n": "x",
|
||||
"widescreen": "x",
|
||||
"npmurl": ""
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "x",
|
||||
"aliyun": "x",
|
||||
"alipay": "x"
|
||||
},
|
||||
"client": {
|
||||
"uni-app-x": {
|
||||
"web": {
|
||||
"safari": "√",
|
||||
"chrome": "√"
|
||||
},
|
||||
"app": {
|
||||
"android": "√",
|
||||
"ios": "√",
|
||||
"harmony": "√"
|
||||
},
|
||||
"mp": {
|
||||
"weixin": "√",
|
||||
"alipay": "√"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
### 介绍
|
||||
|
||||
本组件以竖排方式显示字母索引,常悬浮或固定于列表右侧,用户通过点击或滑动索引条上的字母索引,触发 `select` 事件。在该事件中通过代码驱动指定的列表滚动到该字母索引所在的位置。
|
||||
|
||||
本组件为绝对定位,默认垂直居中于父容器右侧。
|
||||
|
||||
本组件不包括 list-view 组件,需要配合 list-view 使用。list-view 中需要有对应索引的 list-item,且这些 list-item 需要有 id 属性,可以通过 `scroll-into-view` 属性或 `scrollTop` 属性跳转。
|
||||
|
||||
本组件包括一个指示器(indicator)子组件。当用户手指触摸在索引条上的某个字母时,在触摸的字母左边悬浮显示一个水滴形气泡,箭头指向当前触摸的字母。
|
||||
|
||||
### 基本用法
|
||||
|
||||
```html
|
||||
<view style="flex: 1; position: relative;">
|
||||
<list-view ref="listRef" style="flex: 1;">
|
||||
<!-- 列表内容 -->
|
||||
</list-view>
|
||||
<uni-index-bar @select="onSelect"></uni-index-bar>
|
||||
</view>
|
||||
```
|
||||
|
||||
```ts
|
||||
const onSelect = (index: string) => {
|
||||
// 处理索引选择,滚动列表到对应位置
|
||||
}
|
||||
```
|
||||
|
||||
### 事件
|
||||
|
||||
| 事件名 | 说明 | 回调参数 |
|
||||
|--------|------|----------|
|
||||
| select | 选择索引时触发 | index: string - 当前选择的索引字符 |
|
||||
|
||||
### 外部样式类
|
||||
|
||||
| 类名 | 说明 |
|
||||
|------|------|
|
||||
| indicator-view-class | 指示器容器样式,可自定义气泡背景色等 |
|
||||
| indicator-text-class | 指示器文字样式,可自定义文字颜色等 |
|
||||
|
||||
### 示例
|
||||
|
||||
#### 自定义索引
|
||||
|
||||
```html
|
||||
<uni-index-bar :indexs="'☆\nA\nB\nC\n#'" @select="onSelect"></uni-index-bar>
|
||||
```
|
||||
|
||||
#### 自定义样式
|
||||
|
||||
```html
|
||||
<uni-index-bar
|
||||
indicator-view-class="custom-indicator"
|
||||
indicator-text-class="custom-indicator-text"
|
||||
style="color: aqua;"
|
||||
@select="onSelect">
|
||||
</uni-index-bar>
|
||||
```
|
||||
|
||||
```css
|
||||
.custom-indicator {
|
||||
background-color: #007aff;
|
||||
}
|
||||
.custom-indicator-text {
|
||||
color: #ffff00;
|
||||
}
|
||||
```
|
||||
|
||||
#### 配合 list-view 使用
|
||||
|
||||
```html
|
||||
<view style="flex: 1; position: relative;">
|
||||
<list-view ref="listRef" style="flex: 1;" :scroll-into-view="indexViewID">
|
||||
<template v-for="group in cityGroups" :key="group.index">
|
||||
<list-item :id="'idx-' + group.index" type="header">
|
||||
<view class="group-header">
|
||||
<text>{{ group.index }}</text>
|
||||
</view>
|
||||
</list-item>
|
||||
<list-item v-for="city in group.cities" :key="city.id" type="city">
|
||||
<view class="city-item">
|
||||
<text>{{ city.name }}</text>
|
||||
</view>
|
||||
</list-item>
|
||||
</template>
|
||||
</list-view>
|
||||
<uni-index-bar :indexs="indexList" @select="onSelect"></uni-index-bar>
|
||||
</view>
|
||||
```
|
||||
|
||||
```ts
|
||||
const indexList = cityGroups.map((g): string => g.index).join('\n')
|
||||
const indexViewID = ref("")
|
||||
|
||||
const onSelect = (index: string) => {
|
||||
indexViewID.value = 'idx-' + index
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user