From e56792f78e4df0df2f12552d1a61dd8ca1db5c67 Mon Sep 17 00:00:00 2001
From: MrShi <1878285526@qq.com>
Date: 星期一, 27 四月 2026 22:26:28 +0800
Subject: [PATCH] app

---
 app/pages/order/order.vue |  932 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 926 insertions(+), 6 deletions(-)

diff --git a/app/pages/order/order.vue b/app/pages/order/order.vue
index 94d9b9c..faa455c 100644
--- a/app/pages/order/order.vue
+++ b/app/pages/order/order.vue
@@ -1,6 +1,146 @@
 <template>
-	<view>
-		
+	<view class="order-page">
+		<view class="order-page__nav" :style="{ paddingTop: statusBarHeight + 'px' }">
+			<view class="order-page__nav-inner">
+				<text class="order-page__nav-title">鎴戠殑璁㈠崟</text>
+			</view>
+		</view>
+
+		<view class="order-page__tabs" :style="{ top: navHeight + 'px' }">
+			<view v-for="tab in displayTabs" :key="tab.value" class="order-page__tab" :class="{ 'order-page__tab--active': activeTab === tab.value }" @click="activeTab = tab.value">
+				<text class="order-page__tab-text">{{ tab.label }}</text>
+				<view v-if="activeTab === tab.value" class="order-page__tab-line"></view>
+			</view>
+		</view>
+
+		<scroll-view class="order-page__body" scroll-y :style="bodyStyle">
+			<view class="order-page__list">
+				<view v-for="(item, index) in orders" :key="item.id" class="order-card" @click="goToOrderDetail(item, index)">
+					<view class="order-card__head">
+					<view class="order-card__head-left">
+						<image class="order-card__badge-icon" :src="getBadgeIcon(item)" mode="widthFix"></image>
+						<text class="order-card__time-text">涓嬪崟鏃堕棿锛歿{ item.createTime }}</text>
+					</view>
+						<text class="order-card__status" :class="{ 'order-card__status--highlight': item.status === 3 || item.status === 4 }">{{ getStatusText(item.status) }}</text>
+					</view>
+
+					<view class="order-card__route-item">
+						<view class="order-card__point order-card__point--pickup">鍙�</view>
+						<view class="order-card__route-texts">
+							<text class="order-card__route-title">{{ item.takeName }}</text>
+							<text class="order-card__route-desc">{{ item.takeAddress }}</text>
+						</view>
+					</view>
+
+					<view class="order-card__route-item order-card__route-item--delivery">
+						<view class="order-card__point order-card__point--delivery">閫�</view>
+						<view class="order-card__route-texts">
+							<text class="order-card__route-title">{{ item.depositShopName }}</text>
+							<text class="order-card__route-desc">{{ item.depositShopAddress }}</text>
+						</view>
+					</view>
+
+					<view class="order-card__footer">
+						<view class="order-card__arrival">
+							<image class="order-card__clock" src="/static/image/ic_clock@2x.png" mode="aspectFit"></image>
+							<text class="order-card__arrival-text">鍓╀綑{{ item.remainMinutes }}鍒嗛挓</text>
+						</view>
+						<view class="order-card__price-wrap">
+							<text class="order-card__price">楼{{ (item.driverFee / 100).toFixed(2) }}</text>
+						</view>
+					</view>
+
+					<view v-if="getActions(item).length" class="order-card__actions">
+						<view></view>
+						<view style="display: flex;flex-wrap: wrap;gap: 20rpx;">
+							<button
+								v-for="action in getActions(item)"
+								:key="action.text"
+								class="order-card__action-btn"
+								:class="['order-card__action-btn--' + action.type, { 'order-card__action-btn--primary-fill': action.fill }]"
+								hover-class="order-card__action-btn--hover"
+								@click.stop="handleAction(item, action)"
+							>
+								{{ action.text }}
+							</button>
+						</view>
+						
+					</view>
+				</view>
+
+				<view v-if="orders.length === 0 && !loading" class="order-page__empty">
+					<image class="order-page__empty-icon" src="/static/image/default_nodata@2x.png" mode="aspectFit"></image>
+				</view>
+
+				<view v-if="loading" class="order-page__loading">
+					<text>鍔犺浇涓�...</text>
+				</view>
+			</view>
+		</scroll-view>
+
+		<u-modal
+			:show="showCancelModal"
+			showCancelButton
+			@cancel="showCancelModal = false"
+			cancelColor="#666666"
+			confirmColor="#0055FF"
+			title="鍙栨秷璁㈠崟纭"
+			@confirm="confirmCancelOrder">
+			<view style="text-align: center;color: #333333;font-size: 28rpx;font-weight: 400;">
+				鎮ㄤ粖鏃ヨ繕鍙彇娑� {{ cancelRemain }} 娆¤鍗曪紝娆℃暟鐢ㄥ敖鍚庝粖鏃ュ皢鏃犳硶鎺ュ崟锛屾槸鍚︾‘璁ゅ彇娑堬紵
+			</view>
+		</u-modal>
+
+		<u-popup :show="showQRPopup" round="20" mode="bottom">
+			<view class="qrcode">
+				<view class="qrcode-title">
+					<image src="/static/image/ic_close@2x.png" mode="widthFix" style="opacity: 0;"></image>
+					<text>{{ selectedOrder && selectedOrder.status === 4 ? '瀛樹欢鐮�' : '鍙栬揣鐮�' }}</text>
+					<image src="/static/image/ic_close@2x.png" mode="widthFix" @click="showQRPopup = false"></image>
+				</view>
+				<view class="qrcode-image">
+					<image v-if="selectedOrder && selectedOrder.driverVerifyCode" :src="'https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=' + selectedOrder.driverVerifyCode" mode="widthFix"></image>
+				</view>
+				<view class="qrcode-btn" hover-class="qrcode-btn--hover" @click="showQRPopup = false">鍏抽棴</view>
+			</view>
+		</u-popup>
+
+		<u-popup :show="showPhotoPopup" round="20" mode="bottom">
+			<view class="photo-popup">
+				<view class="photo-popup__header">
+					<view class="photo-popup__placeholder"></view>
+					<text class="photo-popup__title">{{ photoPopupTitle }}</text>
+					<image class="photo-popup__close" src="/static/image/ic_close2@2x.png" mode="aspectFit" @click="closePhotoPopup"></image>
+				</view>
+
+				<view class="photo-popup__section">
+					<view class="photo-popup__label-row">
+						<text class="photo-popup__label">{{ photoPopupLabel }}</text>
+						<text class="photo-popup__required">*</text>
+						<text class="photo-popup__hint">鏈�澶�3寮犵収鐗�</text>
+					</view>
+
+					<view class="photo-popup__photos">
+						<view v-for="(photo, index) in uploadedPhotos" :key="index" class="photo-popup__preview-card">
+							<image class="photo-popup__preview-image" :src="photo" mode="aspectFill"></image>
+							<view class="photo-popup__preview-mask" @click="deletePhoto(index)">
+								<text class="photo-popup__preview-delete">鍒犻櫎</text>
+							</view>
+						</view>
+						<view v-if="uploadedPhotos.length < 3" class="photo-popup__upload-btn" @click="choosePhoto">
+							<image src="/static/image/btn_upload2@2x.png" mode="aspectFit"></image>
+						</view>
+					</view>
+				</view>
+
+				<view class="photo-popup__section photo-popup__section--remark">
+					<text class="photo-popup__remark-title">澶囨敞淇℃伅</text>
+					<textarea v-model="photoRemark" class="photo-popup__textarea" maxlength="200" placeholder="璇疯緭鍏�" placeholder-style="color: #c7cbd3;" />
+				</view>
+
+				<button class="photo-popup__submit" hover-class="photo-popup__submit--hover" @click="submitPhotoPopup">{{ photoPopupSubmitText }}</button>
+			</view>
+		</u-popup>
 	</view>
 </template>
 
