feat(visit): 客户拜访页按新原型重构为多卡片表单

参照设计稿将 pages/visit/index 拆分为 4 张表单卡片 + 历史记录卡片:
- 我的信息:姓名,进页面自动带出账号 realName,可编辑
- 拜访对象:类型(门店/门店经理/其他 胶囊 tab)、姓名/门店、连锁
- 拜访信息:定位信息(点击重新定位 + 经纬度副行)、拜访日期(默认当天)
- 其他信息:沟通内容(500 字 + 实时计数)、拍照上传、活动小结

一并修复:
- 必填星号原用 ::before 伪元素,小程序对 <text> 伪元素支持不稳,改为
  显式 <text class="field-star">
- 必填标签原复用全局类名 .required,而 common.scss 中的 .required 经
  @extend .pos-a 带上 position:absolute / left:-9px / top:15px,导致标签
  脱离文档流堆叠到卡片左上角。改用 .is-required 并在样式处加注释说明。

配色沿用项目主题变量,未照搬设计稿颜色。历史记录展开详情增加拜访
类型与拜访日期两行。
This commit is contained in:
2026-10-11 20:47:31 +08:00
parent 876847ccbb
commit da5bf8733e
+391 -150
View File
@@ -3,88 +3,195 @@
<view class="page-hero"> <view class="page-hero">
<AppNavbar title="客户拜访" show-back/> <AppNavbar title="客户拜访" show-back/>
<view class="intro-card"> <view class="intro-card">
<view class="card-title"> <view class="tip-icon">
<view class="title-bar"></view> <u-icon name="info-circle-fill" size="40rpx" :color="brandColor"/>
<text>培训拜访</text> </view>
<view class="tip-body">
<text class="tip-title">温馨提示</text>
<text class="tip-text">请如实填写本次拜访信息,系统将自动记录您的姓名,拜访对象请填写实际到访人员或门店/经理。</text>
</view> </view>
<text class="intro-text">记录培训拜访现场定位、沟通内容和照片,提交后可回看历史记录。</text>
</view> </view>
</view> </view>
<!-- 我的信息 -->
<view class="card block"> <view class="card block">
<view class="section-title">新增拜访</view> <view class="form-card-head">
<view class="form"> <view class="head-icon">
<view class="form-row location-row"> <u-icon name="account-fill" size="32rpx" :color="brandColor"/>
<text>定位信息</text>
<view class="location-box">
<view class="picker-value location-text" :class="{placeholder: !visitForm.location}">
{{ visitForm.location || '定位中,或点击右侧重新定位' }}
</view>
<view class="location-meta">
<text>{{ formatCoordinate('纬度', visitForm.lat) }}</text>
<text>{{ formatCoordinate('经度', visitForm.lng) }}</text>
</view>
</view>
<view class="refresh-btn" @tap="fetchLocation(false, true)">重新定位</view>
</view> </view>
<text class="head-title">我的信息</text>
</view>
<view class="field-row">
<text class="field-label is-required"><text class="field-star">*</text>姓名</text>
<view class="field-box">
<u-icon name="account" size="32rpx" color="#9aa7b8"/>
<input
v-model="visitForm.submitterName"
class="field-input"
type="text"
maxlength="30"
placeholder="请输入您的姓名"
placeholder-class="field-placeholder"
/>
</view>
</view>
</view>
<view class="form-row"> <!-- 拜访对象 -->
<text>连锁</text> <view class="card block">
<view class="selector-picker" @tap="openChainPicker"> <view class="form-card-head">
<view class="picker-value" :class="{placeholder: !visitForm.chain}"> <view class="head-icon">
{{ visitForm.chain || (chainLoading ? '连锁加载中...' : '请选择连锁') }} <u-icon name="account" size="32rpx" :color="brandColor"/>
</view> </view>
<text class="head-title">拜访对象</text>
</view>
<view class="field-row">
<text class="field-label is-required"><text class="field-star">*</text>类型</text>
<view class="type-tabs">
<view
v-for="tab in visitTypeTabs"
:key="tab.value"
:class="['type-tab', visitForm.visitType === tab.value ? 'active' : '']"
@tap="visitForm.visitType = tab.value"
>
<u-icon :name="tab.icon" size="28rpx" :color="visitForm.visitType === tab.value ? brandColor : '#9aa7b8'"/>
<text>{{ tab.label }}</text>
</view> </view>
</view> </view>
</view>
<view class="form-row"> <view class="field-row">
<text>拜访人员</text> <text class="field-label is-required"><text class="field-star">*</text>姓名/门店</text>
<input v-model="visitForm.name" maxlength="30" placeholder="请输入拜访人员"/> <view class="field-box">
<input
v-model="visitForm.name"
class="field-input"
type="text"
maxlength="30"
:placeholder="targetPlaceholder"
placeholder-class="field-placeholder"
/>
<u-icon name="arrow-right" size="26rpx" color="#b6c2d1"/>
</view> </view>
</view>
<view class="form-row textarea-row"> <view class="field-row">
<text>沟通内容</text> <text class="field-label is-required"><text class="field-star">*</text>连锁</text>
<view class="field-box" @tap="openChainPicker">
<text class="field-text" :class="{placeholder: !visitForm.chain}">
{{ visitForm.chain || (chainLoading ? '连锁加载中...' : '请选择连锁') }}
</text>
<u-icon name="arrow-right" size="26rpx" color="#b6c2d1"/>
</view>
</view>
</view>
<!-- 拜访信息 -->
<view class="card block">
<view class="form-card-head">
<view class="head-icon">
<u-icon name="map-fill" size="32rpx" :color="brandColor"/>
</view>
<text class="head-title">拜访信息</text>
</view>
<view class="field-row">
<text class="field-label is-required"><text class="field-star">*</text>定位信息</text>
<view class="field-box" @tap="fetchLocation(false, true)">
<u-icon name="map" size="32rpx" color="#9aa7b8"/>
<text class="field-text" :class="{placeholder: !visitForm.location}">
{{ visitForm.location || '请点击获取当前位置' }}
</text>
<u-icon name="reload" size="30rpx" color="#b6c2d1"/>
</view>
</view>
<view v-if="visitForm.lat || visitForm.lng" class="field-meta">
<text>{{ formatCoordinate('纬度', visitForm.lat) }}</text>
<text>{{ formatCoordinate('经度', visitForm.lng) }}</text>
</view>
<view class="field-row">
<text class="field-label is-required"><text class="field-star">*</text>拜访日期</text>
<view class="field-box" @tap="showVisitDatePicker = true">
<u-icon name="calendar" size="32rpx" color="#9aa7b8"/>
<text class="field-text" :class="{placeholder: !visitForm.visitDate}">
{{ visitForm.visitDate || '请选择拜访日期' }}
</text>
<u-icon name="arrow-right" size="26rpx" color="#b6c2d1"/>
</view>
</view>
</view>
<!-- 其他信息 -->
<view class="card block">
<view class="form-card-head">
<view class="head-icon">
<u-icon name="edit-pen-fill" size="32rpx" :color="brandColor"/>
</view>
<text class="head-title">其他信息</text>
</view>
<view class="field-row textarea-row">
<text class="field-label is-required"><text class="field-star">*</text>沟通内容</text>
<view class="field-box textarea-box">
<textarea <textarea
v-model="visitForm.content" v-model="visitForm.content"
class="textarea-input" class="field-textarea"
maxlength="500" :maxlength="contentMaxLength"
auto-height placeholder="请输入沟通内容..."
placeholder="请输入沟通内容" placeholder-class="field-placeholder"
/> />
<text class="counter">{{ (visitForm.content || '').length }}/{{ contentMaxLength }}</text>
</view> </view>
</view>
<view class="upload" @tap="uploadPhotos"> <view class="field-row">
<view> <text class="field-label is-required"><text class="field-star">*</text>拍照上传</text>
<view class="upload-title">拍照上传</view> <view class="field-box" @tap="uploadPhotos">
<view class="upload-meta">{{ photos.length ? `已上传 ${photos.length} 张,点击继续拍照` : '点击拍照上传,最多 9 张' }}</view> <u-icon name="camera-fill" size="32rpx" color="#9aa7b8"/>
</view> <text class="field-text" :class="{placeholder: !photos.length}">
<view class="chip">必填</view> {{ photos.length ? `已上传 ${photos.length} 张,点击继续拍照` : '点击拍照上传,最多 9 张' }}
</text>
<u-icon name="arrow-right" size="26rpx" color="#b6c2d1"/>
</view> </view>
</view>
<view v-if="photos.length" class="photo-list"> <view v-if="photos.length" class="photo-list">
<view class="photo-item" v-for="(photo, index) in photos" :key="`${photo}-${index}`"> <view class="photo-item" v-for="(photo, index) in photos" :key="`${photo}-${index}`">
<image class="photo-image" :src="photo" mode="aspectFill" @tap="previewPhoto(index)"/> <image class="photo-image" :src="photo" mode="aspectFill" @tap="previewPhoto(index)"/>
<view class="photo-remove" @tap.stop="removePhoto(index)">x</view> <view class="photo-remove" @tap.stop="removePhoto(index)">x</view>
</view>
</view> </view>
</view>
<view class="form-row textarea-row"> <view class="field-row textarea-row">
<text>活动小结</text> <text class="field-label is-required"><text class="field-star">*</text>活动小结</text>
<view class="field-box textarea-box">
<textarea <textarea
v-model="visitForm.summary" v-model="visitForm.summary"
class="textarea-input" class="field-textarea"
maxlength="300" maxlength="300"
auto-height
placeholder="请输入活动小结" placeholder="请输入活动小结"
placeholder-class="field-placeholder"
/> />
<text class="counter">{{ (visitForm.summary || '').length }}/300</text>
</view> </view>
</view>
</view>
<view class="primary-btn submit" @tap="submitVisit">提交拜访</view> <view class="submit-wrap">
<view class="primary-btn submit" @tap="submitVisit">
<text>提交拜访</text>
<u-icon name="arrow-right" size="30rpx" color="#ffffff"/>
</view> </view>
</view> </view>
<view class="card block"> <view class="card block">
<view class="section-title">历史记录</view> <view class="form-card-head">
<view class="head-icon">
<u-icon name="list-dot" size="32rpx" :color="brandColor"/>
</view>
<text class="head-title">历史记录</text>
</view>
<view v-if="loading" class="empty">加载中...</view> <view v-if="loading" class="empty">加载中...</view>
<view v-else-if="visitHistory.length === 0" class="empty">暂无记录</view> <view v-else-if="visitHistory.length === 0" class="empty">暂无记录</view>
<view v-else> <view v-else>
@@ -100,6 +207,8 @@
</view> </view>
</view> </view>
<view v-if="item.expanded" class="history-detail"> <view v-if="item.expanded" class="history-detail">
<view class="history-line">拜访类型:{{ item.visitType || '-' }}</view>
<view class="history-line">拜访日期:{{ item.visitDate || '-' }}</view>
<view class="history-line">定位:{{ item.location || '-' }}</view> <view class="history-line">定位:{{ item.location || '-' }}</view>
<view class="history-line">纬度:{{ item.lat || '-' }}</view> <view class="history-line">纬度:{{ item.lat || '-' }}</view>
<view class="history-line">经度:{{ item.lng || '-' }}</view> <view class="history-line">经度:{{ item.lng || '-' }}</view>
@@ -133,25 +242,43 @@
@cancel="closeChainPicker" @cancel="closeChainPicker"
@close="closeChainPicker" @close="closeChainPicker"
/> />
<u-datetime-picker
:show="showVisitDatePicker"
:value="visitDatePickerValue"
mode="date"
@confirm="onVisitDateConfirm"
@cancel="showVisitDatePicker = false"
@close="showVisitDatePicker = false"
/>
</view> </view>
</template> </template>
<script> <script>
import dayjs from "dayjs";
import AppNavbar from "@/components/AppNavbar.vue"; import AppNavbar from "@/components/AppNavbar.vue";
import {chooseImg, getLocationOnly} from "@/util"; import {chooseImg, getLocationOnly} from "@/util";
import {visitAddReq, visitListReq} from "@/api/visit"; import {visitAddReq, visitListReq} from "@/api/visit";
import {merchantGroupByChainReq} from "@/api/shop"; import {merchantGroupByChainReq} from "@/api/shop";
import {userInfoReq} from "@/api/user";
export default { export default {
components: {AppNavbar}, components: {AppNavbar},
data() { data() {
return { return {
brandColor: '#2fc6a7',
contentMaxLength: 500,
visitTypeTabs: [
{label: '门店', value: '门店', icon: 'home'},
{label: '门店经理', value: '门店经理', icon: 'account'},
{label: '其他', value: '其他', icon: 'more-dot-fill'}
],
visitForm: this.buildForm(), visitForm: this.buildForm(),
photos: [], photos: [],
visitHistory: [], visitHistory: [],
chainOptions: [], chainOptions: [],
chainLoading: false, chainLoading: false,
showChainPicker: false, showChainPicker: false,
showVisitDatePicker: false,
loading: false, loading: false,
locating: false, locating: false,
} }
@@ -161,13 +288,22 @@ export default {
if (!this.chainOptions.length) return [0] if (!this.chainOptions.length) return [0]
const idx = this.chainOptions.findIndex(item => String(item.id) === String(this.visitForm.chainId)) const idx = this.chainOptions.findIndex(item => String(item.id) === String(this.visitForm.chainId))
return [idx < 0 ? 0 : idx] return [idx < 0 ? 0 : idx]
},
visitDatePickerValue() {
return this.visitForm.visitDate ? dayjs(this.visitForm.visitDate).valueOf() : Date.now()
},
targetPlaceholder() {
if (this.visitForm.visitType === '门店经理') return '请输入门店经理姓名'
if (this.visitForm.visitType === '其他') return '请输入拜访人员姓名'
return '请输入拜访人员姓名 / 门店名称 / 经理姓名'
} }
}, },
async onShow() { async onShow() {
await Promise.all([ await Promise.all([
this.fetchLocation(true), this.fetchLocation(true),
this.fetchHistory(), this.fetchHistory(),
this.fetchChainOptions() this.fetchChainOptions(),
this.fetchSubmitterName()
]) ])
}, },
methods: { methods: {
@@ -179,10 +315,25 @@ export default {
chainId: '', chainId: '',
chain: '', chain: '',
name: '', name: '',
visitType: '门店',
visitDate: dayjs().format('YYYY-MM-DD'),
submitterName: '',
content: '', content: '',
summary: '' summary: ''
} }
}, },
async fetchSubmitterName() {
if (this.visitForm.submitterName) return
try {
const {data} = await userInfoReq()
const info = data || {}
const name = info.realName || info.real_name || info.nickname || info.name || ''
if (name && !this.visitForm.submitterName) {
this.visitForm.submitterName = name
}
} catch (err) {
}
},
async fetchChainOptions() { async fetchChainOptions() {
this.chainLoading = true this.chainLoading = true
try { try {
@@ -212,6 +363,11 @@ export default {
this.visitForm.chain = selected ? selected.text : '' this.visitForm.chain = selected ? selected.text : ''
this.showChainPicker = false this.showChainPicker = false
}, },
onVisitDateConfirm(event) {
const raw = event && event.value !== undefined ? event.value : Date.now()
this.visitForm.visitDate = dayjs(raw).format('YYYY-MM-DD')
this.showVisitDatePicker = false
},
formatCoordinate(label, value) { formatCoordinate(label, value) {
if (value === '' || value === null || value === undefined) return `${label}:-` if (value === '' || value === null || value === undefined) return `${label}:-`
return `${label}:${value}` return `${label}:${value}`
@@ -268,9 +424,23 @@ export default {
}) })
}, },
async submitVisit() { async submitVisit() {
if (!this.visitForm.lat || !this.visitForm.lng || !this.visitForm.location) { if (!String(this.visitForm.submitterName || '').trim()) {
uni.showToast({ uni.showToast({
title: '请先获取定位', title: '请填写您的姓名',
icon: 'none'
})
return
}
if (!this.visitForm.visitType) {
uni.showToast({
title: '请选择拜访对象类型',
icon: 'none'
})
return
}
if (!String(this.visitForm.name || '').trim()) {
uni.showToast({
title: '请填写拜访对象姓名/门店',
icon: 'none' icon: 'none'
}) })
return return
@@ -282,7 +452,21 @@ export default {
}) })
return return
} }
if (!this.visitForm.name || !this.visitForm.content || !this.photos.length || !this.visitForm.summary) { if (!this.visitForm.lat || !this.visitForm.lng || !this.visitForm.location) {
uni.showToast({
title: '请先获取定位',
icon: 'none'
})
return
}
if (!this.visitForm.visitDate) {
uni.showToast({
title: '请选择拜访日期',
icon: 'none'
})
return
}
if (!this.visitForm.content || !this.photos.length || !this.visitForm.summary) {
uni.showToast({ uni.showToast({
title: '请填写完整信息', title: '请填写完整信息',
icon: 'none' icon: 'none'
@@ -297,6 +481,9 @@ export default {
chainId: this.visitForm.chainId, chainId: this.visitForm.chainId,
chain: this.visitForm.chain, chain: this.visitForm.chain,
name: this.visitForm.name, name: this.visitForm.name,
visitType: this.visitForm.visitType,
visitDate: this.visitForm.visitDate,
submitterName: this.visitForm.submitterName,
content: this.visitForm.content, content: this.visitForm.content,
photos: JSON.stringify(this.photos), photos: JSON.stringify(this.photos),
summary: this.visitForm.summary summary: this.visitForm.summary
@@ -305,16 +492,17 @@ export default {
title: '提交成功', title: '提交成功',
icon: 'success' icon: 'success'
}) })
const locationSnapshot = { const snapshot = {
lat: this.visitForm.lat, lat: this.visitForm.lat,
lng: this.visitForm.lng, lng: this.visitForm.lng,
location: this.visitForm.location, location: this.visitForm.location,
chainId: this.visitForm.chainId, chainId: this.visitForm.chainId,
chain: this.visitForm.chain chain: this.visitForm.chain,
submitterName: this.visitForm.submitterName
} }
this.visitForm = { this.visitForm = {
...this.buildForm(), ...this.buildForm(),
...locationSnapshot ...snapshot
} }
this.photos = [] this.photos = []
await this.fetchHistory() await this.fetchHistory()
@@ -362,6 +550,8 @@ export default {
name: item.name || item.visitName || '', name: item.name || item.visitName || '',
content: item.content || item.remark || '', content: item.content || item.remark || '',
summary: item.summary || item.result || '', summary: item.summary || item.result || '',
visitType: item.visitType || item.visit_type || '',
visitDate: item.visitDate || item.visit_date || '',
location: item.location || item.address || '', location: item.location || item.address || '',
lat: item.lat || item.latitude || '', lat: item.lat || item.latitude || '',
lng: item.lng || item.longitude || '', lng: item.lng || item.longitude || '',
@@ -449,6 +639,7 @@ export default {
.visit-page { .visit-page {
min-height: 100vh; min-height: 100vh;
background: #f5f5f5; background: #f5f5f5;
padding-bottom: 40rpx;
} }
.page-hero { .page-hero {
@@ -457,31 +648,35 @@ export default {
.intro-card { .intro-card {
margin: 18rpx 20rpx 0; margin: 18rpx 20rpx 0;
padding: 20rpx 18rpx; padding: 22rpx 20rpx;
border-radius: 20rpx; border-radius: 20rpx;
background: rgba(255, 255, 255, 0.9); background: rgba(255, 255, 255, 0.94);
box-shadow: 0 10rpx 20rpx rgba(0, 0, 0, 0.08); box-shadow: 0 10rpx 20rpx rgba(0, 0, 0, 0.08);
display: flex;
align-items: flex-start;
gap: 16rpx;
} }
.card-title { .tip-icon {
flex-shrink: 0;
margin-top: 2rpx;
}
.tip-body {
flex: 1;
display: flex; display: flex;
align-items: center; flex-direction: column;
gap: 12rpx; gap: 8rpx;
}
.tip-title {
font-size: 28rpx; font-size: 28rpx;
font-weight: 700; font-weight: 700;
color: #1f2937; color: #1f2937;
} }
.title-bar { .tip-text {
width: 6rpx; font-size: 25rpx;
height: 30rpx;
border-radius: 999rpx;
background: #2f78ff;
}
.intro-text {
margin-top: 12rpx;
font-size: 26rpx;
color: #6b7280; color: #6b7280;
line-height: 1.6; line-height: 1.6;
} }
@@ -490,124 +685,165 @@ export default {
margin: 18rpx 20rpx 0; margin: 18rpx 20rpx 0;
} }
.form { .form-card-head {
display: flex; display: flex;
flex-direction: column;
gap: 10rpx;
}
.form-row {
display: flex;
justify-content: space-between;
align-items: center; align-items: center;
gap: 20rpx; gap: 14rpx;
padding: 12rpx 0; padding-bottom: 20rpx;
border-bottom: 1rpx solid #edf1f7; margin-bottom: 6rpx;
border-bottom: 1rpx solid #eef2f7;
} }
.form-row input { .head-icon {
flex: 1; width: 56rpx;
text-align: right; height: 56rpx;
border-radius: 16rpx;
display: flex;
align-items: center;
justify-content: center;
background: rgba(47, 198, 167, 0.12);
}
.head-title {
font-size: 30rpx;
font-weight: 700;
color: var(--text-main); color: var(--text-main);
} }
.selector-picker { .field-row {
flex: 1; display: flex;
align-items: center;
gap: 20rpx;
padding: 14rpx 0;
} }
.selector-picker .picker-value { .field-row.textarea-row {
text-align: right;
}
.location-row {
align-items: flex-start; align-items: flex-start;
} }
.location-box { .field-label {
flex: 1; flex-shrink: 0;
display: flex; width: 158rpx;
flex-direction: column; font-size: 28rpx;
align-items: flex-end;
gap: 8rpx;
}
.picker-value {
color: var(--text-main); color: var(--text-main);
font-weight: 600;
} }
.picker-value.placeholder { /* 注意:不要用 .required 作类名 —— 全局 common.scss 里的 .required
color: var(--text-sub); (经 uni.scss 全局引入)@extend 了 .pos-a,会带上 position:absolute /
left:-9px / top:15px,导致标签脱离文档流堆到卡片左上角。 */
.field-label.is-required {
display: flex;
align-items: center;
} }
.location-text { .field-star {
color: #ff3b30;
margin-right: 6rpx;
font-size: 26rpx;
}
.field-box {
flex: 1;
min-width: 0;
display: flex;
align-items: center;
gap: 12rpx;
min-height: 80rpx;
padding: 0 20rpx;
border-radius: 16rpx;
background: #f5f7fa;
}
.textarea-box {
flex-direction: column;
align-items: stretch;
padding: 16rpx 20rpx;
}
.field-input {
flex: 1;
min-width: 0;
height: 80rpx;
font-size: 28rpx;
color: var(--text-main);
}
.field-textarea {
width: 100%; width: 100%;
text-align: right; min-height: 180rpx;
font-size: 28rpx;
line-height: 1.6;
color: var(--text-main);
}
.field-text {
flex: 1;
min-width: 0;
font-size: 28rpx;
color: var(--text-main);
line-height: 1.5; line-height: 1.5;
word-break: break-all; word-break: break-all;
} }
.location-meta { .field-text.placeholder,
.field-placeholder {
color: #9aa7b8;
}
.counter {
align-self: flex-end;
margin-top: 6rpx;
font-size: 24rpx;
color: #9aa7b8;
}
.field-meta {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: flex-end; align-items: flex-end;
gap: 4rpx; gap: 4rpx;
margin: -6rpx 0 6rpx 178rpx;
font-size: 22rpx; font-size: 22rpx;
color: var(--text-sub); color: var(--text-sub);
} }
.refresh-btn { .type-tabs {
flex-shrink: 0;
margin-top: 2rpx;
padding: 8rpx 18rpx;
border-radius: 999rpx;
background: #eef4ff;
color: #2f78ff;
font-size: 22rpx;
font-weight: 700;
}
.textarea-row {
align-items: flex-start;
padding-top: 16rpx;
}
.textarea-input {
flex: 1; flex: 1;
min-height: 120rpx;
text-align: right;
font-size: 26rpx;
color: var(--text-main);
}
.upload {
display: flex; display: flex;
justify-content: space-between; gap: 12rpx;
}
.type-tab {
flex: 1;
min-width: 0;
display: flex;
align-items: center; align-items: center;
padding: 12rpx 0; justify-content: center;
border-bottom: 1rpx solid #edf1f7; gap: 6rpx;
} height: 80rpx;
border-radius: 16rpx;
.upload-title { background: #f5f7fa;
font-weight: 700;
}
.upload-meta {
color: var(--text-sub); color: var(--text-sub);
font-size: 24rpx; font-size: 25rpx;
}
.type-tab.active {
background: rgba(47, 198, 167, 0.14);
color: var(--brand-primary);
font-weight: 700;
} }
.photo-list { .photo-list {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 10rpx; gap: 10rpx;
margin-top: 10rpx; margin: 6rpx 0 6rpx 178rpx;
} }
.photo-item { .photo-item {
position: relative; position: relative;
width: 160rpx; width: 150rpx;
height: 160rpx; height: 150rpx;
border-radius: 12rpx; border-radius: 12rpx;
overflow: hidden; overflow: hidden;
background: #f8fafc; background: #f8fafc;
@@ -635,10 +871,15 @@ export default {
line-height: 1; line-height: 1;
} }
.submit-wrap {
margin: 24rpx 20rpx 0;
}
.submit { .submit {
margin-top: 12rpx; gap: 10rpx;
padding: 14rpx; padding: 24rpx 32rpx;
text-align: center; font-size: 32rpx;
font-weight: 700;
} }
.history { .history {