提交 2c9ba045 编写于 作者: H hdx

long-list: 调整为 swiper-list 结构

上级 1cdd1c58
<template>
<list-view class="list" ref="listView" @scrolltolower="loadData">
<list-item class="list-item" v-for="(item, index) in dataList" :key="index">
<view class="list-item-icon">
<image class="list-item-icon-image" :src="item.plugin_img_link"></image>
</view>
<view class="list-item-fill">
<view class="flex-row">
<text class="title">{{item.plugin_name}}</text>
</view>
<view class="description">
<text class="description-text">{{item.plugin_intro}}</text>
</view>
<text class="icon-star star">{{getStarUnicode(item.score)}}</text>
<view class="tag-list">
<text class="tag-item" v-for="(item2, index2) in item.tags" :key="index2">{{item2}}</text>
</view>
<view class="flex-row update-date">
<text class="update-date-text">更新日期</text>
<text class="update-date-value">{{item.update_date}}</text>
<text class="author">{{item.author_name}}</text>
</view>
</view>
</list-item>
</list-view>
</template>
<script>
const SERVER_URL = "https://ext.dcloud.net.cn/plugin/uniappx-plugin-list"
const PAGE_SIZE = 10; // 最大值 10
type ListItem = {
plugin_id : number,
plugin_img_link : string,
plugin_name : string,
plugin_intro : string,
score : number,
tags : Array<string>,
update_date : string,
author_name : string,
}
type ResponseDataType = {
code : number,
data : ListItem[]
}
export default {
props: {
type: {
type: String,
default: ''
}
},
data() {
return {
loading: false,
dataList: [] as ListItem[],
isEnded: false,
$currentPage: 0
}
},
mounted() {
uni.loadFontFace({
global: false,
family: 'UtsIconsFontFamily',
source: '/static/fonts/icon-star.ttf'
})
this.loadData()
},
methods: {
loadData() {
if (this.loading || this.isEnded) {
return
}
this.loading = true
// TODO request data 没有拼接到 url 中,暂时手动拼接
uni.request({
url: `${SERVER_URL}?type=${this.type}&page=${this.$currentPage}&page_size=${PAGE_SIZE}`,
data: {
type: this.type,
page: this.$currentPage,
page_size: PAGE_SIZE
},
dataType: '',
success: (res) => {
const responseData = JSON.parse<ResponseDataType>(res.data as string)
if (responseData == null) {
return
}
responseData.data.forEach((item) => {
this.dataList.push(item)
})
if (responseData.data.length <= 0) {
this.isEnded = true
} else {
this.$currentPage++
}
},
fail: (err) => {
console.log(err)
},
complete: () => {
this.loading = false
}
})
},
// score 0 ~ 50
getStarUnicode(score : Number) : string {
const fill_code = '\ue879'
const half_code = '\ue87a'
const null_code = '\ue87b'
const fillStarCount = parseInt(score / 10 % 10 + '')
const halfStarCount = score % 10 >= 5 ? 1 : 0
const nullStarCount = 5 - fillStarCount - halfStarCount
let result = ''
if (fillStarCount > 0) { result += fill_code.repeat(fillStarCount) }
if (halfStarCount > 0) { result += half_code.repeat(halfStarCount) }
if (nullStarCount > 0) { result += null_code.repeat(nullStarCount) }
return result
},
// onScroll(e : ScrollEvent) {
// console.log(e.detail.deltaY);
// }
}
}
</script>
<style>
.list {
flex: 1;
background-color: #ffffff;
}
.list-item {
flex-direction: row;
margin-top: 10px;
padding: 10px;
}
.list-item-icon {
position: relative;
}
.list-item-icon-image {
width: 80px;
height: 80px;
}
.list-item-fill {
flex: 1;
margin-left: 15px;
}
.description {
line-height: 14px;
}
.description-text {
font-size: 13px;
color: #666;
line-height: 19px;
}
.icon-star {
font-family: "UtsIconsFontFamily" !important;
font-size: 16px;
font-style: normal;
color: #ffca3e;
letter-spacing: 3px;
}
.tag-list {
flex-direction: row;
margin-top: 5px;
}
.tag-item {
font-size: 14px;
background-color: #EFF9F0;
color: #639069;
border-radius: 20px;
margin-right: 5px;
padding: 2px 5px;
}
.update-date {
margin-top: 10px;
}
.update-date-text {
font-size: 12px;
color: #888888;
}
.update-date-value {
font-size: 12px;
color: #777777;
margin-left: 5px;
}
.author {
font-size: 12px;
color: #008000;
margin-left: auto;
}
</style>
\ No newline at end of file
<template> <template>
<list-view class="list" @scrolltolower="loadData"> <scroll-view class="page" :scroll-top="pageScrollTop">
<list-item class="list-item" v-for="(item, index) in dataList" :key="index"> <view class="search-bar">
<view class="list-item-icon"> <input placeholder="搜索..." />
<image class="list-item-icon-image" :src="item.author_avatar_link"></image> </view>
</view> <view class="swiper-list">
<view class="list-item-fill"> <scroll-view class="swiper-tabs" :scroll-left="tabsScrollLeft" :scroll-x="true" :show-scrollbar="false">
<view class="flex-row"> <view>
<text class="title">{{item.plugin_name}}</text> <view class="flex-row">
<view class="swiper-tabs-item" v-for="(item, index) in swiperList" :id="'swipertab' + index" ref="swipertab"
:key="index" @click="onTabClick(index)">
<text class="swiper-tabs-item-text"
:class="swiperIndex==index ? 'uni-tab-item-title-active' : ''">{{item.name}}</text>
</view>
</view>
<view class="swiper-tabs-indicator">
<view class="swiper-tabs-underline"
:style="{left: swiperIndicatorLineLeft + 'px', width: swiperIndicatorLineWidth + 'px'}"></view>
</view>
</view> </view>
<view class="description"> </scroll-view>
<text class="description-text">{{item.plugin_intro}}</text> <swiper class="swiper-view" ref="swiper" :current="swiperIndex" @change="onSwiperChange"
</view> @transition="onSwiperTransition" @animationfinish="onSwiperAnimationfinish">
<view class="tag-list"> <swiper-item class="swiper-item" v-for="(item, index) in swiperList" :key="index">
<text class="tag-item" v-for="(item2, index2) in item.tags" :key="index2">{{item2}}</text> <long-page :type="item.type"></long-page>
</view> </swiper-item>
<!-- <uts-rate></uts-rate> --> </swiper>
<view class="flex-row update-date"> </view>
<text class="update-date-text">更新日期</text> </scroll-view>
<text class="update-date-value">{{item.update_date}}</text>
<text class="author">{{item.author_name}}</text>
</view>
</view>
</list-item>
</list-view>
</template> </template>
<script> <script>
const SERVER_URL = "https://ext.dcloud.net.cn/plugin/uniappx-plugin-list" import longPage from './long-list-page.uvue';
const PAGE_SIZE = 10; // 最大值 10
type SwiperTabsItem = {
type ListItem = { left : number,
plugin_id : number, width : number
author_avatar_link : string, }
plugin_name : string,
plugin_intro : string, type SwiperViewItem = {
tags : Array<string>, type : string,
update_date : string, name : string
author_name : string, }
}
// 测试数据
type ResponseDataType = { const CategoryData = [
code : number, {
data : ListItem[] type: 'UpdatedDate',
} name: '最新上架'
} as SwiperViewItem,
{
type: 'FreeHot',
name: '免费热榜'
} as SwiperViewItem,
{
type: 'PaymentHot',
name: '付费热榜'
} as SwiperViewItem,
{
type: 'HotList',
name: '热门总榜'
} as SwiperViewItem
]
export default { export default {
components: {
longPage
},
data() { data() {
return { return {
loading: false, pageScrollTop: 0,
dataList: [] as ListItem[], swiperList: [] as SwiperViewItem[],
isEnded: false, swiperIndex: -1,
$currentPage: 0 tabsScrollLeft: 0,
swiperIndicatorLineLeft: 0,
swiperIndicatorLineWidth: 0,
$lastSwiperIndex: 0,
$swiperWidth: 0,
$swiperTabsRect: [] as SwiperTabsItem[],
$isTap: false
} }
}, },
onLoad() { onLoad() {
this.loadData() CategoryData.forEach((item) => {
this.swiperList.push(item)
})
},
onReady() {
this.$swiperWidth = (this.$refs["swiper"] as INode)?.offsetWidth as number
this.queryTabItemsSize()
this.setSwiperIndex(0, true)
}, },
methods: { methods: {
loadData() { onTabClick(index : number) {
if (this.loading || this.isEnded) { this.setSwiperIndex(index, false)
},
onSwiperChange(e : SwiperChangeEvent) {
this.setSwiperIndex(e.detail.current, false)
},
onSwiperTransition(e : SwiperTransitionEvent) {
const offsetX = e.detail.dx;
let moveToIndex = offsetX > 0 ? this.$lastSwiperIndex + 1 : this.$lastSwiperIndex - 1
if (moveToIndex < 0) { moveToIndex = 0 }
if (moveToIndex > this.$swiperTabsRect.length - 1) { moveToIndex = this.$swiperTabsRect.length - 1 }
const percentage = Math.abs(offsetX) / this.$swiperWidth;
const currentSize = this.$swiperTabsRect[this.$lastSwiperIndex];
const moveToSize = this.$swiperTabsRect[moveToIndex];
const indicatorlineL = currentSize.left + (moveToSize.left - currentSize.left) * percentage;
const indicatorlineW = currentSize.width + (moveToSize.width - currentSize.width) * percentage;
this.updateTabIndicator(indicatorlineL, indicatorlineW);
//console.log(this.$lastSwiperIndex, moveToIndex, offsetX, this.$swiperWidth, percentage);
},
onSwiperAnimationfinish(e : SwiperAnimationFinishEvent) {
this.$lastSwiperIndex = e.detail.current;
// console.log("onSwiperAnimationfinish", e.detail.current);
// this.setSwiperIndex(e.detail.current, true);
},
queryTabItemsSize() {
this.$swiperTabsRect.length = 0;
const tabs = this.$refs["swipertab"] as INode[]
tabs.forEach((node) => {
this.$swiperTabsRect.push({
left: node.offsetLeft as number,
width: node.offsetWidth as number
} as SwiperTabsItem)
})
},
setSwiperIndex(index : Number, updateIndicator : Boolean) {
if (this.swiperIndex === index) {
return return
} }
this.swiperIndex = index
this.loading = true; if (updateIndicator) {
this.updateTabIndicator(this.$swiperTabsRect[index].left, this.$swiperTabsRect[index].width)
// TODO request data 没有拼接到 url 中,暂时手动拼接 }
uni.request({ },
url: `${SERVER_URL}?page=${this.$currentPage}&page_size=${PAGE_SIZE}`, updateTabIndicator(left : Number, width : Number) {
data: { this.swiperIndicatorLineLeft = left
page: this.$currentPage, this.swiperIndicatorLineWidth = width
page_size: PAGE_SIZE const offset = left + width / 2
}, if (offset > this.$swiperWidth / 2) {
dataType: '', this.tabsScrollLeft = offset - this.$swiperWidth / 2
success: (res) => { }
const responseData = JSON.parse<ResponseDataType>(res.data as string)
if (responseData == null) {
return
}
responseData.data.forEach((item) => {
this.dataList.push(item)
})
this.isEnded = responseData.data.length <= 0;
this.$currentPage++
},
fail: (err) => {
console.log(err);
},
complete: () => {
this.loading = false;
}
})
} }
} }
} }
...@@ -103,86 +155,53 @@ ...@@ -103,86 +155,53 @@
flex-direction: row; flex-direction: row;
} }
.list { .page {
flex: 1; flex: 1;
background-color: #ffffff;
} }
.list-item { .search-bar {
flex-direction: row;
margin-top: 10px;
padding: 10px; padding: 10px;
} }
.list-item-icon { .swiper-list {
position: relative; height: 100%;
} }
.list-item-icon-image { .swiper-tabs {
width: 80px; background-color: #ffffff;
height: 80px;
}
.list-item-icon-index {
font-size: 12px;
color: #cccccc;
position: absolute;
left: 2px;
bottom: 2px;
}
.list-item-fill {
flex: 1;
margin-left: 15px;
}
.index {
margin-left: 10px;
}
.description {
margin-top: 3px;
} }
.description-text { .swiper-tabs-item {
font-size: 13px; padding: 12px 25px;
color: #666;
margin-top: 5px;
} }
.tag-list { .swiper-tabs-item-text {
flex-direction: row; color: #555;
margin-top: 5px; font-size: 16px;
} }
.tag-item { .swiper-tabs-item-text-active {
font-size: 14px; color: #007AFF;
font-weight: bold;
background-color: #EFF9F0;
color: #639069;
border-radius: 20px;
margin-right: 5px;
padding: 2px 5px;
} }
.update-date { .swiper-tabs-indicator {
margin-top: 10px; position: relative;
height: 2px;
} }
.update-date-text { .swiper-tabs-underline {
font-size: 12px; position: absolute;
color: #888888; top: 0;
bottom: 0;
width: 0;
background-color: #007AFF;
} }
.update-date-value { .swiper-view {
font-size: 12px; flex: 1;
color: #777777;
margin-left: 5px;
} }
.author { .swiper-item {
font-size: 12px; flex: 1;
color: #005000;
margin-left: auto;
} }
</style> </style>
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册