| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731 |
- <template>
- <view class="page-two-container">
- <!-- 上门地址卡片 -->
- <view class="card-wrap">
- <view class="address-section">
- <view class="address-header">
- <u-icon name="map" size="36rpx" color="#108cff" class="location-icon" />
- <text class="address-title">上门地址</text>
- </view>
- <view class="address-content">
- <text class="address-text">{{ orderDetail.address || '未填写' }}</text>
- </view>
- </view>
- </view>
- <!-- 跟进清单卡片 -->
- <view class="card-wrap checklist-card">
- <view class="checklist-section">
- <u-checkbox-group v-model="selectedCheckbox" placement="column">
- <!-- 联系师傅 -->
- <view class="checklist-item">
- <view class="checkbox-text-container">
- <u-checkbox name="contactMaster" size="40rpx" color="#108cff" />
- <text class="checklist-text">联系师傅</text>
- </view>
- <u-input
- v-if="selectedCheckbox.includes('contactMaster')"
- v-model="formData.contactPhone"
- placeholder="请输入师傅手机号"
- class="checklist-input"
- />
- </view>
- <!-- 师傅拍图技巧 -->
- <view class="checklist-item">
- <view class="checkbox-text-container">
- <u-checkbox name="photoTips" size="40rpx" color="#108cff" />
- <text class="checklist-text">师傅拍图技巧</text>
- </view>
- <u-input
- v-if="selectedCheckbox.includes('photoTips')"
- v-model="formData.photoTips"
- type="textarea"
- placeholder="请输入拍图技巧"
- rows="3"
- class="checklist-textarea"
- />
- <view v-if="selectedCheckbox.includes('photoTips')" class="upload-btn-container">
- <view class="upload-btn" @click="handleUpload('photoTips')">
- <u-icon name="camera" size="32rpx" color="#108cff" />
- <text class="upload-btn-text">上传图片</text>
- </view>
- </view>
- <view
- v-if="selectedCheckbox.includes('photoTips') && photoTipsImages.length > 0"
- class="image-list"
- >
- <view
- v-for="(image, index) in photoTipsImages"
- :key="index"
- class="image-item"
- @click="previewPhotoTips(image)"
- >
- <image :src="image" mode="aspectFill" class="image-thumb" />
- </view>
- </view>
- </view>
- <!-- 到达客户面对面 -->
- <view class="checklist-item">
- <view class="checkbox-text-container">
- <u-checkbox name="faceToFace" size="40rpx" color="#108cff" />
- <text class="checklist-text">到达客户面对面</text>
- </view>
- <u-input
- v-if="selectedCheckbox.includes('faceToFace')"
- v-model="formData.faceToFaceNotes"
- type="textarea"
- placeholder="请输入备注信息"
- rows="3"
- class="checklist-textarea"
- />
- <view v-if="selectedCheckbox.includes('faceToFace')" class="upload-btn-container">
- <view class="upload-btn" @click="handleUpload('faceToFace')">
- <u-icon name="camera" size="32rpx" color="#108cff" />
- <text class="upload-btn-text">上传图片</text>
- </view>
- </view>
- <view
- v-if="selectedCheckbox.includes('faceToFace') && faceToFaceImages.length > 0"
- class="image-list"
- >
- <view
- v-for="(image, index) in faceToFaceImages"
- :key="index"
- class="image-item"
- @click="previewFaceToFace(image)"
- >
- <image :src="image" mode="aspectFill" class="image-thumb" />
- </view>
- </view>
- </view>
- </u-checkbox-group>
- </view>
- </view>
- <!-- 核准价卡片 -->
- <view class="card-wrap price-card">
- <view class="price-section">
- <view class="price-picker-container">
- <view class="quick-actions top-actions">
- <view class="quick-btn increase" @click="quickChangePrice(100)">+100</view>
- <view class="quick-btn increase" @click="quickChangePrice(1000)">+1000</view>
- </view>
- <view class="number-box-container">
- <view class="price-input-box">
- <text class="price-label">核准价¥</text>
- <input
- type="number"
- v-model="approvedPrice"
- class="price-input"
- placeholder="0"
- min="0"
- @input="onPriceInput"
- />
- </view>
- </view>
- <view class="quick-actions bottom-actions">
- <view class="quick-btn decrease" @click="quickChangePrice(-100)">-100</view>
- <view class="quick-btn decrease" @click="quickChangePrice(-1000)">-1000</view>
- </view>
- </view>
- </view>
- </view>
- <!-- 高清细节图卡片 -->
- <view class="card-wrap detail-image-card">
- <view class="detail-image-section">
- <view class="detail-image-header">
- <text class="detail-image-title">上传高清细节图(支持多选)</text>
- <view class="copy-btn" @click="copyAllDetailImages">
- <text>一键复制</text>
- </view>
- </view>
- <view class="detail-image-upload-container">
- <view class="detail-image-list">
- <view
- v-for="(item, index) in detailImages"
- :key="`detail-${index}`"
- class="detail-image-item"
- >
- <PicComp
- :src="item.fileUrl"
- @needPreviewPic="previewImageDetail"
- />
- <view class="detail-delete-btn" @click="handleDeleteImage(item)">×</view>
- </view>
- <view
- class="detail-upload-btn"
- @click="handleUploadImage('detailImages')"
- >
- <u-icon name="plus" size="40rpx" color="#999" />
- </view>
- </view>
- </view>
- </view>
- </view>
- <!-- 下一步按钮 -->
- <u-button
- class="next-btn"
- @click="handleNext"
- type="primary"
- size="middle"
- >
- 下一步
- </u-button>
- </view>
- </template>
- <script>
- import PicComp from './PicComp.vue'
- import imageUpload from '../utils/imageUpload.js'
- export default {
- name: 'PageTwo',
- components: {
- PicComp
- },
- props: {
- orderDetail: {
- type: Object,
- default: () => ({})
- },
- orderId: {
- type: String,
- default: ''
- },
- currentReceipt: {
- type: Object,
- default: () => ({})
- },
- followUpList: {
- type: Array,
- default: () => []
- }
- },
- data() {
- return {
- selectedCheckbox: [],
- formData: {
- contactPhone: '',
- photoTips: '',
- faceToFaceNotes: ''
- },
- photoTipsImages: [],
- faceToFaceImages: [],
- approvedPrice: 0,
- detailImages: []
- }
- },
- watch: {
- currentReceipt: {
- handler(newVal) {
- if (newVal && newVal.id) {
- this.approvedPrice = Number(newVal.sellingPrice) || 0
- this.loadDetailImages()
- }
- },
- immediate: true,
- deep: true
- },
- followUpList: {
- handler(newVal) {
- if (newVal && newVal.length > 0) {
- this.checkFollowUpContent(newVal)
- }
- },
- deep: true
- }
- },
- methods: {
- /**
- * 加载细节图
- */
- async loadDetailImages() {
- if (!this.currentReceipt.id || !this.orderDetail.itemBrand) return
-
- try {
- const list = await imageUpload.getFileList(
- '2',
- '3',
- this.currentReceipt.id,
- this.orderDetail.itemBrand,
- this.currentReceipt.clueId
- )
- this.detailImages = list || []
- } catch (error) {
- console.error('加载细节图失败:', error)
- }
- },
- /**
- * 检查跟进内容
- */
- checkFollowUpContent(followUpList) {
- const contentList = followUpList.map(item => item.content || '')
-
- contentList.forEach(item => {
- if (item.includes('联系师傅') && !this.selectedCheckbox.includes('contactMaster')) {
- this.selectedCheckbox.push('contactMaster')
- const phone = item.split(';')[1] || ''
- this.formData.contactPhone = phone
- }
- if (item.includes('师傅拍图技巧') && !this.selectedCheckbox.includes('photoTips')) {
- this.selectedCheckbox.push('photoTips')
- const tips = item.split(';')[1] || ''
- this.formData.photoTips = tips
- const urls = item.split(';')[2] || ''
- this.photoTipsImages = urls.split(',').filter(url => url.trim())
- }
- if (item.includes('到达客户面对面') && !this.selectedCheckbox.includes('faceToFace')) {
- this.selectedCheckbox.push('faceToFace')
- const notes = item.split(';')[1] || ''
- this.formData.faceToFaceNotes = notes
- const urls = item.split(';')[2] || ''
- this.faceToFaceImages = urls.split(',').filter(url => url.trim())
- }
- })
- },
- /**
- * 价格输入处理
- */
- onPriceInput(e) {
- let value = Number(e.detail.value)
- if (isNaN(value)) value = 0
- value = Math.max(0, value)
- this.approvedPrice = value
- },
- /**
- * 快速调整价格
- */
- quickChangePrice(amount) {
- let newPrice = this.approvedPrice + amount
- newPrice = Math.max(0, newPrice)
- this.approvedPrice = newPrice
- },
- /**
- * 上传图片(跟进清单)
- */
- async handleUpload(field) {
- try {
- const filePaths = await imageUpload.chooseImage(9)
- const uploadResults = await imageUpload.uploadFiles(filePaths)
- const urls = uploadResults.map(item => item.fileUrl)
-
- if (field === 'photoTips') {
- this.photoTipsImages = [...this.photoTipsImages, ...urls]
- } else if (field === 'faceToFace') {
- this.faceToFaceImages = [...this.faceToFaceImages, ...urls]
- }
- } catch (error) {
- console.error('上传失败:', error)
- uni.$u.toast('上传失败')
- }
- },
- /**
- * 上传细节图
- */
- async handleUploadImage() {
- try {
- const filePaths = await imageUpload.chooseImage(9)
- const uploadResults = await imageUpload.uploadFiles(filePaths)
- await imageUpload.bindOrderFile(
- this.currentReceipt.clueId,
- this.currentReceipt.id,
- '3',
- uploadResults
- )
- this.loadDetailImages()
- } catch (error) {
- console.error('上传失败:', error)
- }
- },
- /**
- * 删除图片
- */
- async handleDeleteImage(item) {
- uni.showModal({
- title: '提示',
- content: '确定要删除这张图片吗?',
- success: async (res) => {
- if (res.confirm) {
- try {
- await imageUpload.deleteFile(item.id)
- this.loadDetailImages()
- } catch (error) {
- console.error('删除失败:', error)
- }
- }
- }
- })
- },
- /**
- * 复制所有细节图
- */
- async copyAllDetailImages() {
- const allUrls = this.detailImages.map(item => item.fileUrl)
- if (allUrls.length === 0) {
- uni.showToast({
- title: '没有图片可复制',
- icon: 'none'
- })
- return
- }
-
- uni.showModal({
- title: '保存图片',
- content: `是否将 ${allUrls.length} 张图片保存到本地相册?`,
- confirmText: '保存',
- success: (res) => {
- if (res.confirm) {
- imageUpload.saveImagesToLocal(allUrls)
- }
- }
- })
- },
- /**
- * 预览细节图
- */
- previewImageDetail(src) {
- const urlList = this.detailImages.map(item => item.fileUrl)
- uni.previewImage({
- urls: urlList,
- current: src
- })
- },
- /**
- * 预览师傅拍图技巧图片
- */
- previewPhotoTips(src) {
- uni.previewImage({
- urls: this.photoTipsImages,
- current: src
- })
- },
- /**
- * 预览到达客户面对面图片
- */
- previewFaceToFace(src) {
- uni.previewImage({
- urls: this.faceToFaceImages,
- current: src
- })
- },
- /**
- * 下一步
- */
- async handleNext() {
- // 保存核准价
- await uni.$u.api.updateReceiptForm({
- id: this.currentReceipt.id,
- sellingPrice: this.approvedPrice
- })
- // 保存跟进记录
- if (this.selectedCheckbox.includes('contactMaster')) {
- await uni.$u.api.addOrderFollow({
- orderId: this.orderId,
- content: `联系师傅;${this.formData.contactPhone}`
- })
- }
- if (this.selectedCheckbox.includes('photoTips')) {
- const urls = this.photoTipsImages.join(',')
- await uni.$u.api.addOrderFollow({
- orderId: this.orderId,
- content: `师傅拍图技巧;${this.formData.photoTips};${urls}`
- })
- }
- if (this.selectedCheckbox.includes('faceToFace')) {
- const urls = this.faceToFaceImages.join(',')
- await uni.$u.api.addOrderFollow({
- orderId: this.orderId,
- content: `到达客户面对面;${this.formData.faceToFaceNotes};${urls}`
- })
- }
- this.$emit('next', {
- nowPage: 'formTwo',
- form: {
- approvedPrice: this.approvedPrice,
- detailImages: this.detailImages
- }
- })
- }
- }
- }
- </script>
- <style scoped lang="scss">
- @import '../styles/common.scss';
- .page-two-container {
- @extend .page-container;
- padding-bottom: 100rpx;
- }
- .address-section {
- padding: 20rpx;
- }
- .checklist-card {
- margin-top: 20rpx;
- }
- .checklist-section {
- padding: 20rpx;
- }
- .checklist-item {
- padding: 16rpx 0;
- border-bottom: 1rpx solid map-get($colors, border);
-
- &:last-child {
- border-bottom: none;
- }
- }
- .checkbox-text-container {
- @include flex-center;
- margin-bottom: 12rpx;
- }
- .checklist-text {
- @include font-styles;
- margin-left: 16rpx;
- }
- .checklist-input,
- .checklist-textarea {
- margin-top: 12rpx;
- margin-left: 56rpx;
- width: calc(100% - 72rpx);
- border-radius: 8rpx;
- border: 1rpx solid #e5e7eb;
- padding: 12rpx 16rpx;
- }
- .upload-btn-container {
- margin-top: 16rpx;
- margin-left: 56rpx;
- }
- .upload-btn {
- display: inline-flex;
- align-items: center;
- gap: 12rpx;
- padding: 20rpx 40rpx;
- border-radius: 8rpx;
- background-color: map-get($colors, bg);
- border: 2rpx dashed map-get($colors, primary);
- color: map-get($colors, primary);
- cursor: pointer;
- }
- .upload-btn-text {
- @include font-styles($size: small, $weight: medium);
- }
- .image-list {
- margin-top: 16rpx;
- margin-left: 56rpx;
- display: flex;
- flex-wrap: wrap;
- gap: 16rpx;
- }
- .image-item {
- width: 120rpx;
- height: 120rpx;
- border-radius: 8rpx;
- overflow: hidden;
- cursor: pointer;
- transition: opacity 0.2s;
-
- &:active {
- opacity: 0.7;
- }
- }
- .image-thumb {
- width: 100%;
- height: 100%;
- object-fit: cover;
- }
- .price-card {
- margin-top: 20rpx;
- }
- .price-section {
- padding: 20rpx;
- }
- .price-picker-container {
- display: flex;
- flex-direction: column;
- align-items: center;
- padding: 20rpx 0;
- }
- .quick-actions {
- display: flex;
- justify-content: center;
- gap: 16rpx;
- margin: 5rpx 0;
- width: 100%;
- }
- .quick-btn {
- flex: 1;
- border-radius: 12rpx;
- font-size: 36rpx;
- padding: 10rpx 0;
- text-align: center;
- font-weight: 600;
- cursor: pointer;
-
- &.increase {
- background-color: #e6f7ed;
- color: #00b42a;
- }
-
- &.decrease {
- background-color: #fff1f0;
- color: #f53f3f;
- }
- }
- .number-box-container {
- display: flex;
- align-items: center;
- margin: 20rpx 0;
- width: 100%;
- justify-content: center;
- }
- .price-input-box {
- flex: 1;
- max-width: 800rpx;
- background-color: #f5f7fa;
- border: 2rpx solid #e5e7eb;
- border-radius: 12rpx;
- padding: 0rpx 24rpx;
- display: flex;
- align-items: center;
- justify-content: space-between;
- }
- .price-label {
- font-size: 30rpx;
- font-weight: 700;
- min-width: 120rpx;
- }
- .price-input {
- width: 100%;
- border: none;
- outline: none;
- background-color: transparent;
- text-align: right;
- font-size: 48rpx;
- font-weight: 600;
- padding: 0 10rpx;
- }
- .detail-image-card {
- margin-top: 20rpx;
- }
- .detail-image-section {
- padding: 20rpx;
- }
- .detail-image-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 20rpx;
- padding-bottom: 20rpx;
- border-bottom: 1rpx solid map-get($colors, border);
- }
- .detail-image-title {
- @include font-styles($size: content, $weight: bold, $color: primary);
- }
- .copy-btn {
- border-radius: 20rpx;
- border: 1rpx solid #007AFF;
- background-color: transparent;
- color: #007AFF;
- padding: 0 24rpx;
- height: 64rpx;
- line-height: 64rpx;
- cursor: pointer;
- }
- .detail-image-list {
- display: flex;
- flex-wrap: wrap;
- gap: 20rpx;
- }
- .detail-image-item {
- position: relative;
- width: 200rpx;
- height: 200rpx;
- }
- .detail-delete-btn {
- position: absolute;
- top: -10rpx;
- right: -10rpx;
- width: 40rpx;
- height: 40rpx;
- background-color: #ff4d4f;
- color: #fff;
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- font-weight: bold;
- z-index: 10;
- cursor: pointer;
- }
- .detail-upload-btn {
- width: 200rpx;
- height: 200rpx;
- border: 8rpx dashed #ddd;
- border-radius: 30rpx;
- display: flex;
- align-items: center;
- justify-content: center;
- background-color: #f9f9f9;
- cursor: pointer;
- }
- .next-btn {
- position: fixed;
- bottom: 10rpx;
- left: 2.5%;
- width: 95%;
- height: 80rpx;
- line-height: 80rpx;
- text-align: center;
- border-radius: 20rpx;
- }
- </style>
|