147 lines
7.3 KiB
Vue
147 lines
7.3 KiB
Vue
<template>
|
||
<view class="request-page">
|
||
<view v-if="dataList.length" class="request-list">
|
||
<view class="section-title"><text class="cuIcon-titles section-icon"></text>定制记录</view>
|
||
<view v-for="item in dataList" :key="item.id" class="request-card" @tap="goDetail(item.id)">
|
||
<view class="request-head">
|
||
<text class="request-no">需求单号:{{ item.requestNo }}</text>
|
||
<text class="request-status" :class="item.statusClass">{{ item.statusText }}</text>
|
||
</view>
|
||
<view class="project-line">
|
||
<view class="project-name">{{ item.projectName }}</view>
|
||
<view v-if="item.techStackName" class="tech-stack-tag"><text class="cuIcon-settingsfill tech-stack-icon"></text><text class="tech-stack-name">{{ item.techStackName }}</text></view>
|
||
</view>
|
||
<view class="requirement-text">{{ item.requirementContent }}</view>
|
||
<view v-if="item.progressNote && !item.isRejected" class="progress-note">
|
||
<text class="cuIcon-timefill note-icon"></text><text class="note-content">{{ item.progressNote }}</text>
|
||
</view>
|
||
<view v-if="item.isRejected && item.rejectReason" class="reject-note">
|
||
<text class="cuIcon-infofill note-icon"></text><text class="note-content">未通过原因:{{ item.rejectReason }}</text>
|
||
</view>
|
||
<view class="request-footer">
|
||
<text>{{ item.updateTime || item.createTime }}</text>
|
||
<text class="detail-link">查看详情 <text class="cuIcon-right"></text></text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view v-else-if="!loading && !loadError" class="empty-state">
|
||
<image class="empty-image" src="https://img.yidaima.cn/feast/nodata.svg" mode="aspectFit"></image>
|
||
<view class="empty-title">还没有定制需求</view>
|
||
<view class="empty-desc">提交项目想法后,可以在这里查看审核和开发进度。</view>
|
||
<button class="empty-button" @tap="goSubmit">去提交需求</button>
|
||
</view>
|
||
|
||
<view v-if="!loading && loadError" class="error-state">
|
||
<text class="cuIcon-warn error-icon"></text>
|
||
<view class="error-title">加载失败</view>
|
||
<view class="error-desc">请检查网络后重新加载</view>
|
||
<button class="retry-button" @tap="retryLoad">重新加载</button>
|
||
</view>
|
||
|
||
<view v-if="loading" class="loading-text"><text class="cuIcon-loading2 loading-icon"></text>正在加载...</view>
|
||
<view v-if="dataList.length && !hasMore" class="bottom-text">没有更多了</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import { listMyCustomRequests } from '@/api/app/index.js'
|
||
import { getCustomRequestStatus } from '@/utils/customRequest.js'
|
||
|
||
export default {
|
||
data() {
|
||
return {
|
||
loading: false,
|
||
loadError: false,
|
||
dataList: [],
|
||
total: 0,
|
||
queryParams: { pageNum: 1, pageSize: 10 }
|
||
}
|
||
},
|
||
computed: {
|
||
hasMore() { return this.dataList.length < this.total }
|
||
},
|
||
onLoad() { this.loadList(true) },
|
||
onShow() { if (this.dataList.length) this.loadList(true) },
|
||
onPullDownRefresh() { this.loadList(true) },
|
||
onReachBottom() {
|
||
if (!this.loading && this.hasMore) {
|
||
this.queryParams.pageNum++
|
||
this.loadList(false)
|
||
}
|
||
},
|
||
methods: {
|
||
decorateItem(item) {
|
||
const status = Number(item.status)
|
||
const statusConfig = getCustomRequestStatus(status)
|
||
return Object.assign({}, item, {
|
||
statusClass: statusConfig.className,
|
||
statusText: statusConfig.text,
|
||
isRejected: status === 5
|
||
})
|
||
},
|
||
loadList(reset) {
|
||
if (this.loading) return
|
||
if (reset) this.queryParams.pageNum = 1
|
||
this.loading = true
|
||
this.loadError = false
|
||
listMyCustomRequests(this.queryParams).then(response => {
|
||
const rows = (response.rows || []).map(this.decorateItem)
|
||
this.dataList = reset ? rows : this.dataList.concat(rows)
|
||
this.total = Number(response.total || 0)
|
||
}).catch(() => {
|
||
this.loadError = true
|
||
}).finally(() => {
|
||
this.loading = false
|
||
uni.stopPullDownRefresh()
|
||
})
|
||
},
|
||
retryLoad() { this.loadList(true) },
|
||
goDetail(id) { this.$tab.navigateTo('/pages/mine/custom_request_detail?id=' + id) },
|
||
goSubmit() { uni.switchTab({ url: '/pages/tool/index' }) }
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style>
|
||
page { min-height: 100%; background: #f7f7f7; }
|
||
.request-page { min-height: 100vh; }
|
||
.request-list { padding: 28rpx; }
|
||
.section-title { margin: 4rpx 2rpx 24rpx; font-size: 35rpx; font-weight: 600; color: #222; }
|
||
.section-icon { margin-right: 4rpx; font-size: 42rpx; color: #0081ff; }
|
||
.request-card { margin-bottom: 24rpx; padding: 28rpx; border-radius: 20rpx; background: #fff; box-shadow: 0 6rpx 18rpx rgba(50, 50, 50, .04); }
|
||
.request-head, .request-footer { display: flex; align-items: center; justify-content: space-between; }
|
||
.request-head { padding-bottom: 20rpx; border-bottom: 1rpx solid #eee; }
|
||
.request-no { max-width: 500rpx; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font-size: 23rpx; color: #999; }
|
||
.request-status { flex-shrink: 0; margin-left: 14rpx; padding: 6rpx 12rpx; border-radius: 999rpx; font-size: 22rpx; }
|
||
.status-pending { color: #e98500; background: #fff6e8; }
|
||
.status-reviewing, .status-developing { color: #0081ff; background: #edf7ff; }
|
||
.status-accepted { color: #149d96; background: #eaf9f7; }
|
||
.status-completed { color: #178c4d; background: #ebf8f1; }
|
||
.status-rejected { color: #d8473d; background: #fff0ef; }
|
||
.status-unknown { color: #777; background: #f2f2f2; }
|
||
.project-line { display: flex; align-items: center; min-width: 0; margin-top: 22rpx; }
|
||
.project-name { flex: 1; min-width: 0; overflow: hidden; font-size: 32rpx; font-weight: 600; line-height: 1.5; color: #242424; white-space: nowrap; text-overflow: ellipsis; }
|
||
.tech-stack-tag { display: flex; flex-shrink: 0; align-items: center; max-width: 250rpx; margin-left: 14rpx; padding: 6rpx 12rpx; border-radius: 8rpx; font-size: 21rpx; color: #3976a8; background: #edf7ff; }
|
||
.tech-stack-icon { flex-shrink: 0; margin-right: 7rpx; }
|
||
.tech-stack-name { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
|
||
.requirement-text { margin-top: 14rpx; overflow: hidden; font-size: 26rpx; line-height: 1.65; color: #666; white-space: nowrap; text-overflow: ellipsis; }
|
||
.progress-note, .reject-note { display: flex; align-items: flex-start; margin-top: 20rpx; padding: 16rpx 18rpx; border-radius: 12rpx; font-size: 24rpx; line-height: 1.55; }
|
||
.progress-note { color: #3976a8; background: #edf7ff; }
|
||
.reject-note { color: #b54b43; background: #fff1f0; }
|
||
.note-icon { flex-shrink: 0; margin: 2rpx 10rpx 0 0; }
|
||
.note-content { flex: 1; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
|
||
.request-footer { margin-top: 22rpx; padding-top: 20rpx; border-top: 1rpx solid #eee; font-size: 22rpx; color: #aaa; }
|
||
.detail-link { color: #0081ff; }
|
||
.empty-state, .error-state { padding: 170rpx 60rpx 0; text-align: center; }
|
||
.empty-image { width: 400rpx; height: 200rpx; }
|
||
.empty-title, .error-title { margin-top: 18rpx; font-size: 31rpx; color: #666; }
|
||
.empty-desc, .error-desc { margin-top: 12rpx; font-size: 25rpx; line-height: 1.6; color: #aaa; }
|
||
.empty-button, .retry-button { width: 280rpx; height: 76rpx; margin-top: 30rpx; border-radius: 12rpx; font-size: 27rpx; line-height: 76rpx; color: #fff; background: #0081ff; }
|
||
.empty-button::after { border: 0; }
|
||
.retry-button::after { border: 0; }
|
||
.error-icon { font-size: 90rpx; color: #c8cfdb; }
|
||
.loading-icon { display: inline-block; margin-right: 10rpx; }
|
||
.loading-text, .bottom-text { padding: 40rpx 0 60rpx; text-align: center; font-size: 24rpx; color: #aaa; }
|
||
</style>
|