purchase-detail.vue 9.26 KB
<template>
	<view class="container">
		<!-- 详情卡片 -->
		<view class="detail-card">
			<view class="card-header">物品购买详情</view>
			<view class="detail-content">
				<!-- 加载状态 -->
				<view v-if="loading" class="loading">正在加载详情...</view>
				
				<!-- 错误状态 -->
				<view v-else-if="error" class="error-state">
					<view>{{ error }}</view>
					<u-button class="retry-btn" @click="retryLoad" type="primary" size="small">重试</u-button>
				</view>
				
				<!-- 详情内容 -->
				<view v-else-if="detailData" class="info-sections">
					<!-- 基本信息 -->
					<view class="info-section">
						<view class="section-title">基本信息</view>
						<view class="info-grid">
							<view class="info-item">
								<text class="info-label">分类</text>
								<text class="info-value">{{ detailData.reimbursementCategoryName || '无' }}</text>
							</view>
							<view class="info-item">
								<text class="info-label">单价</text>
								<text class="info-value amount">¥{{ detailData.unitPrice || 0 }}</text>
							</view>
							<view class="info-item">
								<text class="info-label">数量</text>
								<text class="info-value">{{ detailData.quantity || 0 }}</text>
							</view>
							<view class="info-item">
								<text class="info-label">总金额</text>
								<text class="info-value amount">¥{{ detailData.amount || 0 }}</text>
							</view>
							<view class="info-item">
								<text class="info-label">购买时间</text>
								<text class="info-value">{{ utils.formatTime(detailData.purchaseTime) }}</text>
							</view>
							<view class="info-item">
								<text class="info-label">审批状态</text>
								<!-- {{ detailData.approveStatus || '未审批' }} -->
								<view class="status-badge" :class="detailData.approveStatus=='已审批'?'approved':detailData.approveStatus=='待审批'?'pending':detailData.approveStatus=='未通过'?'rejected':'unapproved'">
									{{ detailData.approveStatus?detailData.approveStatus=='未审批'?'未提交':detailData.approveStatus :'未提交' }}
								</view>
							</view>
						</view>
					</view>
					
					<!-- 备注信息 -->
					<view class="info-section" v-if="detailData.memo">
						<view class="section-title">备注说明</view>
						<view class="memo-content">
							<text class="memo-text">{{ detailData.memo }}</text>
						</view>
					</view>
					
					<!-- 附件信息 -->
					<view class="info-section" v-if="detailData.attachment && detailData.attachment.length > 0">
						<view class="section-title">附件</view>
						<view class="attachment-list">
							<view v-for="(file, index) in detailData.attachment" :key="index" class="attachment-item">
								<view class="file-info" @click="previewFile(file)">
									<text class="file-icon">📎</text>
									<text class="file-name">{{ file.name || file.url || '文件' }}</text>
								</view>
							</view>
						</view>
					</view>
					
					<!-- 创建信息 -->
					<view class="info-section">
						<view class="section-title">创建信息</view>
						<view class="info-grid">
							<view class="info-item">
								<text class="info-label">创建人</text>
								<text class="info-value">{{ detailData.createUserName || detailData.createUser || '无' }}</text>
							</view>
							<view class="info-item">
								<text class="info-label">门店</text>
								<text class="info-value">{{ detailData.createUserStoreName || '无' }}</text>
							</view>
							<view class="info-item">
								<text class="info-label">创建时间</text>
								<text class="info-value">{{ utils.formatTime(detailData.createTime) }}</text>
							</view>
						</view>
					</view>
					
					<!-- 审批信息 -->
					<view class="info-section" v-if="detailData.approveStatus && detailData.approveStatus !== '未审批'">
						<view class="section-title">审批信息</view>
						<view class="info-grid">
							<view class="info-item" v-if="detailData.approveUser">
								<text class="info-label">审批人</text>
								<text class="info-value">{{ detailData.approveUserName || detailData.approveUser || '无' }}</text>
							</view>
							<view class="info-item" v-if="detailData.approveTime">
								<text class="info-label">审批时间</text>
								<text class="info-value">{{ utils.formatTime(detailData.approveTime) }}</text>
							</view>
							<view class="info-item" v-if="detailData.applicationId">
								<text class="info-label">审批单编号</text>
								<text class="info-value">{{ detailData.applicationId }}</text>
							</view>
						</view>
					</view>
				</view>
			</view>
		</view>
	</view>
</template>

