本文介绍了一个简洁的项目列表页和详情页Vue实现方案。列表页包含顶部导航、状态筛选Tab、项目列表展示、搜索功能以及分页加载逻辑,采用模拟数据进行演示。详情页展示项目基本信息、描述和快捷操作入口,包含加载状态处理。代码特点包括:功能完整但简洁,添加了清晰注释,采用语义化命名和组件化设计,便于扩展和复用。该实现不依赖后端API,适合学习分享,展示了常见的列表-详情交互模式。
列表页 (SimpleList.vue)
<template>
<view class="container">
<!-- 顶部导航 -->
<cu-custom bgColor="bg-blue" :isBack="true">
<block slot="content">项目列表</block>
<block slot="right">
<span @click="showSearch = true">
<u-icon name="search" color="#fff" size="28"></u-icon>
</span>
</block>
</cu-custom>
<!-- 状态筛选Tab -->
<view class="tabs">
<view
v-for="(tab, index) in tabs"
:key="index"
:class="['tab', activeTab === index ? 'active' : '']"
@click="switchTab(index)"
>
{{ tab }}
</view>
</view>
<!-- 项目列表 -->
<scroll-view
scroll-y
class="list"
@scrolltolower="loadMore"
refresher-enabled
:refresher-triggered="refreshing"
@refresherrefresh="refreshData"
>
<view
v-for="(item, i) in listData"
:key="i"
class="item"
@click="goDetail(item.id)"
>
<view class="item-header">
<text class="title">{{ item.name }}</text>
<text class="time">{{ item.startDate }} 至 {{ item.endDate }}</text>
</view>
<view class="item-footer">
<view class="manager">
<u-avatar :src="item.avatar" size="24"></u-avatar>
<text>{{ item.manager }}</text>
</view>
<view class="progress">
<text>进度: {{ item.progress }}%</text>
<view class="progress-bar">
<view class="progress-inner" :style="{width: item.progress + '%'}"></view>
</view>
</view>
</view>
</view>
<u-loadmore :status="loadStatus" />
</scroll-view>
<!-- 搜索面板 -->
<u-popup v-model="showSearch" mode="bottom" border-radius="20">
<view class="search-panel">
<view class="search-title">项目搜索</view>
<u-form :model="searchForm">
<u-form-item label="项目名称">
<u-input v-model="searchForm.name" placeholder="请输入项目名称" />
</u-form-item>
<u-form-item label="负责人">
<u-input v-model="searchForm.manager" placeholder="请输入负责人" />
</u-form-item>
<u-form-item label="状态">
<u-radio-group v-model="searchForm.status">
<u-radio :name="0">全部</u-radio>
<u-radio :name="1">进行中</u-radio>
<u-radio :name="2">已完成</u-radio>
</u-radio-group>
</u-form-item>
</u-form>
<view class="search-buttons">
<u-button type="default" @click="resetSearch">重置</u-button>
<u-button type="primary" @click="doSearch">搜索</u-button>
</view>
</view>
</u-popup>
</view>
</template>
<script>
export default {
data() {
return {
tabs: ['全部', '进行中', '已完成', '已延期'],
activeTab: 0,
listData: [],
page: 1,
loadStatus: 'loadmore',
refreshing: false,
showSearch: false,
searchForm: {
name: '',
manager: '',
status: 0
}
}
},
onLoad() {
this.loadData()
},
methods: {
// 加载数据
loadData() {
// 模拟API请求
setTimeout(() => {
// 模拟数据
const mockData = Array.from({length: 10}).map((_, i) => ({
id: 'proj-' + (this.page * 10 + i),
name: `示例项目${this.page * 10 + i}`,
startDate: '2023-01-01',
endDate: '2023-12-31',
manager: ['张经理', '李主管', '王总监'][i % 3],
avatar: '',
progress: Math.floor(Math.random() * 100),
status: [0, 1, 2][Math.floor(Math.random() * 3)] // 0-待启动 1-进行中 2-已完成
}))
if (this.page === 1) {
this.listData = mockData
} else {
this.listData = [...this.listData, ...mockData]
}
this.loadStatus = this.page >= 3 ? 'nomore' : 'loadmore'
this.refreshing = false
}, 800)
},
// 切换Tab
switchTab(index) {
this.activeTab = index
this.page = 1
this.loadData()
},
// 加载更多
loadMore() {
if (this.loadStatus === 'nomore') return
this.page++
this.loadData()
},
// 下拉刷新
refreshData() {
this.refreshing = true
this.page = 1
this.loadData()
},
// 重置搜索
resetSearch() {
this.searchForm = {
name: '',
manager: '',
status: 0
}
},
// 执行搜索
doSearch() {
this.showSearch = false
this.page = 1
this.loadData()
},
// 跳转详情
goDetail(id) {
uni.navigateTo({
url: '/pages/project/SimpleDetail?id=' + id
})
}
}
}
</script>
<style lang="scss">
.container {
background-color: #f5f5f5;
min-height: 100vh;
}
.tabs {
display: flex;
background: #fff;
padding: 20rpx 0;
.tab {
flex: 1;
text-align: center;
font-size: 28rpx;
color: #666;
&.active {
color: #409EFF;
font-weight: bold;
}
}
}
.list {
height: calc(100vh - 180rpx);
padding: 20rpx;
}
.item {
background: #fff;
border-radius: 12rpx;
padding: 24rpx;
margin-bottom: 20rpx;
.item-header {
margin-bottom: 20rpx;
.title {
font-size: 32rpx;
font-weight: bold;
color: #333;
display: block;
margin-bottom: 10rpx;
}
.time {
font-size: 24rpx;
color: #999;
}
}
.item-footer {
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 15rpx;
border-top: 1px solid #f0f0f0;
.manager {
display: flex;
align-items: center;
font-size: 26rpx;
color: #666;
text {
margin-left: 10rpx;
}
}
.progress {
text-align: right;
font-size: 24rpx;
color: #999;
.progress-bar {
width: 150rpx;
height: 8rpx;
background: #eee;
border-radius: 4rpx;
margin-top: 5rpx;
overflow: hidden;
.progress-inner {
height: 100%;
background: #409EFF;
border-radius: 4rpx;
}
}
}
}
}
.search-panel {
padding: 30rpx;
.search-title {
font-size: 32rpx;
font-weight: bold;
text-align: center;
margin-bottom: 30rpx;
}
.search-buttons {
display: flex;
margin-top: 40rpx;
gap: 20rpx;
button {
flex: 1;
}
}
}
</style>
详情页 (SimpleDetail.vue)
<template>
<view class="container">
<!-- 顶部导航 -->
<cu-custom bgColor="bg-blue" :isBack="true">
<block slot="content">项目详情</block>
</cu-custom>
<!-- 加载状态 -->
<u-loading mode="circle" v-if="loading"></u-loading>
<!-- 内容区域 -->
<view v-else class="content">
<!-- 基本信息 -->
<view class="card">
<view class="title">{{ detail.name }}</view>
<view class="info-row">
<text class="label">时间:</text>
<text class="value">{{ detail.startDate }} 至 {{ detail.endDate }}</text>
</view>
<view class="info-row">
<text class="label">负责人:</text>
<view class="value">
<u-avatar :src="detail.avatar" size="30"></u-avatar>
<text style="margin-left:10rpx">{{ detail.manager }}</text>
</view>
</view>
<view class="info-row">
<text class="label">进度:</text>
<view class="progress">
<text>{{ detail.progress }}%</text>
<view class="progress-bar">
<view class="progress-inner" :style="{width: detail.progress + '%'}"></view>
</view>
</view>
</view>
</view>
<!-- 项目描述 -->
<view class="card">
<view class="section-title">项目描述</view>
<view class="desc">{{ detail.desc || '暂无描述' }}</view>
</view>
<!-- 快捷操作 -->
<view class="card">
<view class="section-title">快捷操作</view>
<view class="actions">
<view class="action-item" @click="goPage('task')">
<u-icon name="list" size="40" color="#409EFF"></u-icon>
<text>查看任务</text>
</view>
<view class="action-item" @click="goPage('file')">
<u-icon name="file-text" size="40" color="#409EFF"></u-icon>
<text>项目文件</text>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
loading: true,
detail: {}
}
},
onLoad(options) {
if (options.id) {
this.loadDetail(options.id)
}
},
methods: {
// 加载详情数据
loadDetail(id) {
this.loading = true
// 模拟API请求
setTimeout(() => {
this.detail = {
id: id,
name: `示例项目 ${id.slice(-3)}`,
startDate: '2023-01-01',
endDate: '2023-12-31',
manager: '张经理',
avatar: '',
progress: 65,
desc: '这是一个示例项目,用于演示详情页的实现。实际项目中这里会显示项目的详细描述信息,包括项目背景、目标、关键节点等内容。'
}
this.loading = false
}, 800)
},
// 跳转页面
goPage(type) {
const urlMap = {
task: '/pages/project/task?id=' + this.detail.id,
file: '/pages/project/file?id=' + this.detail.id
}
if (urlMap[type]) {
uni.navigateTo({
url: urlMap[type]
})
}
}
}
}
</script>
<style lang="scss">
.container {
background-color: #f5f5f5;
min-height: 100vh;
}
.content {
padding: 20rpx;
}
.card {
background: #fff;
border-radius: 12rpx;
padding: 24rpx;
margin-bottom: 20rpx;
.title {
font-size: 36rpx;
font-weight: bold;
margin-bottom: 20rpx;
color: #333;
}
.info-row {
display: flex;
margin-bottom: 15rpx;
font-size: 28rpx;
.label {
color: #666;
width: 120rpx;
}
.value {
flex: 1;
color: #333;
display: flex;
align-items: center;
}
.progress {
flex: 1;
.progress-bar {
width: 100%;
height: 8rpx;
background: #eee;
border-radius: 4rpx;
margin-top: 5rpx;
overflow: hidden;
.progress-inner {
height: 100%;
background: #409EFF;
border-radius: 4rpx;
}
}
}
}
.section-title {
font-size: 30rpx;
font-weight: bold;
margin-bottom: 20rpx;
color: #333;
padding-bottom: 10rpx;
border-bottom: 1px solid #f0f0f0;
}
.desc {
font-size: 28rpx;
color: #333;
line-height: 1.6;
}
.actions {
display: flex;
justify-content: space-around;
padding: 20rpx 0;
.action-item {
display: flex;
flex-direction: column;
align-items: center;
text {
margin-top: 10rpx;
font-size: 26rpx;
color: #666;
}
}
}
}
</style>
特点说明
-
简洁性:
-
列表页仅保留核心功能:Tab切换、搜索、下拉刷新、上拉加载
-
详情页展示基本信息、描述和快捷操作入口
-
-
易读性:
-
添加了清晰的注释说明
-
使用语义化的class命名
-
保持一致的代码风格
-
-
功能性:
-
列表页包含完整的分页逻辑
-
详情页有加载状态处理
-
保留了必要的交互功能
-
-
可扩展性:
-
数据结构清晰,易于添加新字段
-
组件化设计,方便复用
-
-
适合分享:
-
使用模拟数据,不依赖后端API
-
无敏感业务信息
-
完整的功能演示
-