Files
yimo-resource-blog/pages/mine/custom_requests.vue
2026-09-08 08:25:22 +08:00

147 lines
7.3 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>