<script>
	import purchaseApi from '@/apis/modules/purchase.js'
	import config from '@/common/config.js'

	export default {
		data() {
			return {
				loading: false,
				error: null,
				detailData: null,
				purchaseId: null
			}
		},

		onLoad(options) {
			if (options.id) {
				this.purchaseId = options.id
				this.loadDetail()
			} else {
				this.error = '缺少记录ID'
			}
		},

		methods: {
			// 加载详情数据
			async loadDetail() {
				if (!this.purchaseId) return

				try {
					this.loading = true
					this.error = null

					const res = await purchaseApi.getPurchaseDetail(this.purchaseId)

					if (res.code === 200 && res.data) {
						this.detailData = res.data
					} else {
						this.error = res.message || '加载详情失败'
					}
				} catch (error) {
					console.error('加载详情失败:', error)
					this.error = '加载详情失败,请重试'
				} finally {
					this.loading = false
				}
			},

			// 重试加载
			retryLoad() {
				this.loadDetail()
			},

			// 获取状态样式类
			getStatusClass(status) {
				switch (status) {
					case '已审批':
						return 'approved'
					case '待审批':
						return 'pending'
					case '未通过':
						return 'rejected'
					case '未审批':
					default:
						return 'unapproved'
				}
			},

			// 预览文件
			previewFile(file) {
				if (file.url) {
					const baseUrl = config.getImgBaseUrl()
					const fullUrl = file.url.startsWith('http') ? file.url : `${baseUrl}${file.url}`
					
					// 如果是图片,使用预览图片功能
					if (file.url.match(/\.(jpg|jpeg|png|gif|bmp|webp)$/i)) {
						uni.previewImage({
							urls: [fullUrl],
							current: fullUrl
						})
					} else {
						// 其他文件类型,尝试打开
						uni.showToast({
							title: '暂不支持预览此文件类型',
							icon: 'none'
						})
					}
				}
			}
		}
	}
</script>

<style lang="scss" scoped>
	.container {
		min-height: 100vh;
		background: linear-gradient(135deg, #e8f5e9 0%, #b2dfdb 100%);
		padding: 40rpx;
		box-sizing: border-box;
	}

	.detail-card {
		background: #fff;
		border-radius: 32rpx;
		box-shadow: 0 8rpx 32rpx rgba(76, 175, 80, 0.1);
		overflow: hidden;
	}

	.card-header {
		background: linear-gradient(120deg, #43e97b 0%, #38f9d7 100%);
		padding: 32rpx 40rpx;
		color: #fff;
		font-size: 36rpx;
		font-weight: 600;
		letter-spacing: 2rpx;
		text-align: center;
	}

	.detail-content {
		padding: 40rpx;
	}

	.loading {
		text-align: center;
		padding: 80rpx 40rpx;
		color: #6a9c6a;
		font-size: 28rpx;
	}

	.error-state {
		text-align: center;
		padding: 80rpx 40rpx;
		color: #f56c6c;
		font-size: 28rpx;
	}

	.retry-btn {
		margin-top: 32rpx;
	}

	.info-sections {
		display: flex;
		flex-direction: column;
		gap: 32rpx;
	}

	.info-section {
		background: #f9fff9;
		border-radius: 24rpx;
		padding: 32rpx;
		border: 2rpx solid #e8f5e9;
	}

	.section-title {
		font-size: 32rpx;
		font-weight: 600;
		color: #2e7d32;
		margin-bottom: 24rpx;
		padding-bottom: 16rpx;
		border-bottom: 2rpx solid #c8e6c9;
	}

	.info-grid {
		display: grid;
		grid-template-columns: 1fr 1fr;
		gap: 24rpx;
	}

	.info-item {
		display: flex;
		flex-direction: column;
		gap: 8rpx;
	}

	.info-label {
		font-size: 24rpx;
		color: #6a9c6a;
		font-weight: 500;
	}

	.info-value {
		font-size: 28rpx;
		color: #2e7d32;
		font-weight: 500;
		word-break: break-all;
	}

	.info-value.amount {
		color: #f57c00;
		font-weight: 600;
		font-size: 32rpx;
	}

	.status-badge {
		display: inline-block;
		padding: 8rpx 24rpx;
		border-radius: 40rpx;
		font-size: 24rpx;
		font-weight: 500;
		text-align: center;
		width: fit-content;
	}

	.status-badge.unapproved {
		background: #fff3e0;
		color: #ef6c00;
		border: 2rpx solid #ffe0b2;
	}

	.status-badge.pending {
		background: #e3f2fd;
		color: #1976d2;
		border: 2rpx solid #bbdefb;
	}

	.status-badge.approved {
		background: #e8f5e9;
		color: #2e7d32;
		border: 2rpx solid #c8e6c9;
	}

	.status-badge.rejected {
		background: #ffebee;
		color: #c62828;
		border: 2rpx solid #ffcdd2;
	}

	.memo-content {
		background: #fff;
		border-radius: 16rpx;
		padding: 24rpx;
		border: 2rpx solid #e0e0e0;
	}

	.memo-text {
		font-size: 28rpx;
		color: #555;
		line-height: 1.6;
		word-break: break-all;
	}

	.attachment-list {
		display: flex;
		flex-direction: column;
		gap: 16rpx;
	}

	.attachment-item {
		background: #fff;
		border-radius: 16rpx;
		padding: 24rpx;
		border: 2rpx solid #e0e0e0;
		transition: all 0.2s ease;
	}

	.attachment-item:active {
		background: #f5f5f5;
		transform: translateX(4rpx);
	}

	.file-info {
		display: flex;
		align-items: center;
		gap: 16rpx;
	}

	.file-icon {
		font-size: 32rpx;
	}

	.file-name {
		font-size: 28rpx;
		color: #2e7d32;
		flex: 1;
		word-break: break-all;
	}
</style>