@@ -8,12 +148,792 @@
 	export default {
 		data() {
 			return {
-				
-			};
+				statusBarHeight: 0,
+				navHeight: 0,
+				activeTab: null,
+				tabs: [
+					{ label: '鍏ㄩ儴', value: null },
+					{ label: '寰呭彇璐�', value: 3 },
+					{ label: '閰嶉�佷腑', value: 4 },
+					{ label: '宸插畬鎴�', value: 7 }
+				],
+				orders: [],
+				page: 1,
+				hasMore: true,
+				loading: false,
+				showCancelModal: false,
+				showQRPopup: false,
+				showPhotoPopup: false,
+				selectedOrder: null,
+				cancelRemain: 0,
+				photoPopupMode: '',
+				photoRemark: '',
+				uploadedPhotos: [],
+				activeOrderCount: null
+			}
+		},
+		computed: {
+			displayTabs() {
+				return this.tabs.map((tab) => {
+					let count = null
+					if (tab.value === 3) {
+						count = this.activeOrderCount?.grabbedCount
+					} else if (tab.value === 4) {
+						count = this.activeOrderCount?.deliveringCount
+					}
+					if (count) {
+						return {
+							...tab,
+							label: `${tab.label} ${count}`
+						}
+					}
+					return tab
+				})
+			},
+
+			bodyStyle() {
+				return {
+					marginTop: this.navHeight + uni.upx2px(88) + 'px',
+					height: `calc(100vh - ${this.navHeight + uni.upx2px(88)}px)`
+				}
+			},
+			photoPopupTitle() {
+				return this.photoPopupMode === 'deliver' ? '鎷嶇収閫佽揪' : '鎷嶇収鍙栬揣'
+			},
+			photoPopupLabel() {
+				return this.photoPopupMode === 'deliver' ? '鎷嶆憚閫佽揪鐓х墖' : '鎷嶆憚鍙栬揣鐓х墖'
+			},
+			photoPopupSubmitText() {
+				return this.photoPopupMode === 'deliver' ? '纭閫佽揪' : '纭鍙栬揣'
+			}
+		},
+		onLoad() {
+			const systemInfo = uni.getSystemInfoSync()
+			this.statusBarHeight = systemInfo.statusBarHeight || 0
+			this.navHeight = this.statusBarHeight + uni.upx2px(88)
+			this.getOrderList()
+		},
+		onShow() {
+			this.getActiveOrderCount()
+		},
+		onReachBottom() {
+			if (!this.hasMore || this.loading) return
+			this.page++
+			this.getOrderList()
+		},
+		watch: {
+			activeTab() {
+				this.page = 1
+				this.hasMore = true
+				this.orders = []
+				this.getOrderList()
+			}
+		},
+		methods: {
+			getActiveOrderCount() {
+				this.$u.api.activeOrderCount().then(res => {
+					if (res.code === 200) {
+						this.activeOrderCount = res.data
+					} else {
+						this.activeOrderCount = null
+					}
+				}).catch(() => {
+					this.activeOrderCount = null
+				})
+			},
+			getBadgeIcon(item) {
+				return item.isUrgent ? '/static/image/ic_jisuda@2x.png' : '/static/image/ic_biaosuda@2x.png'
+			},
+			getStatusText(status) {
+				const map = {
+					2: '寰呮帴鍗�',
+					3: '寰呭彇璐�',
+					4: '閰嶉�佷腑',
+					5: '宸插畬鎴�',
+					7: '宸插畬鎴�',
+					99: '宸插彇娑�'
+				}
+				return map[status] || ''
+			},
+			getActions(item) {
+				if (item.status === 3) {
+					return [
+						{ text: '鍙栨秷璁㈠崟', type: 'light', fill: false },
+						{ text: '鍙栬揣鐮�', type: 'primary', fill: false },
+						{ text: '鎷嶇収鍙栬揣', type: 'primary', fill: true }
+					]
+				}
+				if (item.status === 4 && item.takeShopId) {
+					return [
+						{ text: '瀛樹欢鐮�', type: 'primary', fill: false }
+					]
+				}
+				if (item.status === 4 && !item.takeShopId) {
+					return [
+						{ text: '鎷嶇収閫佽揪', type: 'primary', fill: true }
+					]
+				}
+				return []
+			},
+			getOrderList() {
+				if (this.loading) return
+				this.loading = true
+				this.$u.api.orderPage({
+					capacity: 10,
+					page: this.page,
+					model: {
+						status: this.activeTab
+					}
+				}).then(res => {
+					if (res.code === 200) {
+						const list = res.data.records || []
+						if (this.page === 1) {
+							this.orders = list
+						} else {
+							this.orders = [...this.orders, ...list]
+						}
+						this.hasMore = list.length >= 10
+					}
+				}).finally(() => {
+					this.loading = false
+				})
+			},
+			goToOrderDetail(item, index) {
+				uni.navigateTo({
+					url: `/pages/order-detail/order-detail?id=${item.id}`
+				})
+			},
+			handleAction(item, action) {
+				const text = action.text
+				if (text === '鍙栨秷璁㈠崟') {
+					this.handleCancelOrder(item)
+				} else if (text === '鍙栬揣鐮�' || text === '瀛樹欢鐮�') {
+					this.handleShowQRCode(item)
+				} else if (text === '鎷嶇収鍙栬揣') {
+					this.handlePhotoPickup(item)
+				} else if (text === '鎷嶇収閫佽揪') {
+					this.handlePhotoDeliver(item)
+				}
+			},
+			handleCancelOrder(item) {
+				this.selectedOrder = item
+				this.$u.api.cancelLimit().then(res => {
+					if (res.code === 200) {
+						this.cancelRemain = res.data.remain
+					}
+				}).finally(() => {
+					this.showCancelModal = true
+				})
+			},
+			confirmCancelOrder() {
+				if (!this.selectedOrder) return
+				this.$u.api.cancelOrder({ orderId: this.selectedOrder.id }).then(res => {
+					this.showCancelModal = false
+					if (res.code === 200) {
+						uni.showToast({ title: '鍙栨秷鎴愬姛', icon: 'success' })
+						this.getOrderList()
+						this.getActiveOrderCount()
+					} else {
+						uni.showToast({ title: res.message || '鍙栨秷澶辫触', icon: 'none' })
+					}
+				}).catch(() => {
+					this.showCancelModal = false
+				})
+			},
+			handleShowQRCode(item) {
+				this.selectedOrder = item
+				this.showQRPopup = true
+			},
+			handlePhotoPickup(item) {
+				this.selectedOrder = item
+				this.photoPopupMode = 'pickup'
+				this.uploadedPhotos = []
+				this.photoRemark = ''
+				this.showPhotoPopup = true
+			},
+			handlePhotoDeliver(item) {
+				this.selectedOrder = item
+				this.photoPopupMode = 'deliver'
+				this.uploadedPhotos = []
+				this.photoRemark = ''
+				this.showPhotoPopup = true
+			},
+			closePhotoPopup() {
+				this.showPhotoPopup = false
+			},
+			choosePhoto() {
+				if (this.uploadedPhotos.length >= 3) {
+					uni.showToast({ title: '鏈�澶氫笂浼�3寮犵収鐗�', icon: 'none' })
+					return
+				}
+				uni.chooseImage({
+					count: 3 - this.uploadedPhotos.length,
+					sourceType: ['camera', 'album'],
+					success: (res) => {
+						this.uploadedPhotos = [...this.uploadedPhotos, ...res.tempFilePaths]
+					}
+				})
+			},
+			deletePhoto(index) {
+				this.uploadedPhotos.splice(index, 1)
+			},
+			submitPhotoPopup() {
+				if (this.uploadedPhotos.length === 0) {
+					uni.showToast({ title: '璇蜂笂浼犵収鐗�', icon: 'none' })
+					return
+				}
+				uni.showLoading({ title: '涓婁紶涓�...' })
+				const uploadTasks = this.uploadedPhotos.map(path => {
+					return new Promise((resolve, reject) => {
+						uni.uploadFile({
+							url: this.$baseUrl + 'web/public/upload',
+							filePath: path,
+							name: 'file',
+							formData: {
+								folder: 'order'
+							},
+							success: (uploadRes) => {
+								const data = JSON.parse(uploadRes.data)
+								if (data.code === 200) {
+									resolve(data.data)
+								} else {
+									reject(new Error(data.msg))
+								}
+							},
+							fail: (err) => {
+								reject(err)
+							}
+						})
+					})
+				})
+
+				Promise.all(uploadTasks).then(images => {
+					const api = this.photoPopupMode === 'deliver' ? 'confirmDeliver' : 'confirmPickup'
+					const params = {
+						images: images.map(img => img.imgaddr),
+						orderId: this.selectedOrder.id,
+						remark: this.photoRemark
+					}
+					return this.$u.api[api](params)
+				}).then(res => {
+					uni.hideLoading()
+					if (res.code === 200) {
+						uni.showToast({ title: '鎻愪氦鎴愬姛', icon: 'success' })
+						this.showPhotoPopup = false
+						this.getOrderList()
+						this.getActiveOrderCount()
+					} else {
+						uni.showToast({ title: res.msg || '鎻愪氦澶辫触', icon: 'none' })
+					}
+				}).catch(err => {
+					uni.hideLoading()
+					uni.showToast({ title: err.message || '涓婁紶澶辫触', icon: 'none' })
+				})
+			}
 		}
 	}
 </script>
 
