287 lines
6.0 KiB
Plaintext
287 lines
6.0 KiB
Plaintext
<template>
|
||
<view class="page">
|
||
<!-- 分类筛选 -->
|
||
<view class="category-bar">
|
||
<scroll-view class="category-scroll" scroll-x>
|
||
<view class="category-list">
|
||
<view
|
||
class="category-item"
|
||
:class="{ 'category-active': currentCategory == item.id }"
|
||
v-for="item in categories"
|
||
:key="item.id"
|
||
@click="selectCategory(item.id)"
|
||
>
|
||
<text
|
||
class="category-text"
|
||
:class="{ 'category-text-active': currentCategory == item.id }"
|
||
>{{ item.name }}</text>
|
||
</view>
|
||
</view>
|
||
</scroll-view>
|
||
</view>
|
||
|
||
<!-- 案例列表 -->
|
||
<scroll-view
|
||
class="case-scroll"
|
||
scroll-y
|
||
@scrolltolower="loadMore"
|
||
>
|
||
<view class="case-list">
|
||
<case-card
|
||
v-for="item in caseList"
|
||
:key="item.id"
|
||
:caseData="item"
|
||
@click="goToDetail"
|
||
></case-card>
|
||
</view>
|
||
|
||
<!-- 加载状态 -->
|
||
<view class="load-status">
|
||
<text class="load-text" v-if="loading">加载中...</text>
|
||
<text class="load-text" v-else-if="noMore">没有更多了</text>
|
||
</view>
|
||
|
||
<!-- 空状态 -->
|
||
<view class="empty-state" v-if="!loading && caseList.length == 0">
|
||
<text class="empty-icon">📭</text>
|
||
<text class="empty-text">暂无相关案例</text>
|
||
</view>
|
||
|
||
<!-- 底部间距 -->
|
||
<view class="bottom-space"></view>
|
||
</scroll-view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup lang="uts">
|
||
import { getCaseList } from '@/api/index.uts'
|
||
import { SOFA_CATEGORIES, PAGE_SIZE, getServiceTypeName } from '@/utils/config.uts'
|
||
|
||
// 分类类型
|
||
type CategoryItem = {
|
||
id : string
|
||
name : string
|
||
}
|
||
|
||
// 案例类型
|
||
type CaseItem = {
|
||
id : string
|
||
title : string
|
||
category : string
|
||
categoryName : string
|
||
coverImage : string
|
||
material : string
|
||
duration : string
|
||
price : string
|
||
views : number
|
||
likes : number
|
||
}
|
||
|
||
// 分类列表
|
||
const categories = ref<CategoryItem[]>([])
|
||
|
||
// 当前分类
|
||
const currentCategory = ref('all')
|
||
|
||
// 案例列表
|
||
const caseList = ref<CaseItem[]>([])
|
||
|
||
// 分页
|
||
const page = ref(1)
|
||
const total = ref(0)
|
||
|
||
// 加载状态
|
||
const loading = ref(false)
|
||
const noMore = ref(false)
|
||
|
||
// 初始化分类
|
||
const initCategories = () => {
|
||
categories.value = SOFA_CATEGORIES.map((item) : CategoryItem => {
|
||
return {
|
||
id: item.id,
|
||
name: item.name
|
||
} as CategoryItem
|
||
})
|
||
}
|
||
|
||
// 选择分类
|
||
const selectCategory = (id : string) => {
|
||
if (currentCategory.value == id) return
|
||
currentCategory.value = id
|
||
page.value = 1
|
||
caseList.value = []
|
||
noMore.value = false
|
||
console.log(111)
|
||
fetchCaseList()
|
||
}
|
||
|
||
// 获取案例列表
|
||
const fetchCaseList = async () => {
|
||
if (loading.value || noMore.value) return
|
||
|
||
loading.value = true
|
||
try {
|
||
const params = {
|
||
serviceType: currentCategory.value != 'all' ? currentCategory.value : undefined,
|
||
page: page.value,
|
||
limit: PAGE_SIZE,
|
||
status: 'published'
|
||
} as UTSJSONObject
|
||
|
||
const res = await getCaseList(params)
|
||
if (res.code == 0 && res.data != null) {
|
||
const data = res.data as UTSJSONObject
|
||
// 适应后端返回格式:{ list: [], total: 0, page: 1, pageSize: 10 }
|
||
const list = data['list'] as UTSJSONObject[] || []
|
||
total.value = data['total'] as number || 0
|
||
|
||
const newList = list.map((item) : CaseItem => {
|
||
// 优先使用images,其次afterImages,最后beforeImages
|
||
const images = item['images'] as string[] || []
|
||
const afterImages = item['afterImages'] as string[] || []
|
||
const beforeImages = item['beforeImages'] as string[] || []
|
||
const coverImage = images.length > 0 ? images[0] : (afterImages.length > 0 ? afterImages[0] : (beforeImages.length > 0 ? beforeImages[0] : ''))
|
||
|
||
return {
|
||
id: String(item['id']),
|
||
title: item['title'] as string,
|
||
category: item['serviceType'] as string,
|
||
categoryName: getServiceTypeName(item['serviceType'] as string),
|
||
coverImage: coverImage,
|
||
material: item['materials'] as string || '暂无',
|
||
duration: (item['duration'] as number || 0) + '天',
|
||
price: item['price'] != null ? '¥' + item['price'] : '面议',
|
||
views: item['views'] as number || 0,
|
||
likes: item['likes'] as number || 0
|
||
} as CaseItem
|
||
})
|
||
|
||
if (page.value == 1) {
|
||
caseList.value = newList
|
||
} else {
|
||
caseList.value = [...caseList.value, ...newList]
|
||
}
|
||
|
||
if (caseList.value.length >= total.value) {
|
||
noMore.value = true
|
||
}
|
||
}
|
||
} catch (e) {
|
||
console.error('获取案例列表失败', e)
|
||
uni.showToast({
|
||
title: '加载失败',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
loading.value = false
|
||
}
|
||
|
||
// 加载更多
|
||
const loadMore = () => {
|
||
if (!noMore.value && !loading.value) {
|
||
page.value++
|
||
fetchCaseList()
|
||
}
|
||
}
|
||
|
||
// 跳转详情
|
||
const goToDetail = (id : string) => {
|
||
uni.navigateTo({
|
||
url: `/pages/cases/detail?id=${id}`
|
||
})
|
||
}
|
||
|
||
onLoad(() => {
|
||
initCategories()
|
||
fetchCaseList()
|
||
})
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
.page {
|
||
flex: 1;
|
||
background-color: #f5f5f5;
|
||
}
|
||
|
||
/* 分类栏 */
|
||
.category-bar {
|
||
background-color: #ffffff;
|
||
padding: 24rpx 0;
|
||
border-bottom-width: 1rpx;
|
||
border-bottom-style: solid;
|
||
border-bottom-color: #EBEEF5;
|
||
}
|
||
|
||
.category-scroll {
|
||
flex-direction: row;
|
||
}
|
||
.category-scroll ::-webkit-scrollbar {
|
||
display: none;
|
||
}
|
||
.category-list {
|
||
flex-direction: row;
|
||
padding: 0 24rpx;
|
||
}
|
||
|
||
.category-item {
|
||
padding: 16rpx 32rpx;
|
||
margin-right: 16rpx;
|
||
border-radius: 999rpx;
|
||
background-color: #f5f5f5;
|
||
}
|
||
|
||
.category-active {
|
||
background-color: #D4A574;
|
||
}
|
||
|
||
.category-text {
|
||
font-size: 28rpx;
|
||
color: #606266;
|
||
}
|
||
|
||
.category-text-active {
|
||
color: #ffffff;
|
||
}
|
||
|
||
/* 案例列表 */
|
||
.case-scroll {
|
||
flex: 1;
|
||
}
|
||
|
||
.case-list {
|
||
padding: 24rpx;
|
||
}
|
||
|
||
/* 加载状态 */
|
||
.load-status {
|
||
padding: 32rpx 0;
|
||
align-items: center;
|
||
}
|
||
|
||
.load-text {
|
||
font-size: 26rpx;
|
||
color: #909399;
|
||
}
|
||
|
||
/* 空状态 */
|
||
.empty-state {
|
||
padding: 120rpx 0;
|
||
align-items: center;
|
||
}
|
||
|
||
.empty-icon {
|
||
font-size: 80rpx;
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.empty-text {
|
||
font-size: 28rpx;
|
||
color: #909399;
|
||
}
|
||
|
||
/* 底部间距 */
|
||
.bottom-space {
|
||
height: 120rpx;
|
||
}
|
||
</style>
|