提交 69e48a7c 作者: 方治民

test: 测试虚拟滚动 indexlist 组件示例

上级 1d1258bb
...@@ -22,3 +22,5 @@ src/api/services/api.d.ts ...@@ -22,3 +22,5 @@ src/api/services/api.d.ts
src/static/** src/static/**
src/components/firstui/** src/components/firstui/**
src/uni_modules/** src/uni_modules/**
*.nvue
...@@ -5,3 +5,5 @@ example/* ...@@ -5,3 +5,5 @@ example/*
src/static/** src/static/**
src/components/firstui/** src/components/firstui/**
src/uni_modules/** src/uni_modules/**
src/**/*.nvue
...@@ -80,12 +80,12 @@ ...@@ -80,12 +80,12 @@
"@dcloudio/uni-mp-weixin": "3.0.0-alpha-3070720230316001", "@dcloudio/uni-mp-weixin": "3.0.0-alpha-3070720230316001",
"@dcloudio/uni-quickapp-webview": "3.0.0-alpha-3070720230316001", "@dcloudio/uni-quickapp-webview": "3.0.0-alpha-3070720230316001",
"@dcloudio/uni-ui": "^1.4.26", "@dcloudio/uni-ui": "^1.4.26",
"@faker-js/faker": "^7.6.0",
"@vue/runtime-core": "^3.2.47", "@vue/runtime-core": "^3.2.47",
"@vueuse/core": "^9.13.0", "@vueuse/core": "^9.13.0",
"axios": "^0.26.1", "axios": "^0.26.1",
"dayjs": "^1.11.7", "dayjs": "^1.11.7",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"mockjs": "^1.1.0",
"nanoid": "^4.0.1", "nanoid": "^4.0.1",
"pinia": "^2.0.33", "pinia": "^2.0.33",
"pinyin-pro": "^3.13.1", "pinyin-pro": "^3.13.1",
......
...@@ -24,6 +24,7 @@ specifiers: ...@@ -24,6 +24,7 @@ specifiers:
'@dcloudio/uni-stacktracey': 3.0.0-alpha-3070720230316001 '@dcloudio/uni-stacktracey': 3.0.0-alpha-3070720230316001
'@dcloudio/uni-ui': ^1.4.26 '@dcloudio/uni-ui': ^1.4.26
'@dcloudio/vite-plugin-uni': 3.0.0-alpha-3070720230316001 '@dcloudio/vite-plugin-uni': 3.0.0-alpha-3070720230316001
'@faker-js/faker': ^7.6.0
'@iconify/json': ^2.2.36 '@iconify/json': ^2.2.36
'@types/lodash-es': ^4.17.7 '@types/lodash-es': ^4.17.7
'@types/mapbox-gl': ^2.7.10 '@types/mapbox-gl': ^2.7.10
...@@ -57,7 +58,6 @@ specifiers: ...@@ -57,7 +58,6 @@ specifiers:
lint-staged: ^13.2.0 lint-staged: ^13.2.0
lodash-es: ^4.17.21 lodash-es: ^4.17.21
mapbox-gl: ^2.13.0 mapbox-gl: ^2.13.0
mockjs: ^1.1.0
nanoid: ^4.0.1 nanoid: ^4.0.1
npm-run-all: ^4.1.5 npm-run-all: ^4.1.5
picocolors: ^1.0.0 picocolors: ^1.0.0
...@@ -110,12 +110,12 @@ dependencies: ...@@ -110,12 +110,12 @@ dependencies:
'@dcloudio/uni-mp-weixin': 3.0.0-alpha-3070720230316001_postcss@8.4.21+vue@3.2.47 '@dcloudio/uni-mp-weixin': 3.0.0-alpha-3070720230316001_postcss@8.4.21+vue@3.2.47
'@dcloudio/uni-quickapp-webview': 3.0.0-alpha-3070720230316001_postcss@8.4.21+vue@3.2.47 '@dcloudio/uni-quickapp-webview': 3.0.0-alpha-3070720230316001_postcss@8.4.21+vue@3.2.47
'@dcloudio/uni-ui': 1.4.26 '@dcloudio/uni-ui': 1.4.26
'@faker-js/faker': 7.6.0
'@vue/runtime-core': 3.2.47 '@vue/runtime-core': 3.2.47
'@vueuse/core': 9.13.0_vue@3.2.47 '@vueuse/core': 9.13.0_vue@3.2.47
axios: 0.26.1 axios: 0.26.1
dayjs: 1.11.7 dayjs: 1.11.7
lodash-es: 4.17.21 lodash-es: 4.17.21
mockjs: 1.1.0
nanoid: 4.0.1 nanoid: 4.0.1
pinia: 2.0.33_hmuptsblhheur2tugfgucj7gc4 pinia: 2.0.33_hmuptsblhheur2tugfgucj7gc4
pinyin-pro: 3.13.1 pinyin-pro: 3.13.1
...@@ -2582,6 +2582,11 @@ packages: ...@@ -2582,6 +2582,11 @@ packages:
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: true dev: true
/@faker-js/faker/7.6.0:
resolution: {integrity: sha512-XK6BTq1NDMo9Xqw/YkYyGjSsg44fbNwYRx7QK2CuoQgyy+f1rrTDHoExVM5PsyXCtfl2vs2vVJ0MN0yN6LppRw==}
engines: {node: '>=14.0.0', npm: '>=6.0.0'}
dev: false
/@humanwhocodes/config-array/0.11.8: /@humanwhocodes/config-array/0.11.8:
resolution: {integrity: sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==} resolution: {integrity: sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==}
engines: {node: '>=10.10.0'} engines: {node: '>=10.10.0'}
...@@ -4920,6 +4925,7 @@ packages: ...@@ -4920,6 +4925,7 @@ packages:
/commander/10.0.0: /commander/10.0.0:
resolution: {integrity: sha512-zS5PnTI22FIRM6ylNW8G4Ap0IEOyk62fhLSD0+uHRT9McRCLGpkVNvao4bjimpK/GShynyQkFFxHhwMcETmduA==} resolution: {integrity: sha512-zS5PnTI22FIRM6ylNW8G4Ap0IEOyk62fhLSD0+uHRT9McRCLGpkVNvao4bjimpK/GShynyQkFFxHhwMcETmduA==}
engines: {node: '>=14'} engines: {node: '>=14'}
dev: true
/commander/2.20.3: /commander/2.20.3:
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
...@@ -8797,13 +8803,6 @@ packages: ...@@ -8797,13 +8803,6 @@ packages:
ufo: 1.1.1 ufo: 1.1.1
dev: true dev: true
/mockjs/1.1.0:
resolution: {integrity: sha512-eQsKcWzIaZzEZ07NuEyO4Nw65g0hdWAyurVol1IPl1gahRwY+svqzfgfey8U8dahLwG44d6/RwEzuK52rSa/JQ==}
hasBin: true
dependencies:
commander: 10.0.0
dev: false
/modify-values/1.0.1: /modify-values/1.0.1:
resolution: {integrity: sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==} resolution: {integrity: sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
......
...@@ -45,9 +45,4 @@ ...@@ -45,9 +45,4 @@
// 全局样式 // 全局样式
@import './common/public.less'; @import './common/public.less';
uni-page-body,
page {
height: 100%;
}
</style> </style>
...@@ -4,132 +4,3 @@ ...@@ -4,132 +4,3 @@
align-items: center; align-items: center;
height: 100vh; height: 100vh;
} }
// ================ mapbox-gl ================
// 自定义 popup 内容样式
.mapboxgl-popup-content {
box-shadow: 0 1px 5px #6e6e6e !important;
}
.mapboxgl-popup-content .mapboxgl-custom-popup {
position: relative;
}
.mapboxgl-custom-popup .popup-title {
font-weight: bold;
}
.mapboxgl-ctrl-group:not(:empty) {
box-shadow: 0 0 6px #000 !important;
}
.mapboxgl-ctrl-group button {
display: flex !important;
overflow: visible !important;
}
.mapboxgl-ctrl-logo {
display: none !important;
}
.mapboxgl-ctrl-bottom-left {
display: flex;
bottom: 0;
left: 2px;
}
.mapboxgl-ctrl-lnglat {
background: rgb(0 0 0 / 40%);
color: white;
line-height: 2em;
padding: 0 10px;
}
// 覆盖全屏控件的图标
.mapboxgl-ctrl button.mapboxgl-ctrl-fullscreen .mapboxgl-ctrl-icon {
background-image: url('data:image/svg+xml,%3Csvg xmlns="http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg" width="24" height="24" preserveAspectRatio="xMidYMid meet" viewBox="0 0 16 16"%3E%3Cpath fill="currentColor" d="M3.75 3a.75.75 0 0 0-.75.75V5.5a.5.5 0 0 1-1 0V3.75C2 2.784 2.784 2 3.75 2H5.5a.5.5 0 0 1 0 1H3.75ZM10 2.5a.5.5 0 0 1 .5-.5h1.75c.966 0 1.75.784 1.75 1.75V5.5a.5.5 0 0 1-1 0V3.75a.75.75 0 0 0-.75-.75H10.5a.5.5 0 0 1-.5-.5ZM2.5 10a.5.5 0 0 1 .5.5v1.75c0 .414.336.75.75.75H5.5a.5.5 0 0 1 0 1H3.75A1.75 1.75 0 0 1 2 12.25V10.5a.5.5 0 0 1 .5-.5Zm11 0a.5.5 0 0 1 .5.5v1.75A1.75 1.75 0 0 1 12.25 14H10.5a.5.5 0 0 1 0-1h1.75a.75.75 0 0 0 .75-.75V10.5a.5.5 0 0 1 .5-.5Z"%2F%3E%3C%2Fsvg%3E') !important;
}
.mapboxgl-ctrl button.mapboxgl-ctrl-shrink .mapboxgl-ctrl-icon {
background-image: url('data:image/svg+xml,%3Csvg xmlns="http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg" width="24" height="24" preserveAspectRatio="xMidYMid meet" viewBox="0 0 16 16"%3E%3Cpath fill="currentColor" d="M11 4a1 1 0 0 0 1 1h1.5a.5.5 0 0 1 0 1H12a2 2 0 0 1-2-2V2.5a.5.5 0 0 1 1 0V4Zm0 8a1 1 0 0 1 1-1h1.5a.5.5 0 0 0 0-1H12a2 2 0 0 0-2 2v1.5a.5.5 0 0 0 1 0V12Zm-7-1a1 1 0 0 1 1 1v1.5a.5.5 0 0 0 1 0V12a2 2 0 0 0-2-2H2.5a.5.5 0 0 0 0 1H4Zm1-7a1 1 0 0 1-1 1H2.5a.5.5 0 0 0 0 1H4a2 2 0 0 0 2-2V2.5a.5.5 0 0 0-1 0V4Z"%2F%3E%3C%2Fsvg%3E') !important;
}
.mapboxgl-ctrl .mapboxgl-ctrl-reset-button .mapboxgl-ctrl-icon {
background-image: url('data:image/svg+xml,%3Csvg xmlns="http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg" width="20" height="20" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"%3E%3Cpath fill="currentColor" d="M12 16c1.671 0 3-1.331 3-3s-1.329-3-3-3s-3 1.331-3 3s1.329 3 3 3z"%2F%3E%3Cpath fill="currentColor" d="M20.817 11.186a8.94 8.94 0 0 0-1.355-3.219a9.053 9.053 0 0 0-2.43-2.43a8.95 8.95 0 0 0-3.219-1.355a9.028 9.028 0 0 0-1.838-.18V2L8 5l3.975 3V6.002c.484-.002.968.044 1.435.14a6.961 6.961 0 0 1 2.502 1.053a7.005 7.005 0 0 1 1.892 1.892A6.967 6.967 0 0 1 19 13a7.032 7.032 0 0 1-.55 2.725a7.11 7.11 0 0 1-.644 1.188a7.2 7.2 0 0 1-.858 1.039a7.028 7.028 0 0 1-3.536 1.907a7.13 7.13 0 0 1-2.822 0a6.961 6.961 0 0 1-2.503-1.054a7.002 7.002 0 0 1-1.89-1.89A6.996 6.996 0 0 1 5 13H3a9.02 9.02 0 0 0 1.539 5.034a9.096 9.096 0 0 0 2.428 2.428A8.95 8.95 0 0 0 12 22a9.09 9.09 0 0 0 1.814-.183a9.014 9.014 0 0 0 3.218-1.355a8.886 8.886 0 0 0 1.331-1.099a9.228 9.228 0 0 0 1.1-1.332A8.952 8.952 0 0 0 21 13a9.09 9.09 0 0 0-.183-1.814z"%2F%3E%3C%2Fsvg%3E') !important;
}
.mapboxgl-ctrl .mapboxgl-ctrl-layer-button .mapboxgl-ctrl-icon {
background-image: url('data:image/svg+xml,%3Csvg xmlns="http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg" width="24" height="24" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"%3E%3Cpath fill="currentColor" d="m13.387 3.425l6.365 4.243a1 1 0 0 1 0 1.664l-6.365 4.244a2.5 2.5 0 0 1-2.774 0L4.248 9.332a1 1 0 0 1 0-1.664l6.365-4.243a2.5 2.5 0 0 1 2.774 0Zm6.639 8.767a2.002 2.002 0 0 1-.577.598l-6.05 4.084a2.5 2.5 0 0 1-2.798 0l-6.05-4.084a2 2 0 0 1-.779-2.29l6.841 4.56a2.5 2.5 0 0 0 2.613.098l.16-.098l6.841-4.56a1.996 1.996 0 0 1-.201 1.692Zm0 3.25a2.002 2.002 0 0 1-.577.598l-6.05 4.084a2.5 2.5 0 0 1-2.798 0l-6.05-4.084a2 2 0 0 1-.779-2.29l6.841 4.56a2.5 2.5 0 0 0 2.613.098l.16-.098l6.841-4.56a1.996 1.996 0 0 1-.201 1.692Z"%2F%3E%3C%2Fsvg%3E') !important;
}
/* stylelint-disable-next-line selector-no-vendor-prefix */
.mapboxgl-map:-webkit-full-screen {
.mapboxgl-ctrl-bottom-left {
bottom: 0;
}
}
// 自定义图层控制器样式
.mapboxgl-ctrl button.-active svg {
color: #4264fb;
}
.mapboxgl-ctrl-layer-button {
// position: relative;
z-index: 1;
}
.mapboxgl-ctrl-layer-wrap {
z-index: 100;
display: flex;
justify-items: center;
align-items: center;
position: absolute;
top: 0;
left: -220px;
visibility: hidden;
padding: 10px;
border-radius: 3px;
box-shadow: 0 0 3px #333;
background-color: #fff;
}
.mapboxgl-ctrl-layer {
width: 60px;
height: 60px;
display: flex;
justify-content: center;
border: 2px solid #fff;
position: relative;
}
.mapboxgl-ctrl-layer.active {
border-color: #3385ff;
}
.mapboxgl-ctrl-layer img {
width: 100%;
height: 100%;
}
.mapboxgl-ctrl-layer span {
display: inline-flex;
justify-content: center;
background-color: rgb(0 0 0 / 60%);
color: white;
position: absolute;
bottom: 0;
width: 100%;
}
.mapboxgl-ctrl-layer-button.-active .mapboxgl-ctrl-layer-wrap {
visibility: visible;
}
.mapboxgl-ctrl-bottom-left,
.mapboxgl-ctrl-bottom-right {
padding-bottom: 0;
/* stylelint-disable-next-line function-no-unknown */
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
...@@ -435,12 +435,12 @@ export function loadMapControl(mapboxgl: any, map: mapboxgl.Map, options?: MapCo ...@@ -435,12 +435,12 @@ export function loadMapControl(mapboxgl: any, map: mapboxgl.Map, options?: MapCo
label: '影像', label: '影像',
value: 'img,cia', value: 'img,cia',
image: `${host}/api/assets/images/img_w.png`, image: `${host}/api/assets/images/img_w.png`,
active: true,
}, },
{ {
label: '矢量', label: '矢量',
value: 'vec,cva', value: 'vec,cva',
image: `${host}/api/assets/images/vec_w.png`, image: `${host}/api/assets/images/vec_w.png`,
active: true,
}, },
{ {
label: '地形', label: '地形',
......
...@@ -35,7 +35,6 @@ ...@@ -35,7 +35,6 @@
isSelect: { isSelect: {
type: Boolean, type: Boolean,
default: false default: false
}, },
selectedColor: { selectedColor: {
type: String, type: String,
......
<template>
<view class="fui-index__list-item" :class="{'fui-il__between':subRight}" @tap="onClick">
<view class="fui-index__list-left">
<view class="fui-il__checkbox"
:class="{'fui-il__checkbox-color':(!selectedColor || selectedColor=='true') && model.checked}"
:style="{background:model.checked?selectedColor:'#fff',borderColor:model.checked?selectedColor:borderColor}"
v-if="isSelect">
<view class="fui-il__checkmark" v-if="model.checked"></view>
</view>
<view class="fui-il__img-box" v-if="isSrc">
<fLazyload v-if="model.src" :src="model.src" class="fui-index__list-img" width="72" height="72" mode="widthFix"></fLazyload>
</view>
<text class="fui-index__list-main">{{model.text || ''}}</text>
</view>
<text class="fui-index__list-sub">{{model.subText || ''}}</text>
<view class="fui-il__border-bottom" v-if="!last"></view>
</view>
</template>
<script>
import fLazyload from '../fui-lazyload/fui-lazyload.vue'
//此组件为索引列表item项,若不满足要求可自行调整样式
export default {
name: 'f-virtual-index-list-item',
components: {
fLazyload
},
props: {
model: {
type: Object,
default () {
return {}
}
},
isSelect: {
type: Boolean,
default: false
},
selectedColor: {
type: String,
// #ifdef APP-NVUE
default: '#465CFF'
// #endif
// #ifndef APP-NVUE
default: ''
// #endif
},
//checkbox未选中时边框颜色
borderColor: {
type: String,
default: '#ccc'
},
//是否显示图片
isSrc: {
type: Boolean,
default: false
},
//次要内容是否居右侧
subRight: {
type: Boolean,
default: true
},
last: {
type: Boolean,
default: false
},
idx: {
type: Number,
default: 0
},
index: {
type: Number,
default: 0
}
},
methods: {
onClick() {
this.$emit("itemClick", {
idx: this.idx,
index: this.index
})
}
}
};
</script>
<style scoped>
.fui-index__list-item {
/* #ifndef APP-NVUE */
width: 100%;
display: flex;
box-sizing: border-box;
/* #endif */
flex: 1;
flex-direction: row;
align-items: center;
padding: 32rpx 64rpx 32rpx 32rpx;
background-color: #FFFFFF;
position: relative;
}
.fui-il__border-bottom {
position: absolute;
bottom: 0;
/* #ifdef APP-NVUE */
height: 0.5px;
z-index: -1;
background-color: #EEEEEE;
/* #endif */
/* #ifndef APP-NVUE */
height: 1px;
-webkit-transform: scaleY(0.5);
transform: scaleY(0.5);
transform-origin: 0 100%;
z-index: 1;
background-color: var(--fui-color-border, #EEEEEE);
/* #endif */
left: 32rpx;
right: 0;
}
.fui-index__list-item:active {
/* #ifdef APP-NVUE */
background-color: rgba(0, 0, 0, 0.2) !important;
/* #endif */
/* #ifndef APP-NVUE */
background-color: var(--fui-bg-color-hover, rgba(0, 0, 0, 0.2)) !important;
/* #endif */
}
.fui-il__between {
justify-content: space-between;
}
.fui-index__list-left {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
overflow: hidden;
}
.fui-il__checkbox {
font-size: 0;
color: rgba(0, 0, 0, 0);
width: 40rpx;
height: 40rpx;
border-width: 1px;
border-style: solid;
/* #ifdef APP-NVUE */
border-radius: 40rpx;
/* #endif */
/* #ifndef APP-NVUE */
display: inline-flex;
box-sizing: border-box;
border-radius: 50%;
vertical-align: top;
flex-shrink: 0;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: center;
overflow: hidden;
position: relative;
margin-right: 24rpx;
}
/* #ifndef APP-NVUE */
.fui-il__checkbox-color {
background: var(--fui-color-primary, #465CFF) !important;
border-color: var(--fui-color-primary, #465CFF) !important;
}
/* #endif */
.fui-il__checkmark {
width: 20rpx;
height: 40rpx;
border-bottom-style: solid;
border-bottom-width: 3px;
border-bottom-color: #FFFFFF;
border-right-style: solid;
border-right-width: 3px;
border-right-color: #FFFFFF;
transform: rotate(45deg) scale(0.5);
/* #ifndef APP-NVUE */
box-sizing: border-box;
transform: rotate(45deg) scale(0.5) translateZ(0);
/* #endif */
/* #ifdef APP-NVUE */
transform: rotate(45deg) scale(0.5);
/* #endif */
transform-origin: 54% 48%;
}
.fui-il__img-box {
/* #ifndef APP-NVUE */
display: flex;
background-color: var(--fui-bg-color-content, #F8F8F8);
/* #endif */
width: 72rpx;
height: 72rpx;
align-items: center;
justify-content: center;
border-radius: 8rpx;
overflow: hidden;
/* #ifdef APP-NVUE */
background-color: #F8F8F8;
/* #endif */
margin-right: 24rpx;
}
.fui-index__list-img {
width: 72rpx;
height: 72rpx;
/* #ifndef APP-NVUE */
flex-shrink: 0;
/* #endif */
border-radius: 8rpx;
}
.fui-index__list-main {
/* #ifndef APP-NVUE */
display: block;
white-space: nowrap;
/* #endif */
/* #ifdef APP-NVUE */
lines: 1;
/* #endif */
font-size: 32rpx;
font-weight: normal;
overflow: hidden;
text-overflow: ellipsis;
}
.fui-index__list-sub {
/* #ifndef APP-NVUE */
display: block;
/* #endif */
font-weight: normal;
font-size: 28rpx;
color: #999;
/* #ifdef APP-NVUE */
lines: 1;
/* #endif */
padding-left: 24rpx;
}
</style>
<template>
<view class="fui-index__list" ref="fui_index_list" id="fui_index_list">
<!-- 滚动区域内容 -->
<scroll-view class="fui-index__list-sv" scroll-y :scroll-into-view="scrollViewId" @scroll="scrollEvent">
<view :style="{ height: containerHeight, position: 'relative' }">
<slot></slot>
<view :id="'fui_il_letter_'+idx" class="fui-list-item" :style="{ top: list.top + 'px' }" v-for="(list, idx) in visibleList" :key="list.key">
<!-- 字母索引 -->
<view class="fui-index__list-letter" :class="{'fui-il__key-bg':!background}"
:style="{background:background}">
<text class="fui-il__letter-text"
:style="{color:color}">{{list.descr || list.letter}}</text>
</view>
<!-- 解构插槽 item项样式内容自定义 -->
<view v-if="custom">
<view v-for="(model,index) in list.data" :key="index" @tap="onTap(idx,index)">
<slot name="item" :model="model" :idx="idx" :index="index"
:last="list.data.length-1===index" :isSelect="isSelect" :isSrc="isSrc"
:subRight="subRight"></slot>
</view>
</view>
<view :style="{ height: list.itemHeight + 'px' }" v-else>
<f-virtual-index-list-item class="fui-list-item__item" :style="{ top: model.top + 'px' }" @itemClick="onClick" :model="model" :idx="idx" :index="index"
:last="list.data.length-1===index" :isSelect="isSelect"
:borderColor="borderColor" :selectedColor="selectedColor" :isSrc="isSrc"
:subRight="subRight" v-for="(model,index) in list.data" :key="index">
</f-virtual-index-list-item>
</view>
</view>
<view style="position: absolute; width: 100%; bottom: 0;">
<slot name="footer"></slot>
</view>
</view>
</scroll-view>
<view class="fui-il__indicator" :class="{'fui-il__nvue-android':nvueAndroid}"
:style="{ top: indicators[touchmoveIndex] + 'px' }" v-if="touching && touchmoveIndex!==-1">
<view class="fui-il__indicator-after"></view>
<text class="fui-il__indicator-text">{{ lists[touchmoveIndex] && lists[touchmoveIndex].letter }}</text>
</view>
<view class="fui-index__letter" :style="styles" @touchstart="touchStart" @touchmove.stop.prevent="touchMove"
@touchend="touchEnd" @mousedown.stop="mousedown" @mousemove.stop.prevent="mousemove"
@mouseleave.stop="mouseleave" v-if="!isNvue || styles">
<view class="fui-letter__item" v-for="(item, i) in lists" :key="i">
<!-- @tap="letterTap(i)" -->
<text class="fui-letter__key" :class="{'fui-il__key-color':i === touchmoveIndex && !activeBackground}"
:style="{ background: i === touchmoveIndex ? activeBackground : 'transparent', color: i === touchmoveIndex? activeColor : keyColor }">{{ item.letter }}</text>
</view>
</view>
<!-- 调试信息 -->
<view class="debug" v-if="debug">
{{ scrollTop }}
</view>
</view>
</template>
<script>
import { debounce, cloneDeep } from 'lodash-es'
import { unref, toRaw } from 'vue'
import fVirtualIndexListItem from './f-virtual-index-list-item.vue'
// #ifdef APP-PLUS
function throttle(func, delay) {
var prev = Date.now();
return function() {
var context = this;
var args = arguments;
var now = Date.now();
if (now - prev >= delay) {
func.apply(context, args);
prev = Date.now();
}
}
}
function touchMove(e) {
let pageY = e.touches[0].pageY
let index = this.getIndex(pageY - this.winOffsetY)
if (this.touchmoveIndex === index) {
return false
}
let item = this.lists[index]
if (item) {
this.scrollViewId = `fui_il_letter_${index}`
this.touchmoveIndex = index
}
}
const throttleTouchMove = throttle(touchMove, 40)
// #endif
let data = []
export default {
name: 'fui-virtual-index-list',
emits: ['click', 'init'],
components: {
fVirtualIndexListItem
},
props: {
//列表数据
listData: {
type: Array,
default () {
return []
}
},
height: {
type: [Number, String],
default: 64
},
color: {
type: String,
default: '#181818'
},
background: {
type: String,
default: '',
},
keyColor: {
type: String,
default: '#7F7F7F'
},
activeColor: {
type: String,
default: '#FFFFFF'
},
activeBackground: {
type: String,
default: ''
},
isSelect: {
type: Boolean,
default: false
},
//checkbox未选中时边框颜色
borderColor: {
type: String,
default: '#ccc'
},
selectedColor: {
type: String,
default: ''
},
//是否显示图片
isSrc: {
type: Boolean,
default: false
},
//次要内容是否居右侧
subRight: {
type: Boolean,
default: true
},
custom: {
type: Boolean,
default: false
},
//H5端使用,是否使用了默认导航栏,默认44px
isHeader: {
type: Boolean,
default: false
},
// 调试
debug: {
type: Boolean,
default: false
}
},
watch: {
listData(val) {
if (this.init) {
this.initData()
}
}
},
data() {
const { windowHeight } = uni.getSystemInfoSync()
return {
lists: [],
idtHeight: 0,
winOffsetY: 0,
winHeight: 0,
styles: '',
indicators: [],
top: -1,
start: 0,
touching: false,
touchmoveIndex: -1,
scrollViewId: '',
touchmovable: true,
loaded: false,
isPC: false,
nvueAndroid: false,
isNvue: false,
scrollTop: 0,
init: false,
windowHeight,
containerHeight: '0rpx',
visibleList: [],
}
},
mounted() {
// #ifdef H5
this.isPC = this.IsPC()
// #endif
setTimeout(() => {
this.initData()
}, 50)
},
methods: {
getIndex(y) {
let index = -1;
// #ifdef H5
if (this.isHeader) {
y += 44
}
// #endif
index = Math.floor((y - this.start) / uni.upx2px(40))
return index
},
getVisibleList() {
console.log('getVisibleList')
const itemHeight = uni.upx2px(32 + 32 + 72)
const visibleCount = Math.ceil(this.windowHeight / itemHeight)
const startIndex = Math.floor(this.scrollTop / itemHeight)
const endIndex = startIndex + visibleCount
let top = uni.upx2px(72 + 32)
const list = this.lists.filter((item, index) => {
// 检查这个 item 是否在可视区域内
const { start, end } = item
const hasLeft = start >= startIndex && start <= endIndex
const hasRight = end >= startIndex && end <= endIndex
const hasMiddle = start <= startIndex && end >= endIndex
const visible = hasLeft || hasRight || hasMiddle
console.log(`item: ${item.letter}, count: ${item.data.length}, visible: ${visible}`, `start: ${start}, end: ${end}`, `startIndex: ${startIndex}, endIndex: ${endIndex}`)
// if (visible) {
// console.log(item.countIndex - startIndex, endIndex, item.data.slice(item.countIndex - startIndex, endIndex))
// }
// 检查这个 item 其中哪些子项在可视区域内
if (visible) {
const indexes = [0, 0]
if (hasLeft && hasRight) {
indexes[0] = 0
indexes[1] = item.data.length - 1
} else {
if (hasLeft && !hasRight) {
// 左边有
indexes[0] = 0
indexes[1] = endIndex - start
}
if (!hasLeft && hasRight) {
// 左边有
indexes[0] = startIndex - start
indexes[1] = item.data.length - 1
}
}
item.data = item.data.slice(indexes[0], indexes[1] + 1)
console.log(`hasMiddle: ${hasMiddle}, hasLeft: ${hasLeft}, hasRight: ${hasRight}`, indexes, item.data.length)
}
// item.data.forEach((subItem, subIndex) => {
// const subVisible = subIndex >= startIndex && subIndex <= endIndex
// console.log(`subItem: ${subItem.name}, subVisible: ${subVisible}`)
// })
return visible
})
console.log(visibleCount, startIndex, endIndex, list)
this.visibleList = list
},
initData() {
this.init = false
this.lists = []
const indexHeight = uni.upx2px(64)
const itemHeight = uni.upx2px(32 + 32 + 72)
let height = 0;
let top = uni.upx2px(72 + 32)
let lists = [];
let tempArr = [...(this.listData || [])]
for (let i = 0, len = tempArr.length; i < len; i++) {
let model = tempArr[i]
if (!model.data || model.data.length === 0) {
continue;
}
model.top = top
height += 40;
top += indexHeight + model.data.length * itemHeight
model.originalIndex = i;
model.key = `fui_key_${Math.ceil(Math.random() * 10e5).toString(36)}`
model.start = lists.length === 0 ? 0 : lists[i - 1].end
model.end = (lists.length === 0 ? model.data.length : model.data.length + lists[i - 1].end)
model.count = model.data.length
model.itemHeight = model.data.length * itemHeight
model.data = model.data.map((item, index) => {
item.top = indexHeight + index * itemHeight
return item
})
lists.push(model)
}
this.idtHeight = uni.upx2px(height)
this.styles = `height:${this.idtHeight}px;`;
this.lists = lists;
this.getVisibleList()
this.containerHeight = top + (this.lists.length * 10) + uni.upx2px(100 + 30) + 'px'
this.winHeight = this.windowHeight
this.setStyles()
this.$nextTick(() => {
this.init = true
this.$emit('init')
})
},
setStyles() {
this.indicators = []
this.styles =
`height:${this.idtHeight}px;top:${this.winHeight / 2}px;-webkit-transform: translateY(-${this.idtHeight/2}px);transform: translateY(-${this.idtHeight/2}px)`
let start = this.winHeight / 2 - uni.upx2px(this.idtHeight) / 2
this.start = start;
this.lists.forEach((item, index) => {
//20为40的一半,50为100的一半
const top = start + uni.upx2px(index * 40 + 20 - 50)
this.indicators.push(top)
})
},
startEmits(idx, index) {
let item = this.lists[idx]
let data = item.data[index] || {}
this.$emit('click', {
index: item.originalIndex,
letter: item.letter,
subIndex: index,
...data
})
},
onTap(idx, index) {
this.startEmits(idx, index)
},
// #ifdef H5
IsPC() {
var userAgentInfo = navigator.userAgent;
var Agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];
var flag = true;
for (let v = 0; v < Agents.length - 1; v++) {
if (userAgentInfo.indexOf(Agents[v]) > 0) {
flag = false;
break;
}
}
return flag;
},
// #endif
onClick(e) {
const {
idx,
index
} = e;
this.startEmits(idx, index)
},
touchStart(e) {
this.touching = true
let pageY = this.isPC ? e.pageY : e.touches[0].pageY
let index = this.getIndex(pageY - this.winOffsetY)
let item = this.lists[index]
if (item) {
this.scrollViewId = `fui_il_letter_${index}`
this.touchmoveIndex = index
}
},
touchMove(e) {
let pageY = this.isPC ? e.pageY : e.touches[0].pageY
let index = this.getIndex(pageY - this.winOffsetY)
if (this.touchmoveIndex === index) return false;
let item = this.lists[index]
if (item) {
this.scrollViewId = `fui_il_letter_${index}`
this.touchmoveIndex = index
}
// #ifdef APP-PLUS
throttleTouchMove.call(this, e)
// #endif
},
touchEnd() {
this.touching = false
this.touchmoveIndex = -1
},
mousedown(e) {
if (!this.isPC) return
this.touchStart(e)
},
mousemove(e) {
if (!this.isPC) return
this.touchMove(e)
},
mouseleave(e) {
if (!this.isPC) return
this.touchEnd(e)
},
// 滚动事件
scrollEvent(e) {
this.scrollTop = Math.floor(e.detail.scrollTop)
// this.getVisibleList()
}
}
};
</script>
<style scoped>
.fui-index__list {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
}
.fui-index__list-sv {
flex: 1;
}
.fui-index__list-letter {
/* #ifndef APP-NVUE */
width: 100%;
box-sizing: border-box;
display: flex;
position: sticky;
top: 0;
left: 0;
right: 0;
z-index: 10;
/* #endif */
flex: 1;
padding: 0 32rpx;
height: 64rpx;
line-height: 64rpx;
flex-direction: row;
align-items: center;
/* #ifdef APP-NVUE */
border-top-style: solid;
border-top-width: 0.5px;
border-top-color: #eee;
border-bottom-style: solid;
border-bottom-width: 0.5px;
border-bottom-color: #eee;
/* #endif */
}
.fui-il__letter-text {
font-size: 28rpx;
font-weight: 600;
}
/* #ifndef APP-NVUE */
.fui-index__list-letter::before {
content: " ";
position: absolute;
top: 0;
right: 0;
left: 0;
border-top: 1px solid var(--fui-color-border, #EEEEEE);
-webkit-transform: scaleY(0.5) translateZ(0);
transform: scaleY(0.5) translateZ(0);
transform-origin: 0 0;
z-index: 2;
pointer-events: none;
}
.fui-index__list-letter::after {
content: '';
position: absolute;
border-bottom: 1px solid var(--fui-color-border, #EEEEEE);
-webkit-transform: scaleY(0.5) translateZ(0);
transform: scaleY(0.5) translateZ(0);
transform-origin: 0 100%;
bottom: 0;
right: 0;
left: 0;
}
/* #endif */
.fui-index__letter {
position: fixed;
right: 0;
text-align: center;
z-index: 10;
/* #ifndef APP-NVUE */
top: 50%;
transform: translateY(-50%);
display: flex;
/* #endif */
flex-direction: column;
}
.fui-letter__item {
flex: 1;
padding: 0 8rpx;
font-weight: bold;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
align-items: center;
justify-content: center;
}
.fui-letter__key {
width: 40rpx;
height: 40rpx;
font-size: 26rpx;
transform: scale(0.8);
transform-origin: center center;
/* #ifndef APP-NVUE */
display: flex;
border-radius: 50%;
/* #endif */
/* #ifdef APP-NVUE */
border-radius: 40rpx;
line-height: 40rpx;
/* #endif */
align-items: center;
justify-content: center;
text-align: center;
/* #ifdef H5 */
cursor: pointer;
/* #endif */
}
.fui-il__indicator {
position: fixed;
width: 100rpx;
height: 100rpx;
z-index: 999;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
justify-content: center;
align-items: center;
border-radius: 10rpx;
right: 100rpx;
}
/* #ifdef APP-NVUE */
.fui-il__nvue-android {
width: 128rpx !important;
padding-right: 28rpx;
right: 72rpx !important;
}
/* #endif */
.fui-il__indicator-text {
text-align: center;
color: #ffffff;
font-size: 60rpx;
font-weight: bold;
}
.fui-il__indicator-after {
width: 100rpx;
position: absolute;
top: 0;
right: 0;
left: 0;
bottom: 0;
z-index: -1;
border-radius: 100rpx 0 100rpx 100rpx;
background: #c9c9c9;
transform: rotate(45deg);
}
/* #ifndef APP-NVUE */
.fui-il__key-color {
background: var(--fui-color-primary, #465CFF) !important;
}
.fui-il__key-bg {
background: var(--fui-bg-color-grey, #F1F4FA) !important;
}
/* #endif */
.fui-list-item,
.fui-list-item__item {
position: absolute;
width: 100%;
}
.debug {
position: fixed;
top: 120rpx;
right: 50rpx;
z-index: 999;
padding: 10rpx 20rpx;
background: #fff;
box-shadow: 0 0 0 2px rgba(0,0,0,.1);
border-radius: 4px;
}
</style>
...@@ -21,6 +21,12 @@ ...@@ -21,6 +21,12 @@
"navigationBarTitleText": "LinkedList 大列表示例" "navigationBarTitleText": "LinkedList 大列表示例"
} }
}, },
{
"path": "pages/example/linkedlist-nvue/index",
"style": {
"navigationBarTitleText": "LinkedList 大列表示例 - NVUE"
}
},
// === PDF 文件预览 === // === PDF 文件预览 ===
{ {
......
import { faker } from '@faker-js/faker/locale/zh_CN'
import { pinyin } from 'pinyin-pro'
export function getData() {
const data = Array(1000)
.fill(0)
.map((item) => {
const name = faker.name.fullName()
return {
id: item,
name,
phone: faker.phone.number('1###########'),
avatar: faker.image.avatar(),
unit: faker.company.name(),
checked: false,
}
})
const result = data
.map((item) => {
const props = {}
if (item.name) {
item.letter = pinyin(item.name.charAt(), { pattern: 'first' })[0].toUpperCase()
} else {
item.letter = '★'
}
return {
...item,
...props,
// TODO: 存在性能问题,需要优化后再使用
// src: item.avatar || '/static/images/me/user_default.jpg',
text: item.name,
subText: item.unit,
}
})
.reduce((prev, cur) => {
const letter = cur.letter
const index = prev.findIndex((item) => item.letter === letter)
if (index === -1) {
prev.push({
letter,
items: [cur],
})
} else {
prev[index].items.push(cur)
}
return prev
}, [])
.sort((a, b) => {
return a.letter.charCodeAt(0) - b.letter.charCodeAt(0)
})
return result
}
<template>
<list :id="pageId" class="page" :bounce="false" fixFreezing="true">
<cell>
<view id="head" class="header">
<text class="header-title">header</text>
</view>
</cell>
<cell>
<view class="tabs" :style="'height:' + pageHeight + 'px'">
<scroll-view
ref="tabbar1"
id="tab-bar"
class="tab-bar"
:scroll="false"
:scroll-x="true"
:show-scrollbar="false"
:scroll-into-view="scrollInto"
>
<view style="flex-direction: column">
<view style="flex-direction: row">
<view
class="uni-tab-item"
v-for="(tab, index) in tabList"
:key="tab.id"
:id="tab.id"
:ref="'tabitem' + index"
:data-id="index"
:data-current="index"
@click="ontabtap"
>
<text
class="uni-tab-item-title"
:class="tabIndex == index ? 'uni-tab-item-title-active' : ''"
>{{ tab.name }}</text
>
</view>
</view>
<view class="scroll-view-indicator">
<view
ref="underline"
class="scroll-view-underline"
:class="isTap ? 'scroll-view-animation' : ''"
:style="{ left: indicatorLineLeft + 'px', width: indicatorLineWidth + 'px' }"
></view>
</view>
</view>
</scroll-view>
<view class="tab-bar-line"></view>
<swiper
class="tab-view"
ref="swiper1"
id="tab-bar-view"
:current="tabIndex"
:duration="300"
@change="onswiperchange"
@transition="onswiperscroll"
@animationfinish="animationfinish"
@onAnimationEnd="animationfinish"
>
<swiper-item class="swiper-item" v-for="(page, index) in tabList" :key="index">
<swiper-page class="swiper-page" :pid="page.pageid" :parentId="pageId" ref="page"></swiper-page>
</swiper-item>
</swiper>
</view>
</cell>
</list>
</template>
<script>
// #ifdef APP-PLUS
const dom = weex.requireModule('dom')
// #endif
// 缓存每页最多
const MAX_CACHE_DATA = 100
// 缓存页签数量
const MAX_CACHE_PAGE = 3
const TAB_PRELOAD_OFFSET = 1
import swiperPage from './swiper-page.nvue'
export default {
components: {
swiperPage,
},
data() {
return {
tabList: [],
tabIndex: 0,
cacheTab: [],
scrollInto: '',
indicatorLineLeft: 0,
indicatorLineWidth: 0,
isTap: false,
showTitleView: true,
pageHeight: 300,
pageId: 'page',
}
},
onLoad() {
for (var i = 0; i < 1; i++) {
this.tabList.push({
id: 'tab' + i,
name: 'Tab ' + (i + 1),
pageid: i + 1,
})
}
},
onReady() {
this.pageHeight = uni.getSystemInfoSync().windowHeight
this._lastTabIndex = 0
this.swiperWidth = 0
this.tabbarWidth = 0
this.tabListSize = {}
this._touchTabIndex = 0
this._headHeight = 100
this.pageList = this.$refs.page
this.selectorQuery()
},
methods: {
ontabtap(e) {
let index = e.target.dataset.current || e.currentTarget.dataset.current
//let offsetIndex = this._touchTabIndex = Math.abs(index - this._lastTabIndex) > 1;
// #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-QQ
this.isTap = true
var currentSize = this.tabListSize[index]
this.updateIndicator(currentSize.left, currentSize.width)
this._touchTabIndex = index
// #endif
this.switchTab(index)
},
onswiperchange(e) {
// 注意:百度小程序会触发2次
// #ifndef APP-PLUS || H5 || MP-WEIXIN || MP-QQ
let index = e.target.current || e.detail.current
this.switchTab(index)
// #endif
},
onswiperscroll(e) {
if (this.isTap) {
return
}
var offsetX = e.detail.dx
var preloadIndex = this._lastTabIndex
if (offsetX > TAB_PRELOAD_OFFSET) {
preloadIndex++
} else if (offsetX < -TAB_PRELOAD_OFFSET) {
preloadIndex--
}
if (
preloadIndex === this._lastTabIndex ||
preloadIndex < 0 ||
preloadIndex > this.pageList.length - 1
) {
return
}
if (this.pageList[preloadIndex].dataList.length === 0) {
this.loadTabData(preloadIndex)
}
// #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-QQ
var percentage = Math.abs(this.swiperWidth / offsetX)
var currentSize = this.tabListSize[this._lastTabIndex]
var preloadSize = this.tabListSize[preloadIndex]
var lineL = currentSize.left + (preloadSize.left - currentSize.left) / percentage
var lineW = currentSize.width + (preloadSize.width - currentSize.width) / percentage
this.updateIndicator(lineL, lineW)
// #endif
},
animationfinish(e) {
// #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-QQ
let index = e.detail.current
if (this._touchTabIndex === index) {
this.isTap = false
}
this._lastTabIndex = index
this.switchTab(index)
this.updateIndicator(this.tabListSize[index].left, this.tabListSize[index].width)
// #endif
},
selectorQuery() {
// #ifdef APP-NVUE
uni.createSelectorQuery()
.in(this)
.select('#head')
.boundingClientRect()
.exec((rect) => {
this._headHeight = rect[0].height
})
// 查询 tabbar 宽度
uni.createSelectorQuery()
.in(this)
.select('#tab-bar')
.boundingClientRect()
.exec((rect) => {
this.tabbarWidth = rect[0].width
})
// 查询 tabview 宽度
uni.createSelectorQuery()
.in(this)
.select('#tab-bar-view')
.boundingClientRect()
.exec((rect) => {
this.swiperWidth = rect[0].width
})
// 因 nvue 暂不支持 class 查询
var queryTabSize = uni.createSelectorQuery().in(this)
for (var i = 0; i < this.tabList.length; i++) {
queryTabSize.select('#' + this.tabList[i].id).boundingClientRect()
}
queryTabSize.exec((rects) => {
rects.forEach((rect) => {
this.tabListSize[rect.dataset.id] = rect
})
this.updateIndicator(this.tabListSize[this.tabIndex].left, this.tabListSize[this.tabIndex].width)
this.switchTab(this.tabIndex)
})
// #endif
// #ifdef MP-WEIXIN || H5 || MP-QQ
uni.createSelectorQuery()
.in(this)
.select('.tab-view')
.fields(
{
dataset: true,
size: true,
},
(res) => {
this.swiperWidth = res.width
},
)
.exec()
uni.createSelectorQuery()
.in(this)
.selectAll('.uni-tab-item')
.boundingClientRect((rects) => {
rects.forEach((rect) => {
this.tabListSize[rect.dataset.id] = rect
})
this.updateIndicator(
this.tabListSize[this.tabIndex].left,
this.tabListSize[this.tabIndex].width,
)
})
.exec()
// #endif
},
updateIndicator(left, width) {
this.indicatorLineLeft = left
this.indicatorLineWidth = width
},
switchTab(index) {
if (this.pageList[index].dataList.length === 0) {
this.loadTabData(index)
}
this.pageList[index].setScrollRef(this._headHeight)
if (this.tabIndex === index) {
return
}
// 缓存 tabId
if (this.pageList[this.tabIndex].dataList.length > MAX_CACHE_DATA) {
let isExist = this.cacheTab.indexOf(this.tabIndex)
if (isExist < 0) {
this.cacheTab.push(this.tabIndex)
}
}
this.tabIndex = index
// #ifdef APP-NVUE
this.scrollTabTo(index)
// #endif
// #ifndef APP-NVUE
this.scrollInto = this.tabList[index].id
// #endif
// 释放 tabId
if (this.cacheTab.length > MAX_CACHE_PAGE) {
let cacheIndex = this.cacheTab[0]
this.clearTabData(cacheIndex)
this.cacheTab.splice(0, 1)
}
},
scrollTabTo(index) {
const el = this.$refs['tabitem' + index][0]
let offset = 0
// TODO fix ios offset
if (index > 0) {
offset = this.tabbarWidth / 2 - this.tabListSize[index].width / 2
if (this.tabListSize[index].right < this.tabbarWidth / 2) {
offset = this.tabListSize[0].width
}
}
dom.scrollToElement(el, {
offset: -offset,
})
},
loadTabData(index) {
this.pageList[index].loadData()
},
clearTabData(index) {
this.pageList[index].clear()
},
},
}
</script>
<style>
/* #ifndef APP-PLUS */
page {
width: 100%;
min-height: 100%;
display: flex;
}
/* #endif */
.page {
flex: 1;
}
.header {
height: 160px;
flex-direction: row;
align-items: center;
justify-content: center;
background-color: #f4f4f4;
}
.header-title {
font-size: 30px;
font-weight: bold;
color: #444;
}
.flexible-view {
background-color: #f823ff;
}
.page-head {
height: 200px;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: red;
}
.tabs {
flex-direction: column;
overflow: hidden;
background-color: #ffffff;
/* #ifdef MP-ALIPAY || MP-BAIDU */
height: 100vh;
/* #endif */
}
.tab-bar {
width: 750rpx;
height: 84rpx;
flex-direction: row;
/* #ifndef APP-PLUS */
white-space: nowrap;
/* #endif */
}
/* #ifndef APP-NVUE */
.tab-bar ::-webkit-scrollbar {
display: none;
width: 0 !important;
height: 0 !important;
-webkit-appearance: none;
background: transparent;
}
/* #endif */
.scroll-view-indicator {
position: relative;
height: 2px;
background-color: transparent;
}
.scroll-view-underline {
position: absolute;
top: 0;
bottom: 0;
width: 0;
background-color: #007aff;
}
.scroll-view-animation {
transition-duration: 0.2s;
transition-property: left;
}
.tab-bar-line {
height: 1rpx;
background-color: #cccccc;
}
.tab-view {
flex: 1;
}
.uni-tab-item {
/* #ifndef APP-PLUS */
display: inline-block;
/* #endif */
flex-wrap: nowrap;
padding-left: 25px;
padding-right: 25px;
}
.uni-tab-item-title {
color: #555;
font-size: 30rpx;
height: 80rpx;
line-height: 80rpx;
flex-wrap: nowrap;
/* #ifndef APP-PLUS */
white-space: nowrap;
/* #endif */
}
.uni-tab-item-title-active {
color: #007aff;
}
.swiper-item {
flex: 1;
flex-direction: column;
}
.swiper-page {
flex: 1;
flex-direction: row;
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
}
</style>
<template>
<view class="uni-swiper-page">
<list ref="list" class="list" :offset-accuracy="5" :bounce="false" fixFreezing="true">
<cell v-for="(item, index) in dataList" :key="item.id" :ref="'item' + index">
<view class="list-item">
<text>{{ item.name }}</text>
</view>
</cell>
<cell class="loading"></cell>
</list>
</view>
</template>
<script>
import { faker } from '@faker-js/faker/locale/zh_CN'
import { pinyin } from 'pinyin-pro'
import fuiIndexList from '../../../components/FirstUI/fui-index-list/fui-index-list.vue'
export default {
components: {
fuiIndexList,
},
props: {
pid: {
type: [Number, String],
default: '',
},
parentId: {
type: String,
default: '',
},
},
data() {
return {
scrollable: true,
dataList: [],
}
},
created() {
for (var i = 0; i < 5000; i++) {
this.dataList.push({
id: i,
// name: faker.name.lastName() + faker.name.firstName(),
name: i,
})
}
},
methods: {
getData() {
const data = Array(1000)
.fill(0)
.map((item) => {
return {
id: item,
name: faker.name.lastName() + faker.name.firstName(),
phone: faker.phone.number('1###########'),
avatar: faker.image.avatar(),
unit: faker.company.name(),
checked: false,
}
})
const result = data
.map((item) => {
const props = {}
if (item.name) {
item.letter = pinyin(item.name.charAt(), { pattern: 'first' })[0].toUpperCase()
} else {
item.letter = '★'
}
return {
...item,
...props,
// TODO: 存在性能问题,需要优化后再使用
src: item.avatar || '/static/images/me/user_default.jpg',
text: item.name,
subText: item.unit,
}
})
.reduce((prev, cur) => {
const letter = cur.letter
const index = prev.findIndex((item) => item.letter === letter)
if (index === -1) {
prev.push({
letter,
items: [cur],
})
} else {
prev[index].items.push(cur)
}
return prev
}, [])
.sort((a, b) => {
return a.letter.charCodeAt(0) - b.letter.charCodeAt(0)
})
return result
},
setScrollRef(height) {
if (this.$refs['list'].setSpecialEffects) {
this.$refs['list'].setSpecialEffects({
id: this.parentId,
headerHeight: height,
})
}
},
loadData() {
// 首次激活时被调用
},
clear() {
// 释放数据时被调用,参考 swiper-list 缓存配置
this.dataList.length = 0
},
},
}
</script>
<style scoped>
.uni-swiper-page {
flex: 1;
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
}
.list {
flex: 1;
background-color: #ebebeb;
}
.list-item {
margin-left: 12px;
margin-right: 12px;
margin-top: 12px;
padding: 20px;
background-color: #fff;
border-radius: 5px;
}
.loading {
height: 20px;
}
</style>
import Mock from 'mockjs' import { faker } from '@faker-js/faker/locale/zh_CN'
import { pinyin } from 'pinyin-pro' import { pinyin } from 'pinyin-pro'
export function getData() { export function getData() {
const data = Array(1000) const data = Array(1000)
.fill(0) .fill(0)
.map(() => { .map((item) => {
const name = Mock.Random.cname()
return { return {
id: Mock.Random.guid(), id: item,
name, name: faker.name.lastName() + faker.name.firstName(),
phone: String(Mock.Random.integer(13000000000, 19999999999)), phone: faker.phone.number('1###########'),
avatar: Mock.Random.image('50x50', Mock.Random.color(), '#757575', 'png', name.charAt(0)), avatar: faker.image.avatar(),
unit: Mock.Random.cword(3, 5), unit: faker.company.name(),
checked: false, checked: false,
} }
}) })
...@@ -21,9 +19,9 @@ export function getData() { ...@@ -21,9 +19,9 @@ export function getData() {
.map((item: any) => { .map((item: any) => {
const props = {} const props = {}
if (item.name) { if (item.name) {
item.letter = pinyin(item.name.charAt(), { pattern: 'first' })[0].toUpperCase() item.letter = pinyin(item.name.charAt(), { toneType: 'none', pattern: 'first' })[0].toUpperCase()
} else { } else {
item.letter = '' item.letter = '#'
} }
return { return {
......
...@@ -93,7 +93,7 @@ ...@@ -93,7 +93,7 @@
<template> <template>
<view class="wrap"> <view class="wrap">
<!-- --> <!-- -->
<fui-index-list <fui-virtual-index-list
isSrc isSrc
:listData="data.list" :listData="data.list"
:isSelect="model.params?.multiple" :isSelect="model.params?.multiple"
...@@ -101,6 +101,7 @@ ...@@ -101,6 +101,7 @@
@init="model.show = true" @init="model.show = true"
@click="choose" @click="choose"
:class="model.params?.multiple ? 'multiple' : ''" :class="model.params?.multiple ? 'multiple' : ''"
:debug="true"
> >
<fui-search-bar @input="search" @clear="model.search = ''" /> <fui-search-bar @input="search" @clear="model.search = ''" />
<template #footer> <template #footer>
...@@ -110,7 +111,7 @@ ...@@ -110,7 +111,7 @@
<!-- 安全区 --> <!-- 安全区 -->
<fui-safe-area /> <fui-safe-area />
</template> </template>
</fui-index-list> </fui-virtual-index-list>
<!-- 多选确认按钮 --> <!-- 多选确认按钮 -->
......
...@@ -12,7 +12,11 @@ export default { ...@@ -12,7 +12,11 @@ export default {
loadLibs() { loadLibs() {
const id = 'mapbox-gl' const id = 'mapbox-gl'
const resource = 'static/mapbox-gl-js/v2.13.0/mapbox-gl' const resource = 'static/mapbox-gl-js/v2.13.0/mapbox-gl'
return Promise.all([appendScript(id, `${resource}.js`), appendStylesheet(id, `${resource}.css`)]) return Promise.all([
appendScript(id, `${resource}.js`),
appendStylesheet(id, `${resource}.css`),
appendStylesheet(id, `${resource}.extra.css`),
])
}, },
initMap(options) { initMap(options) {
// 移除地图 // 移除地图
......
...@@ -12,6 +12,13 @@ ...@@ -12,6 +12,13 @@
icon: 'emojione-letter-z', icon: 'emojione-letter-z',
page: `/pages/example/linkedlist/index?multiple=true`, page: `/pages/example/linkedlist/index?multiple=true`,
}, },
// #ifdef APP-PLUS
{
name: 'LinkedList 大列表示例 - NVUE',
icon: 'emojione-letter-z',
page: `/pages/example/linkedlist-nvue/index?multiple=true`,
},
// #endif
], ],
}) })
......
.mapboxgl-popup-content {
box-shadow: 0 1px 5px #6e6e6e !important;
}
.mapboxgl-popup-content .mapboxgl-custom-popup {
position: relative;
}
.mapboxgl-custom-popup .popup-title {
font-weight: bold;
}
.mapboxgl-ctrl-logo {
display: none !important;
}
.mapboxgl-ctrl-bottom-left {
display: flex;
bottom: 0;
left: 2px;
}
.mapboxgl-ctrl-lnglat {
background: rgb(0 0 0 / 40%);
color: white;
line-height: 2em;
padding: 0 10px;
box-shadow: 0 0 0 2px rgba(0,0,0,.1);
border-radius: 4px;
}
.mapboxgl-ctrl button.mapboxgl-ctrl-fullscreen .mapboxgl-ctrl-icon {
background-image: url('data:image/svg+xml,%3Csvg xmlns="http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg" width="24" height="24" preserveAspectRatio="xMidYMid meet" viewBox="0 0 16 16"%3E%3Cpath fill="currentColor" d="M3.75 3a.75.75 0 0 0-.75.75V5.5a.5.5 0 0 1-1 0V3.75C2 2.784 2.784 2 3.75 2H5.5a.5.5 0 0 1 0 1H3.75ZM10 2.5a.5.5 0 0 1 .5-.5h1.75c.966 0 1.75.784 1.75 1.75V5.5a.5.5 0 0 1-1 0V3.75a.75.75 0 0 0-.75-.75H10.5a.5.5 0 0 1-.5-.5ZM2.5 10a.5.5 0 0 1 .5.5v1.75c0 .414.336.75.75.75H5.5a.5.5 0 0 1 0 1H3.75A1.75 1.75 0 0 1 2 12.25V10.5a.5.5 0 0 1 .5-.5Zm11 0a.5.5 0 0 1 .5.5v1.75A1.75 1.75 0 0 1 12.25 14H10.5a.5.5 0 0 1 0-1h1.75a.75.75 0 0 0 .75-.75V10.5a.5.5 0 0 1 .5-.5Z"%2F%3E%3C%2Fsvg%3E') !important;
}
.mapboxgl-ctrl button.mapboxgl-ctrl-shrink .mapboxgl-ctrl-icon {
background-image: url('data:image/svg+xml,%3Csvg xmlns="http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg" width="24" height="24" preserveAspectRatio="xMidYMid meet" viewBox="0 0 16 16"%3E%3Cpath fill="currentColor" d="M11 4a1 1 0 0 0 1 1h1.5a.5.5 0 0 1 0 1H12a2 2 0 0 1-2-2V2.5a.5.5 0 0 1 1 0V4Zm0 8a1 1 0 0 1 1-1h1.5a.5.5 0 0 0 0-1H12a2 2 0 0 0-2 2v1.5a.5.5 0 0 0 1 0V12Zm-7-1a1 1 0 0 1 1 1v1.5a.5.5 0 0 0 1 0V12a2 2 0 0 0-2-2H2.5a.5.5 0 0 0 0 1H4Zm1-7a1 1 0 0 1-1 1H2.5a.5.5 0 0 0 0 1H4a2 2 0 0 0 2-2V2.5a.5.5 0 0 0-1 0V4Z"%2F%3E%3C%2Fsvg%3E') !important;
}
.mapboxgl-ctrl .mapboxgl-ctrl-reset-button .mapboxgl-ctrl-icon {
background-image: url('data:image/svg+xml,%3Csvg xmlns="http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg" width="20" height="20" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"%3E%3Cpath fill="currentColor" d="M12 16c1.671 0 3-1.331 3-3s-1.329-3-3-3s-3 1.331-3 3s1.329 3 3 3z"%2F%3E%3Cpath fill="currentColor" d="M20.817 11.186a8.94 8.94 0 0 0-1.355-3.219a9.053 9.053 0 0 0-2.43-2.43a8.95 8.95 0 0 0-3.219-1.355a9.028 9.028 0 0 0-1.838-.18V2L8 5l3.975 3V6.002c.484-.002.968.044 1.435.14a6.961 6.961 0 0 1 2.502 1.053a7.005 7.005 0 0 1 1.892 1.892A6.967 6.967 0 0 1 19 13a7.032 7.032 0 0 1-.55 2.725a7.11 7.11 0 0 1-.644 1.188a7.2 7.2 0 0 1-.858 1.039a7.028 7.028 0 0 1-3.536 1.907a7.13 7.13 0 0 1-2.822 0a6.961 6.961 0 0 1-2.503-1.054a7.002 7.002 0 0 1-1.89-1.89A6.996 6.996 0 0 1 5 13H3a9.02 9.02 0 0 0 1.539 5.034a9.096 9.096 0 0 0 2.428 2.428A8.95 8.95 0 0 0 12 22a9.09 9.09 0 0 0 1.814-.183a9.014 9.014 0 0 0 3.218-1.355a8.886 8.886 0 0 0 1.331-1.099a9.228 9.228 0 0 0 1.1-1.332A8.952 8.952 0 0 0 21 13a9.09 9.09 0 0 0-.183-1.814z"%2F%3E%3C%2Fsvg%3E') !important;
}
.mapboxgl-ctrl .mapboxgl-ctrl-layer-button .mapboxgl-ctrl-icon {
background-image: url('data:image/svg+xml,%3Csvg xmlns="http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg" width="24" height="24" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"%3E%3Cpath fill="currentColor" d="m13.387 3.425l6.365 4.243a1 1 0 0 1 0 1.664l-6.365 4.244a2.5 2.5 0 0 1-2.774 0L4.248 9.332a1 1 0 0 1 0-1.664l6.365-4.243a2.5 2.5 0 0 1 2.774 0Zm6.639 8.767a2.002 2.002 0 0 1-.577.598l-6.05 4.084a2.5 2.5 0 0 1-2.798 0l-6.05-4.084a2 2 0 0 1-.779-2.29l6.841 4.56a2.5 2.5 0 0 0 2.613.098l.16-.098l6.841-4.56a1.996 1.996 0 0 1-.201 1.692Zm0 3.25a2.002 2.002 0 0 1-.577.598l-6.05 4.084a2.5 2.5 0 0 1-2.798 0l-6.05-4.084a2 2 0 0 1-.779-2.29l6.841 4.56a2.5 2.5 0 0 0 2.613.098l.16-.098l6.841-4.56a1.996 1.996 0 0 1-.201 1.692Z"%2F%3E%3C%2Fsvg%3E') !important;
}
.mapboxgl-ctrl button.-active svg {
color: #4264fb;
}
.mapboxgl-ctrl-layer-button {
z-index: 1;
}
.mapboxgl-ctrl-layer-wrap {
z-index: 100;
display: flex;
justify-items: center;
align-items: center;
position: absolute;
top: 0;
left: -220px;
visibility: hidden;
padding: 10px;
border-radius: 3px;
box-shadow: 0 0 3px #333;
background-color: #fff;
}
.mapboxgl-ctrl-layer {
width: 60px;
height: 60px;
display: flex;
justify-content: center;
border: 2px solid #fff;
position: relative;
}
.mapboxgl-ctrl-layer.active {
border-color: #3385ff;
}
.mapboxgl-ctrl-layer img {
width: 100%;
height: 100%;
}
.mapboxgl-ctrl-layer span {
display: inline-flex;
justify-content: center;
background-color: rgb(0 0 0 / 60%);
color: white;
position: absolute;
bottom: 0;
width: 100%;
}
.mapboxgl-ctrl-layer-button.-active .mapboxgl-ctrl-layer-wrap {
visibility: visible;
}
.mapboxgl-ctrl-bottom-left,
.mapboxgl-ctrl-bottom-right {
padding-bottom: 0;
/* stylelint-disable-next-line function-no-unknown */
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
...@@ -123,10 +123,12 @@ declare module '@vue/runtime-core' { ...@@ -123,10 +123,12 @@ declare module '@vue/runtime-core' {
FuiTopPopup: typeof import('./../src/components/FirstUI/fui-top-popup/fui-top-popup.vue')['default'] FuiTopPopup: typeof import('./../src/components/FirstUI/fui-top-popup/fui-top-popup.vue')['default']
FuiUpload: typeof import('./../src/components/FirstUI/fui-upload/fui-upload.vue')['default'] FuiUpload: typeof import('./../src/components/FirstUI/fui-upload/fui-upload.vue')['default']
FuiUploadVideo: typeof import('./../src/components/FirstUI/fui-upload-video/fui-upload-video.vue')['default'] FuiUploadVideo: typeof import('./../src/components/FirstUI/fui-upload-video/fui-upload-video.vue')['default']
FuiVirtualIndexList: typeof import('./../src/components/FirstUI/fui-virtual-index-list/fui-virtual-index-list.vue')['default']
FuiVtabs: typeof import('./../src/components/FirstUI/fui-vtabs/fui-vtabs.vue')['default'] FuiVtabs: typeof import('./../src/components/FirstUI/fui-vtabs/fui-vtabs.vue')['default']
FuiVtabsContent: typeof import('./../src/components/FirstUI/fui-vtabs-content/fui-vtabs-content.vue')['default'] FuiVtabsContent: typeof import('./../src/components/FirstUI/fui-vtabs-content/fui-vtabs-content.vue')['default']
FuiWaterfall: typeof import('./../src/components/FirstUI/fui-waterfall/fui-waterfall.vue')['default'] FuiWaterfall: typeof import('./../src/components/FirstUI/fui-waterfall/fui-waterfall.vue')['default']
FuiWaterfallItem: typeof import('./../src/components/FirstUI/fui-waterfall-item/fui-waterfall-item.vue')['default'] FuiWaterfallItem: typeof import('./../src/components/FirstUI/fui-waterfall-item/fui-waterfall-item.vue')['default']
FVirtualIndexListItem: typeof import('./../src/components/FirstUI/fui-virtual-index-list/f-virtual-index-list-item.vue')['default']
Icon: typeof import('./../src/components/Icon/index.vue')['default'] Icon: typeof import('./../src/components/Icon/index.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink'] RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView'] RouterView: typeof import('vue-router')['RouterView']
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论