-<style lang="scss">
+<style lang="scss" scoped>
+	.order-page {
+		height: 100vh;
+		background: #f5f7fb;
+		overflow: hidden;
 
-</style>
\ No newline at end of file
+		&__nav {
+			position: fixed;
+			left: 0;
+			top: 0;
+			right: 0;
+			z-index: 10;
+			background: linear-gradient(180deg, #1f73f6 0%, #1b6cf2 100%);
+		}
+
+		&__nav-inner {
+			height: 88rpx;
+			display: flex;
+			align-items: center;
+			padding: 0 28rpx;
+		}
+
+		&__nav-title {
+			font-size: 38rpx;
+			font-weight: 700;
+			color: #ffffff;
+		}
+
+		&__tabs {
+			position: fixed;
+			left: 0;
+			right: 0;
+			z-index: 9;
+			height: 88rpx;
+			display: flex;
+			align-items: center;
+			background: #ffffff;
+			box-shadow: 0 10rpx 20rpx rgba(40, 72, 128, 0.04);
+		}
+
+		&__tab {
+			position: relative;
+			flex: 1;
+			height: 100%;
+			display: flex;
+			justify-content: center;
+			align-items: center;
+
+			&--active {
+				.order-page__tab-text {
+					color: #272b33;
+					font-weight: 700;
+				}
+			}
+		}
+
+		&__tab-text {
+			font-size: 30rpx;
+			color: #8f96a3;
+		}
+
+		&__tab-line {
+			position: absolute;
+			left: 26rpx;
+			right: 26rpx;
+			bottom: 0;
+			height: 4rpx;
+			border-radius: 999rpx;
+			background: #1a73f8;
+		}
+
+		&__body {
+			box-sizing: border-box;
+		}
+
+		&__list {
+			padding: 18rpx 22rpx calc(env(safe-area-inset-bottom) + 26rpx);
+		}
+
+		&__empty {
+			display: flex;
+			flex-direction: column;
+			align-items: center;
+			justify-content: center;
+			padding-top: 200rpx;
+		}
+
+		&__empty-icon {
+			width: 320rpx;
+			height: 320rpx;
+		}
+
+		&__loading {
+			display: flex;
+			justify-content: center;
+			align-items: center;
+			padding: 30rpx 0;
+			color: #8f96a3;
+			font-size: 26rpx;
+		}
+	}
+
+	.order-card {
+		margin-bottom: 18rpx;
+		padding: 20rpx 18rpx 18rpx;
+		border-radius: 20rpx;
+		background: #ffffff;
+		box-shadow: 0 8rpx 20rpx rgba(43, 65, 106, 0.05);
+
+		&__head {
+			display: flex;
+			justify-content: space-between;
+			align-items: center;
+		}
+
+		&__head-left {
+			display: flex;
+			align-items: center;
+			gap: 12rpx;
+			min-width: 0;
+		}
+
+		&__badge {
+			padding: 4rpx 10rpx;
+			border-radius: 10rpx;
+			font-size: 22rpx;
+			line-height: 1.2;
+			font-weight: 600;
+
+			&--blue {
+				border: 1rpx solid #75cfff;
+				color: #27a8f8;
+				background: #eefaff;
+			}
+
+			&--red {
+				border: 1rpx solid #ff8f8f;
+				color: #ff5d5d;
+				background: #fff1f1;
+			}
+		}
+
+		&__badge-icon {
+			width: 108rpx;
+			height: 40rpx;
+			flex-shrink: 0;
+		}
+
+		&__time-text,
+		&__status,
+		&__route-desc,
+		&__arrival-text {
+			font-size: 24rpx;
+			color: #a1a7b2;
+		}
+
+		&__status {
+			flex-shrink: 0;
+
+			&--highlight {
+				color: #ff4a3d;
+				font-weight: 700;
+			}
+		}
+
+		&__route-item {
+			display: flex;
+			align-items: flex-start;
+			margin-top: 22rpx;
+
+			&--delivery {
+				margin-top: 20rpx;
+			}
+		}
+
+		&__point {
+			width: 34rpx;
+			height: 34rpx;
+			line-height: 34rpx;
+			text-align: center;
+			border-radius: 50%;
+			font-size: 22rpx;
+			font-weight: 700;
+			color: #ffffff;
+			flex-shrink: 0;
+			margin-right: 16rpx;
+
+			&--pickup {
+				background: #2ab8ff;
+			}
+
+			&--delivery {
+				background: #ff9d2e;
+			}
+		}
+
+		&__route-texts {
+			flex: 1;
+			min-width: 0;
+		}
+
+		&__route-title {
+			display: block;
+			font-size: 34rpx;
+			font-weight: 700;
+			color: #2d3139;
+			line-height: 1.3;
+		}
+
+		&__route-desc {
+			display: block;
+			margin-top: 8rpx;
+			line-height: 1.4;
+		}
+
+		&__footer {
+			display: flex;
+			justify-content: space-between;
+			align-items: center;
+			margin-top: 22rpx;
+			padding-top: 16rpx;
+			border-top: 1rpx solid #f0f2f6;
+		}
+
+		&__arrival {
+			display: flex;
+			align-items: center;
+			gap: 10rpx;
+		}
+
+		&__clock {
+			width: 24rpx;
+			height: 24rpx;
+			flex-shrink: 0;
+		}
+
+		&__price-wrap {
+			display: flex;
+			align-items: center;
+			gap: 10rpx;
+		}
+
+		&__price-tag {
+			padding: 3rpx 8rpx;
+			border-radius: 8rpx;
+			border: 1rpx solid #ff8f8f;
+			font-size: 22rpx;
+			font-weight: 600;
+			color: #ff6a6a;
+			background: #fff4f4;
+		}
+
+		&__price {
+			font-size: 40rpx;
+			font-weight: 700;
+			color: #ff4030;
+		}
+
+		&__actions {
+			display: flex;
+			justify-content: space-between;
+			margin-top: 18rpx;
+			padding-top: 18rpx;
+			border-top: 1rpx solid #f0f2f6;
+		}
+
+		&__action-btn {
+			width: 160rpx;
+			height: 64rpx;
+			line-height: 64rpx;
+			padding: 0;
+			border-radius: 34rpx;
+			font-size: 28rpx;
+			font-weight: 500;
+			border: 1rpx solid transparent;
+			background: #ffffff;
+			box-sizing: border-box;
+
+			&::after {
+				border: 0;
+			}
+
+			&--light {
+				border-color: #d7dbe3;
+				color: #8f96a3;
+			}
+
+			&--primary {
+				border-color: #2c7cff;
+				color: #2c7cff;
+			}
+
+			&--primary-fill {
+				background: #2c7cff;
+				color: #ffffff;
+			}
+
+			&--hover {
+				opacity: 0.92;
+			}
+		}
+	}
+
+.qrcode {
+	padding: 30rpx 40rpx 50rpx;
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+
+	&-title {
+		width: 100%;
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+		margin-bottom: 40rpx;
+
+		image {
+			width: 40rpx;
+			height: 40rpx;
+		}
+
+		text {
+			font-size: 34rpx;
+			font-weight: 700;
+			color: #2d3139;
+		}
+	}
+
+	&-image {
+		width: 400rpx;
+		height: 400rpx;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		background: #f5f7fb;
+		border-radius: 16rpx;
+
+		image {
+			width: 360rpx;
+			height: 360rpx;
+		}
+	}
+
+	&-btn {
+		width: 100%;
+		height: 88rpx;
+		line-height: 88rpx;
+		text-align: center;
+		background: #106EFA;
+		border-radius: 44rpx;
+		font-size: 32rpx;
+		font-weight: 600;
+		color: #ffffff;
+		margin-top: 40rpx;
+
+		&--hover {
+			background: #0d5fc7;
+		}
+	}
+}
+
+.photo-popup {
+	padding: 30rpx 30rpx calc(env(safe-area-inset-bottom) + 30rpx);
+
+	&__header {
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+		margin-bottom: 40rpx;
+	}
+
+	&__placeholder {
+		width: 40rpx;
+		height: 40rpx;
+	}
+
+	&__title {
+		font-size: 34rpx;
+		font-weight: 700;
+		color: #2d3139;
+	}
+
+	&__close {
+		width: 40rpx;
+		height: 40rpx;
+	}
+
+	&__section {
+		margin-bottom: 30rpx;
+
+		&--remark {
+			margin-top: 30rpx;
+		}
+	}
+
+	&__label-row {
+		display: flex;
+		align-items: center;
+		margin-bottom: 20rpx;
+	}
+
+	&__label {
+		font-size: 30rpx;
+		font-weight: 600;
+		color: #2d3139;
+	}
+
+	&__required {
+		color: #ff4030;
+		margin-left: 8rpx;
+	}
+
+	&__hint {
+		font-size: 24rpx;
+		color: #8f96a3;
+		margin-left: auto;
+	}
+
+	&__photos {
+		display: flex;
+		flex-wrap: wrap;
+		gap: 20rpx;
+	}
+
+	&__preview-card {
+		position: relative;
+		width: 144rpx;
+		height: 144rpx;
+		border-radius: 12rpx;
+		overflow: hidden;
+	}
+
+	&__preview-image {
+		width: 100%;
+		height: 100%;
+	}
+
+	&__preview-mask {
+		position: absolute;
+		left: 0;
+		right: 0;
+		bottom: 0;
+		height: 56rpx;
+		background: rgba(0, 0, 0, 0.5);
+		display: flex;
+		align-items: center;
+		justify-content: center;
+	}
+
+	&__preview-delete {
+		font-size: 26rpx;
+		color: #ffffff;
+	}
+
+	&__upload-btn {
+		width: 144rpx;
+		height: 144rpx;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+
+		image {
+			width: 100%;
+			height: 100%;
+		}
+	}
+
+	&__remark-title {
+		font-size: 30rpx;
+		font-weight: 600;
+		color: #2d3139;
+		margin-bottom: 20rpx;
+		display: block;
+	}
+
+	&__textarea {
+		width: 100%;
+		height: 160rpx;
+		padding: 20rpx;
+		background: #f5f7fb;
+		border-radius: 12rpx;
+		font-size: 28rpx;
+		color: #2d3139;
+		box-sizing: border-box;
+	}
+
+	&__submit {
+		width: 100%;
+		height: 88rpx;
+		line-height: 88rpx;
+		background: #2c7cff;
+		border-radius: 44rpx;
+		font-size: 32rpx;
+		font-weight: 600;
+		color: #ffffff;
+		margin-top: 40rpx;
+
+		&--hover {
+			background: #2678e8;
+		}
+	}
+}
+</style>

--
Gitblit v1.9.3