166 lines
8.3 KiB
Vue
166 lines
8.3 KiB
Vue
|
|
<template>
|
|||
|
|
<view class="detail-root">
|
|||
|
|
<view v-if="loading" class="detail-loading"><text class="cuIcon-loading2 loading-icon"></text>正在加载...</view>
|
|||
|
|
|
|||
|
|
<view v-else-if="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="loadDetail">重新加载</button>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<view v-else-if="detail.id" class="detail-page">
|
|||
|
|
<view class="status-card" :class="statusClass">
|
|||
|
|
<view class="status-icon"><text :class="statusIconClass"></text></view>
|
|||
|
|
<view class="status-content">
|
|||
|
|
<view class="status-title">{{ statusText }}</view>
|
|||
|
|
<view class="status-desc">{{ statusDisplayText }}</view>
|
|||
|
|
<view v-if="showStatusTime" class="status-time">更新于 {{ detail.updateTime || detail.createTime }}</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<view class="content-card contact-card">
|
|||
|
|
<view class="contact-main">
|
|||
|
|
<view class="contact-icon"><text class="cuIcon-weixin"></text></view>
|
|||
|
|
<view class="contact-content">
|
|||
|
|
<view class="contact-title">如有疑问,请联系南音</view>
|
|||
|
|
<view class="contact-wechat">微信号:{{ contactWechat }}</view>
|
|||
|
|
</view>
|
|||
|
|
<button class="copy-button" @tap="copyWechat">复制微信号</button>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<view class="content-card">
|
|||
|
|
<view class="card-title"><text class="cuIcon-titles card-title-icon"></text>项目需求</view>
|
|||
|
|
<view class="info-row"><text class="info-label">项目名称</text><text class="info-value project-value">{{ detail.projectName }}</text></view>
|
|||
|
|
<view class="info-row"><text class="info-label">技术栈</text><text class="info-value tech-stack-value">{{ detail.techStackName || '-' }}</text></view>
|
|||
|
|
<view class="info-row requirement-row"><text class="info-label">项目需求</text><text class="info-value requirement-value">{{ detail.requirementContent }}</text></view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<view class="content-card record-card">
|
|||
|
|
<view class="card-title"><text class="cuIcon-titles card-title-icon"></text>提交记录</view>
|
|||
|
|
<view class="info-row"><text class="info-label">需求单号</text><text class="info-value">{{ detail.requestNo }}</text></view>
|
|||
|
|
<view class="info-row"><text class="info-label">提交时间</text><text class="info-value">{{ detail.createTime || '-' }}</text></view>
|
|||
|
|
<view class="info-row">
|
|||
|
|
<text class="info-label">积分</text>
|
|||
|
|
<view class="info-value integral-info">
|
|||
|
|
<text class="point-text">{{ detail.pointAmount }}积分</text>
|
|||
|
|
<text class="info-separator">·</text>
|
|||
|
|
<text :class="pointStatusClass">{{ pointStatusText }}</text>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script>
|
|||
|
|
import { getMyCustomRequest } from '@/api/app/index.js'
|
|||
|
|
import { getCustomRequestStatus } from '@/utils/customRequest.js'
|
|||
|
|
import config from '@/config'
|
|||
|
|
|
|||
|
|
export default {
|
|||
|
|
data() {
|
|||
|
|
return {
|
|||
|
|
id: null,
|
|||
|
|
detail: {},
|
|||
|
|
loading: false,
|
|||
|
|
loadError: false,
|
|||
|
|
contactWechat: config.appInfo.contactWechat
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
computed: {
|
|||
|
|
statusValue() { return Number(this.detail.status) },
|
|||
|
|
statusConfig() { return getCustomRequestStatus(this.statusValue) },
|
|||
|
|
isRejected() { return this.statusValue === 5 },
|
|||
|
|
isRefunded() { return Number(this.detail.pointStatus) === 1 },
|
|||
|
|
statusClass() { return this.statusConfig.className },
|
|||
|
|
statusIconClass() { return this.statusConfig.icon },
|
|||
|
|
statusText() { return this.statusConfig.text },
|
|||
|
|
statusDesc() { return this.statusConfig.description },
|
|||
|
|
statusDisplayText() {
|
|||
|
|
if (this.isRejected) return this.detail.rejectReason ? '未通过原因:' + this.detail.rejectReason : this.statusDesc
|
|||
|
|
return this.detail.progressNote || this.statusDesc
|
|||
|
|
},
|
|||
|
|
showStatusTime() { return !!(this.detail.updateTime || this.detail.createTime) },
|
|||
|
|
pointStatusClass() { return this.isRefunded ? 'refund-text' : '' },
|
|||
|
|
pointStatusText() { return this.isRefunded ? '已退回' : '已扣除' }
|
|||
|
|
},
|
|||
|
|
onLoad(options) { this.id = options.id; this.loadDetail() },
|
|||
|
|
onPullDownRefresh() { this.loadDetail() },
|
|||
|
|
methods: {
|
|||
|
|
copyWechat() {
|
|||
|
|
uni.setClipboardData({
|
|||
|
|
data: this.contactWechat,
|
|||
|
|
success: () => uni.showToast({ title: '微信号已复制', icon: 'success' })
|
|||
|
|
})
|
|||
|
|
},
|
|||
|
|
loadDetail() {
|
|||
|
|
if (!this.id || this.loading) return
|
|||
|
|
this.loading = true
|
|||
|
|
this.loadError = false
|
|||
|
|
getMyCustomRequest(this.id).then(response => {
|
|||
|
|
this.detail = response.data || {}
|
|||
|
|
}).catch(() => {
|
|||
|
|
this.loadError = true
|
|||
|
|
}).finally(() => {
|
|||
|
|
this.loading = false
|
|||
|
|
uni.stopPullDownRefresh()
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style>
|
|||
|
|
page { min-height: 100%; background: #f7f7f7; }
|
|||
|
|
.detail-root { min-height: 100vh; }
|
|||
|
|
.detail-page { padding: 28rpx; }
|
|||
|
|
.status-card, .content-card { border-radius: 20rpx; background: #fff; box-shadow: 0 6rpx 18rpx rgba(50, 50, 50, .04); }
|
|||
|
|
.status-card { display: flex; align-items: center; padding: 30rpx; border-left: 7rpx solid #ff9700; }
|
|||
|
|
.status-reviewing, .status-developing { border-left-color: #0081ff; }
|
|||
|
|
.status-accepted { border-left-color: #1cbbb4; }
|
|||
|
|
.status-completed { border-left-color: #20a05a; }
|
|||
|
|
.status-rejected { border-left-color: #e54d42; }
|
|||
|
|
.status-unknown { border-left-color: #999; }
|
|||
|
|
.status-icon { display: flex; flex-shrink: 0; align-items: center; justify-content: center; width: 76rpx; height: 76rpx; margin-right: 22rpx; border-radius: 50%; font-size: 40rpx; color: #0081ff; background: #edf7ff; }
|
|||
|
|
.status-pending .status-icon { color: #ff9700; background: #fff6e8; }
|
|||
|
|
.status-accepted .status-icon { color: #1cbbb4; background: #eaf9f7; }
|
|||
|
|
.status-completed .status-icon { color: #20a05a; background: #ebf8f1; }
|
|||
|
|
.status-rejected .status-icon { color: #e54d42; background: #fff0ef; }
|
|||
|
|
.status-content { min-width: 0; }
|
|||
|
|
.status-title { font-size: 34rpx; font-weight: 600; color: #222; }
|
|||
|
|
.status-desc { margin-top: 10rpx; font-size: 24rpx; line-height: 1.55; color: #888; }
|
|||
|
|
.status-time { margin-top: 12rpx; font-size: 20rpx; line-height: 1.4; color: #b1b1b1; }
|
|||
|
|
.content-card { margin-top: 24rpx; padding: 28rpx; }
|
|||
|
|
.card-title { display: flex; align-items: center; margin-bottom: 20rpx; font-size: 31rpx; font-weight: 600; color: #222; }
|
|||
|
|
.card-title-icon { margin-right: 4rpx; font-size: 38rpx; color: #0081ff; }
|
|||
|
|
.info-row { display: flex; align-items: flex-start; justify-content: space-between; padding: 17rpx 0; font-size: 25rpx; border-bottom: 1rpx solid #eee; }
|
|||
|
|
.info-row:last-child { border-bottom: 0; }
|
|||
|
|
.info-label { color: #999; }
|
|||
|
|
.info-value { max-width: 470rpx; text-align: right; color: #555; }
|
|||
|
|
.integral-info { display: flex; align-items: center; justify-content: flex-end; }
|
|||
|
|
.info-separator { margin: 0 9rpx; color: #ccc; }
|
|||
|
|
.point-text { color: #ff9700; }
|
|||
|
|
.refund-text { color: #20a05a; }
|
|||
|
|
.project-value { font-weight: 500; color: #333; }
|
|||
|
|
.tech-stack-value { color: #3976a8; }
|
|||
|
|
.requirement-row { min-height: 70rpx; }
|
|||
|
|
.requirement-value { line-height: 1.65; color: #666; white-space: pre-wrap; word-break: break-all; }
|
|||
|
|
.contact-card { border: 1rpx solid #d8f1e1; background: linear-gradient(135deg, #f4fff7, #fff); }
|
|||
|
|
.contact-main { display: flex; align-items: center; }
|
|||
|
|
.contact-icon { display: flex; flex-shrink: 0; align-items: center; justify-content: center; width: 72rpx; height: 72rpx; margin-right: 18rpx; border-radius: 50%; font-size: 40rpx; color: #fff; background: #20b85a; }
|
|||
|
|
.contact-content { flex: 1; min-width: 0; }
|
|||
|
|
.contact-title { font-size: 28rpx; font-weight: 600; color: #27352c; }
|
|||
|
|
.contact-wechat { margin-top: 7rpx; font-size: 23rpx; color: #7b887f; }
|
|||
|
|
.copy-button { flex-shrink: 0; height: 62rpx; margin: 0 0 0 16rpx; padding: 0 20rpx; border: 0; border-radius: 10rpx; font-size: 23rpx; line-height: 62rpx; color: #fff; background: #20b85a; }
|
|||
|
|
.copy-button::after { border: 0; }
|
|||
|
|
.detail-loading, .error-state { padding: 220rpx 60rpx 0; text-align: center; font-size: 25rpx; color: #999; }
|
|||
|
|
.loading-icon { display: inline-block; margin-right: 10rpx; }
|
|||
|
|
.error-icon { font-size: 90rpx; color: #c8cfdb; }
|
|||
|
|
.error-title { margin-top: 20rpx; font-size: 31rpx; color: #666; }
|
|||
|
|
.error-desc { margin-top: 12rpx; font-size: 25rpx; color: #aaa; }
|
|||
|
|
.retry-button { width: 280rpx; height: 76rpx; margin-top: 30rpx; border-radius: 12rpx; font-size: 27rpx; line-height: 76rpx; color: #fff; background: #0081ff; }
|
|||
|
|
.retry-button::after { border: 0; }
|
|||
|
|
</style>
|