PageThree.vue 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144
  1. <template>
  2. <view class="page-three-container">
  3. <!-- 支付信息卡片 -->
  4. <view class="info-card">
  5. <view class="info-card-title">支付信息</view>
  6. <u-row class="info-row" justify="space-between">
  7. <u-col span="5.8">
  8. <view class="info-label">开户人</view>
  9. <u-input v-model="paymentInfo.customName" placeholder="请输入开户人姓名" class="info-input"
  10. @blur="handleCustomNameInput" />
  11. </u-col>
  12. <u-col span="5.8">
  13. <view class="info-label">银行名称</view>
  14. <u-input v-model="paymentInfo.bankName" placeholder="请输入银行名称" class="info-input"
  15. :disabled="isAlipayPayment" @blur="handleBankNameInput" />
  16. </u-col>
  17. </u-row>
  18. <u-row class="info-row">
  19. <u-col span="12">
  20. <view class="info-label">{{ bankAccountLabel }}</view>
  21. <u-input v-model="paymentInfo.bankAccount" :placeholder="bankAccountPlaceholder" class="info-input"
  22. :type="isAlipayPayment ? 'text' : 'number'" @input="handleBankAccountInput" />
  23. </u-col>
  24. </u-row>
  25. <u-row class="info-row">
  26. <u-col span="12">
  27. <view class="info-label">身份证号</view>
  28. <u-input v-model="paymentInfo.idNumber" placeholder="请输入身份证号" class="info-input"
  29. @blur="handleIdNumberInput" />
  30. </u-col>
  31. </u-row>
  32. <u-row class="info-row">
  33. <u-col span="12">
  34. <view class="info-label">支付方式选择</view>
  35. <u-radio-group iconPlacement="left" v-model="paymentMethodRadio" placement="column"
  36. @change="handlePaymentMethodRadioChange">
  37. <u-radio shape="circle" label="小葫芦线上支付" name="online" :customStyle="{marginBottom: '16rpx'}"/>
  38. <u-radio shape="circle" label="小葫芦线上支付宝" name="online_alipay" :customStyle="{marginBottom: '16rpx'}" />
  39. <u-radio shape="circle" label="线下支付" name="offline" :customStyle="{marginBottom: '16rpx'}"/>
  40. </u-radio-group>
  41. </u-col>
  42. </u-row>
  43. <u-row class="info-row">
  44. <u-col span="12">
  45. <view class="info-label">支付方式</view>
  46. <u-input :disabled="paymentMethodRadio === 'online'||paymentMethodRadio === 'online_alipay'" v-model="paymentMethod" placeholder="请输入支付方式"
  47. class="info-input" />
  48. </u-col>
  49. </u-row>
  50. </view>
  51. <!-- 高清实物图卡片 -->
  52. <view class="card-wrap">
  53. <view class="detail-image-section">
  54. <view class="detail-image-header">
  55. <text class="detail-image-title">高清实物图(拖拽排序)</text>
  56. <u-button type="primary" shape="circle" plain text="核价" size="small" class="pricing-btn" @click="handlePricing"></u-button>
  57. </view>
  58. <view class="detail-image-content">
  59. <view class="detail-image-list">
  60. <view v-for="(item, index) in displayImages" :key="item.id || `detail-${index}`" class="detail-image-item"
  61. :class="{
  62. 'dragging': draggingIndex === index,
  63. 'can-drop': canDropIndex === index && draggingIndex !== index
  64. }" :style="draggingIndex === index ? draggingStyle : ''" @touchstart.stop="onTouchStart($event, index)"
  65. @touchmove.stop="onTouchMove($event, index)" @touchend.stop="onTouchEnd">
  66. <PicComp :src="item.fileUrl" @needPreviewPic="previewImageDetail" />
  67. <view class="image-type-tag">{{ getImageType(index) }}</view>
  68. <view class="detail-delete-btn" @click.stop="handleHideImage(item, index)">
  69. ×
  70. </view>
  71. </view>
  72. <view class="detail-upload-btn" @click="handleUploadImage">
  73. <u-icon name="plus" size="40rpx" color="#999" />
  74. </view>
  75. </view>
  76. </view>
  77. </view>
  78. </view>
  79. <!-- 支付总额卡片 -->
  80. <view class="card-wrap payment-card">
  81. <view class="payment-section">
  82. <view class="payment-total-container">
  83. <text class="payment-label">支付总额</text>
  84. <view class="payment-amount-wrap">
  85. <u-input v-model="paymentAmount" class="payment-amount" type="number" decimal="2" prefix="¥" />
  86. </view>
  87. <view class="pay-now-btn-wrap">
  88. <u-button type="primary" shape="circle" plain text="立即支付" size="small" @click="handlePayNowClick"></u-button>
  89. </view>
  90. </view>
  91. <view class="payment-buttons-row">
  92. <view class="payment-button" @click="handleUnpaidClick">
  93. <u-icon name="star" size="40rpx" color="#ff9500" />
  94. <text class="button-text">未收</text>
  95. </view>
  96. <view class="payment-button" @click="handleFollowUpClick">
  97. <u-icon name="chat" size="40rpx" color="#108cff" />
  98. <text class="button-text">待跟进</text>
  99. </view>
  100. </view>
  101. </view>
  102. </view>
  103. <!-- 下一步按钮 -->
  104. <u-button class="next-btn" @click="handleNext" type="primary" size="middle">
  105. 下一步
  106. </u-button>
  107. <!-- 未收评级模态窗 -->
  108. <u-modal showCancelButton showConfirmButton @confirm="confirmUnpaid" @cancel="onUnpaidModalCancel"
  109. :show="unpaidModalVisible" title="未收评级">
  110. <view class="modal-content">
  111. <u-rate v-model="unpaidRating" :count="5" :size="50" active-color="#ff9500" />
  112. <view class="unpaid-benefit-fee-wrap">
  113. <view class="unpaid-benefit-fee-label">好处费</view>
  114. <u-input v-model="unpaidBenefitFee" class="unpaid-benefit-fee-input" type="number" :decimal="2"
  115. placeholder="请输入好处费(金额)" @input="handleUnpaidBenefitFeeInput" />
  116. </view>
  117. </view>
  118. </u-modal>
  119. <!-- 待跟进模态窗 -->
  120. <u-modal showCancelButton showConfirmButton @confirm="confirmFollowUp" @cancel="followUpModalVisible = false"
  121. :show="followUpModalVisible" title="填写跟进细节">
  122. <view class="modal-content">
  123. <u--textarea v-model="followUpNotes" placeholder="请输入情况" confirm-type="done"
  124. style="width: 100%; margin-bottom: 30rpx;" />
  125. </view>
  126. </u-modal>
  127. <!-- 确认支付模态窗 -->
  128. <u-modal showCancelButton showConfirmButton @confirm="confirmRiskWarning" @cancel="riskWarningModalVisible = false"
  129. confirmText="确认并继续付款" :show="riskWarningModalVisible" title="付款警示" :content="'湖南耒阳地区身份证号请确认!'">
  130. </u-modal>
  131. <!-- 核价对话框 -->
  132. <add-inquiry-dialog ref="pricingDialog" :clueId="pricingClueId" :editOrAdd="pricingEditOrAdd"
  133. :editInfo="pricingEditInfo" :type="2" />
  134. <u-modal :show="payNowModalVisible" title="确认支付信息" :showConfirmButton="false">
  135. <view class="modal-content">
  136. <view class="payment-amount-display">¥{{ paymentAmount }}</view>
  137. <view class="payment-info-section">
  138. <view class="info-item">
  139. <text class="info-label">收款姓名:</text>
  140. <text class="info-value">{{ paymentInfo.customName || '未填写' }}</text>
  141. </view>
  142. <view class="info-item">
  143. <text class="info-label">开户银行:</text>
  144. <text class="info-value">{{ paymentInfo.bankName || '未填写' }}</text>
  145. </view>
  146. <view class="info-item">
  147. <text class="info-label">银行卡号:</text>
  148. <text class="info-value">{{ paymentInfo.bankAccount || '未填写' }}</text>
  149. </view>
  150. <view class="info-item">
  151. <text class="info-label">支付方式:</text>
  152. <text class="info-value">{{ paymentMethod || '未填写' }}</text>
  153. </view>
  154. </view>
  155. <u-button type="primary" size="large" @click="confirmTransfer" style="margin-top: 40rpx;">
  156. 确认转账
  157. </u-button>
  158. <u-button type="primary" :plain="true" @click="payNowModalVisible = false" size="large"
  159. style="margin-top: 20rpx;">
  160. 取消
  161. </u-button>
  162. </view>
  163. </u-modal>
  164. </view>
  165. </template>
  166. <script>
  167. import PicComp from './PicComp.vue'
  168. import imageUpload from '../utils/imageUpload.js'
  169. import addInquiryDialog from '@/components/add-inquiry-dialog/index.vue'
  170. export default {
  171. name: 'PageThree',
  172. components: {
  173. PicComp,
  174. addInquiryDialog
  175. },
  176. props: {
  177. orderDetail: {
  178. type: Object,
  179. default: () => ({})
  180. },
  181. orderId: {
  182. type: String,
  183. default: ''
  184. },
  185. currentReceipt: {
  186. type: Object,
  187. default: () => ({})
  188. }
  189. },
  190. data() {
  191. return {
  192. paymentInfo: {
  193. customName: '',
  194. bankName: '',
  195. bankAccount: '',
  196. idNumber: ''
  197. },
  198. paymentMethodRadio: 'offline',
  199. paymentMethod: '',
  200. paymentAmount: '0.00',
  201. detailImages: [],
  202. // 拖拽相关
  203. draggingIndex: -1,
  204. canDropIndex: -1,
  205. startX: 0,
  206. startY: 0,
  207. currentX: 0,
  208. currentY: 0,
  209. // 模态窗
  210. unpaidModalVisible: false,
  211. followUpModalVisible: false,
  212. payNowModalVisible: false,
  213. riskWarningModalVisible: false,
  214. unpaidRating: 0,
  215. unpaidBenefitFee: '',
  216. followUpNotes: '',
  217. // 核价相关
  218. pricingClueId: '',
  219. pricingEditOrAdd: 'receptFormAdd',
  220. pricingEditInfo: {}
  221. }
  222. },
  223. computed: {
  224. // 显示前6个图片用于拖拽
  225. displayImages() {
  226. return this.detailImages.slice(0, 6)
  227. },
  228. // 拖拽时的样式
  229. draggingStyle() {
  230. if (this.draggingIndex === -1) return ''
  231. return {
  232. transform: `translate(${this.currentX - this.startX}px, ${this.currentY - this.startY}px)`,
  233. zIndex: 1000
  234. }
  235. },
  236. // 是否为支付宝支付
  237. isAlipayPayment() {
  238. return this.paymentMethodRadio === 'online_alipay'
  239. },
  240. // 银行账号标签
  241. bankAccountLabel() {
  242. return this.isAlipayPayment ? '支付宝账号' : '银行账号'
  243. },
  244. // 银行账号占位符
  245. bankAccountPlaceholder() {
  246. return this.isAlipayPayment ? '请输入支付宝账号' : '请输入银行账号'
  247. }
  248. },
  249. watch: {
  250. // 支付信息已保存到 receiptForm,不再从 orderDetail(sendForm) 读取
  251. currentReceipt: {
  252. handler(newVal, oldVal) {
  253. if (newVal) {
  254. this.paymentAmount = newVal.tableFee || '0.00'
  255. // 从回单表 receiptForm 回填支付信息(与 PC 端一致)
  256. this.paymentInfo.customName = newVal.customName || ''
  257. this.paymentInfo.bankName = newVal.bankName || ''
  258. this.paymentInfo.bankAccount = newVal.bankCardNumber || ''
  259. this.paymentInfo.idNumber = newVal.idCard || ''
  260. this.initPaymentMethod(newVal.paymentMethod)
  261. // 只有当 receipt id 变化时才重新加载图片,避免 fileIds 更新时重新加载
  262. if (!oldVal || oldVal.id !== newVal.id) {
  263. this.loadDetailImages()
  264. }
  265. }
  266. },
  267. deep: true,
  268. immediate: true
  269. }
  270. },
  271. methods: {
  272. /**
  273. * 刷新图片列表(供父组件调用)
  274. */
  275. async refreshImageList() {
  276. await this.loadDetailImages()
  277. },
  278. /**
  279. * 加载细节图
  280. */
  281. async loadDetailImages() {
  282. if (!this.currentReceipt.id || !this.orderDetail.itemBrand) return
  283. try {
  284. const list = await imageUpload.getFileList(
  285. '2',
  286. '3',
  287. this.currentReceipt.id,
  288. this.orderDetail.itemBrand,
  289. this.currentReceipt.clueId
  290. )
  291. // 按照 fileIds 排序
  292. if (this.currentReceipt.fileIds && list && list.length > 0) {
  293. const sortedIds = this.currentReceipt.fileIds.split(',')
  294. list.sort((a, b) => {
  295. const indexA = sortedIds.indexOf(a.id)
  296. const indexB = sortedIds.indexOf(b.id)
  297. // 如果都不在列表中,保持原顺序
  298. if (indexA === -1 && indexB === -1) return 0
  299. // 如果 a 不在列表中,放到后面
  300. if (indexA === -1) return 1
  301. // 如果 b 不在列表中,放到后面
  302. if (indexB === -1) return -1
  303. // 都在列表中,按 index 排序
  304. return indexA - indexB
  305. })
  306. }
  307. this.detailImages = list || []
  308. } catch (error) {
  309. console.error('加载细节图失败:', error)
  310. }
  311. },
  312. /**
  313. * 初始化支付方式
  314. */
  315. initPaymentMethod(value) {
  316. if (value === '小葫芦线上支付') {
  317. this.paymentMethod = '小葫芦线上支付'
  318. this.paymentMethodRadio = 'online'
  319. } else if (value === '小葫芦线上支付宝') {
  320. this.paymentMethod = '小葫芦线上支付宝'
  321. this.paymentMethodRadio = 'online_alipay'
  322. this.paymentInfo.bankName = '支付宝'
  323. } else {
  324. this.paymentMethod = value || ''
  325. this.paymentMethodRadio = 'offline'
  326. }
  327. },
  328. /**
  329. * 支付方式选择改变
  330. */
  331. handlePaymentMethodRadioChange(value) {
  332. if (value === 'online') {
  333. this.paymentMethod = '小葫芦线上支付'
  334. // 恢复银行名称输入框
  335. if (this.paymentInfo.bankName === '支付宝') {
  336. this.paymentInfo.bankName = ''
  337. }
  338. } else if (value === 'online_alipay') {
  339. this.paymentMethod = '小葫芦线上支付宝'
  340. // 自动设置银行名称为支付宝
  341. this.paymentInfo.bankName = '支付宝'
  342. } else {
  343. this.paymentMethod = ''
  344. // 恢复银行名称输入框
  345. if (this.paymentInfo.bankName === '支付宝') {
  346. this.paymentInfo.bankName = ''
  347. }
  348. }
  349. },
  350. /**
  351. * 开户人输入处理 - 只允许中文
  352. */
  353. handleCustomNameInput(value) {
  354. // 只保留中文字符(包括中文标点)
  355. const chineseReg = /[^\u4e00-\u9fa5]/g
  356. this.paymentInfo.customName = String(value || '').replace(chineseReg, '')
  357. },
  358. /**
  359. * 银行名称输入处理 - 只允许中文
  360. */
  361. handleBankNameInput(value) {
  362. // 只保留中文字符(包括中文标点)
  363. const chineseReg = /[^\u4e00-\u9fa5]/g
  364. this.paymentInfo.bankName = String(value || '').replace(chineseReg, '')
  365. },
  366. /**
  367. * 银行账号输入处理 - 只允许数字
  368. */
  369. handleBankAccountInput(value) {
  370. // 只保留数字
  371. const numberReg = /[^\d]/g
  372. this.paymentInfo.bankAccount = String(value || '').replace(numberReg, '')
  373. },
  374. /**
  375. * 身份证号输入处理 - 允许数字和X
  376. */
  377. handleIdNumberInput(value) {
  378. // 只保留数字和X
  379. const reg = /[^\dxX]/g
  380. this.paymentInfo.idNumber = String(value || '').replace(reg, '')
  381. },
  382. /**
  383. * 获取图片类型
  384. */
  385. getImageType(index) {
  386. const types = ['正面', '反面', '侧面', '扣子', '编号']
  387. return types[index] || `细节${index - 4}`
  388. },
  389. /**
  390. * 触摸开始
  391. */
  392. onTouchStart(event, index) {
  393. if (this.draggingIndex !== -1) return
  394. this.draggingIndex = index
  395. const touch = event.touches[0]
  396. this.startX = touch.clientX
  397. this.startY = touch.clientY
  398. this.currentX = touch.clientX
  399. this.currentY = touch.clientY
  400. },
  401. /**
  402. * 触摸移动
  403. */
  404. onTouchMove(event, index) {
  405. if (this.draggingIndex === -1 || this.draggingIndex !== index) return
  406. const touch = event.touches[0]
  407. this.currentX = touch.clientX
  408. this.currentY = touch.clientY
  409. this.findTargetIndex(touch.clientX, touch.clientY)
  410. },
  411. /**
  412. * 触摸结束
  413. */
  414. async onTouchEnd() {
  415. if (this.draggingIndex === -1) return
  416. if (this.canDropIndex !== -1 && this.canDropIndex !== this.draggingIndex) {
  417. // 交换位置
  418. const images = [...this.detailImages]
  419. const temp = images[this.draggingIndex]
  420. images[this.draggingIndex] = images[this.canDropIndex]
  421. images[this.canDropIndex] = temp
  422. this.detailImages = images
  423. }
  424. this.resetDragState()
  425. //每次拖拽结束后把新的顺序传送给接口
  426. const fileIds = this.detailImages.map(item => item.id).join(',')
  427. await uni.$u.api.updateReceiptForm({
  428. id: this.currentReceipt.id,
  429. fileIds: fileIds
  430. })
  431. this.$emit('update-file-ids', fileIds)
  432. },
  433. /**
  434. * 查找目标索引
  435. */
  436. findTargetIndex(x, y) {
  437. const query = uni.createSelectorQuery().in(this)
  438. query.selectAll('.detail-image-item').boundingClientRect((rects) => {
  439. if (!rects || rects.length === 0) return
  440. let targetIndex = -1
  441. for (let i = 0; i < rects.length; i++) {
  442. if (i === this.draggingIndex) continue
  443. const rect = rects[i]
  444. if (x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom) {
  445. targetIndex = i
  446. break
  447. }
  448. }
  449. this.canDropIndex = targetIndex
  450. }).exec()
  451. },
  452. /**
  453. * 重置拖拽状态
  454. */
  455. resetDragState() {
  456. this.draggingIndex = -1
  457. this.canDropIndex = -1
  458. this.startX = 0
  459. this.startY = 0
  460. this.currentX = 0
  461. this.currentY = 0
  462. },
  463. /**
  464. * 上传图片
  465. */
  466. async handleUploadImage() {
  467. try {
  468. const filePaths = await imageUpload.chooseImage(9)
  469. const uploadResults = await imageUpload.uploadFiles(filePaths)
  470. await imageUpload.bindOrderFile(
  471. this.currentReceipt.clueId,
  472. this.currentReceipt.id,
  473. '3',
  474. uploadResults
  475. )
  476. await this.loadDetailImages()
  477. //上传新图片完成之后也要把新数据给接口
  478. const fileIds = this.detailImages.map(item => item.id).join(',')
  479. await uni.$u.api.updateReceiptForm({
  480. id: this.currentReceipt.id,
  481. fileIds: fileIds
  482. })
  483. this.$emit('update-file-ids', fileIds)
  484. } catch (error) {
  485. console.error('上传失败:', error)
  486. }
  487. },
  488. /**
  489. * 隐藏图片
  490. */
  491. async handleHideImage(item, index) {
  492. const itemIndex = this.detailImages.findIndex(img => img.id === item.id || img.fileUrl === item.fileUrl)
  493. if (itemIndex !== -1) {
  494. this.detailImages.splice(itemIndex, 1)
  495. // 更新 fileIds
  496. const fileIds = this.detailImages.map(item => item.id).join(',')
  497. try {
  498. await uni.$u.api.updateReceiptForm({
  499. id: this.currentReceipt.id,
  500. fileIds: fileIds
  501. })
  502. this.$emit('update-file-ids', fileIds)
  503. uni.$u.toast('图片已隐藏')
  504. } catch (error) {
  505. console.error('更新失败:', error)
  506. }
  507. }
  508. },
  509. /**
  510. * 预览图片
  511. */
  512. previewImageDetail(src) {
  513. const urlList = this.detailImages.map(item => item.fileUrl)
  514. uni.previewImage({
  515. urls: urlList,
  516. current: src
  517. })
  518. },
  519. /**
  520. * 未收点击
  521. */
  522. handleUnpaidClick() {
  523. this.unpaidBenefitFee = ''
  524. this.unpaidModalVisible = true
  525. },
  526. /**
  527. * 未收弹窗取消 - 重置好处费
  528. */
  529. onUnpaidModalCancel() {
  530. this.unpaidModalVisible = false
  531. this.unpaidBenefitFee = ''
  532. },
  533. /**
  534. * 好处费输入 - 限制仅可输入金额(数字、最多两位小数)
  535. */
  536. handleUnpaidBenefitFeeInput(e) {
  537. const value = (e && e.detail && e.detail.value !== undefined) ? e.detail.value : e
  538. let val = String(value ?? '').trim()
  539. if (!val) {
  540. this.unpaidBenefitFee = ''
  541. return
  542. }
  543. // 只保留数字和第一个小数点,且小数点后最多两位
  544. const parts = val.replace(/[^\d.]/g, '').split('.')
  545. if (parts.length > 2) {
  546. val = parts[0] + '.' + parts.slice(1).join('')
  547. }
  548. if (parts.length === 2 && parts[1].length > 2) {
  549. val = parts[0] + '.' + parts[1].slice(0, 2)
  550. }
  551. this.unpaidBenefitFee = val
  552. },
  553. /**
  554. * 待跟进点击
  555. */
  556. handleFollowUpClick() {
  557. this.followUpModalVisible = true
  558. },
  559. /**
  560. * 立即支付点击
  561. */
  562. async handlePayNowClick() {
  563. // 与 PC 端一致:支付信息通过 updateReceiptForm 保存;后端需 sendFormId 才能更新发单表的开户人/银行/身份证等
  564. await uni.$u.api.updateReceiptForm({
  565. id: this.currentReceipt.id,
  566. sendFormId: this.currentReceipt.sendFormId || this.orderDetail.id,
  567. tableFee: this.paymentAmount,
  568. fileIds: this.detailImages.map(item => item.id).join(','),
  569. customName: this.paymentInfo.customName || '',
  570. bankName: this.paymentInfo.bankName || '',
  571. bankCardNumber: this.paymentInfo.bankAccount || '',
  572. idCard: this.paymentInfo.idNumber || ''
  573. })
  574. if (!this.paymentInfo.customName || !this.paymentInfo.bankName ||
  575. !this.paymentInfo.bankAccount || !this.paymentInfo.idNumber || !this.paymentMethod) {
  576. uni.$u.toast('请填写完整的支付信息')
  577. return
  578. }
  579. if (this.paymentAmount <= 0) {
  580. uni.$u.toast('请填写正确的支付总额')
  581. return
  582. }
  583. //判断当前账号是否敏感地区
  584. if (String(this.paymentInfo.idNumber).startsWith('430481')) {
  585. this.riskWarningModalVisible = true
  586. return
  587. }
  588. // 线下支付:不弹确认框,直接更新支付方式并进入下一步(后端需 sendFormId 更新发单表支付方式)
  589. if (this.paymentMethodRadio === 'offline') {
  590. await uni.$u.api.updateReceiptForm({
  591. id: this.currentReceipt.id,
  592. sendFormId: this.currentReceipt.sendFormId || this.orderDetail.id,
  593. paymentMethod: this.paymentMethod || ''
  594. })
  595. this.handleNext()
  596. return
  597. }
  598. this.payNowModalVisible = true
  599. },
  600. /**
  601. * 确认风险警示
  602. */
  603. async confirmRiskWarning() {
  604. this.riskWarningModalVisible = false
  605. // 线下支付:不弹确认框,直接更新支付方式并进入下一步(后端需 sendFormId 更新发单表支付方式)
  606. if (this.paymentMethodRadio === 'offline') {
  607. await uni.$u.api.updateReceiptForm({
  608. id: this.currentReceipt.id,
  609. sendFormId: this.currentReceipt.sendFormId || this.orderDetail.id,
  610. paymentMethod: this.paymentMethod || ''
  611. })
  612. this.handleNext()
  613. return
  614. }
  615. this.payNowModalVisible = true
  616. },
  617. /**
  618. * 确认转账
  619. */
  620. async confirmTransfer() {
  621. this.payNowModalVisible = false
  622. // 支付方式保存(后端需 sendFormId 更新发单表)
  623. await uni.$u.api.updateReceiptForm({
  624. id: this.currentReceipt.id,
  625. sendFormId: this.currentReceipt.sendFormId || this.orderDetail.id,
  626. paymentMethod: this.paymentMethod || ''
  627. })
  628. // 根据 radio 选择判断:线上支付(online / online_alipay)需调用支付接口,线下则直接下一步
  629. if (this.paymentMethodRadio === 'online' || this.paymentMethodRadio === 'online_alipay') {
  630. this.$emit('confirm-pay')
  631. } else {
  632. this.handleNext()
  633. }
  634. },
  635. /**
  636. * 确认未收
  637. */
  638. async confirmUnpaid() {
  639. try {
  640. // 若填写了好处费,先更新回单表
  641. if (this.currentReceipt?.id && (this.unpaidBenefitFee !== '' && this.unpaidBenefitFee != null)) {
  642. await uni.$u.api.updateReceiptForm({
  643. id: this.currentReceipt.id,
  644. benefitFee: this.unpaidBenefitFee
  645. })
  646. }
  647. await uni.$u.api.addOrderFollow({
  648. orderId: this.orderId,
  649. content: `未收评分_${this.unpaidRating}`
  650. })
  651. await uni.$u.api.oderForm({
  652. status: '4',
  653. id: this.orderId
  654. })
  655. uni.$u.toast('提交未收评级成功')
  656. this.unpaidModalVisible = false
  657. this.unpaidBenefitFee = ''
  658. // 刷新当前收单数据,以便第四页好处费等字段同步
  659. this.$emit('price-updated')
  660. } catch (error) {
  661. console.error('提交失败:', error)
  662. uni.$u.toast('提交失败')
  663. }
  664. },
  665. /**
  666. * 确认跟进
  667. */
  668. async confirmFollowUp() {
  669. try {
  670. await uni.$u.api.addOrderFollow({
  671. orderId: this.orderId,
  672. content: `待跟进_${this.followUpNotes}`
  673. })
  674. uni.$u.toast('提交待跟进记录成功')
  675. this.followUpModalVisible = false
  676. this.followUpNotes = ''
  677. } catch (error) {
  678. console.error('提交失败:', error)
  679. uni.$u.toast('提交失败')
  680. }
  681. },
  682. /**
  683. * 下一步
  684. */
  685. async handleNext() {
  686. // 与 PC 端一致:支付信息通过 updateReceiptForm 保存;后端需 sendFormId 才能更新发单表的开户人/银行/身份证等
  687. await uni.$u.api.updateReceiptForm({
  688. id: this.currentReceipt.id,
  689. sendFormId: this.currentReceipt.sendFormId || this.orderDetail.id,
  690. tableFee: this.paymentAmount,
  691. fileIds: this.detailImages.map(item => item.id).join(','),
  692. customName: this.paymentInfo.customName || '',
  693. bankName: this.paymentInfo.bankName || '',
  694. bankCardNumber: this.paymentInfo.bankAccount || '',
  695. idCard: this.paymentInfo.idNumber || '',
  696. // paymentMethod: this.paymentMethod || ''
  697. })
  698. this.$emit('save', {
  699. nowPage: 'formThree',
  700. form: {
  701. ...this.paymentInfo
  702. },
  703. fileIds: this.detailImages.map(item => item.id).join(',')
  704. })
  705. this.$emit('next', {
  706. nowPage: 'formThree',
  707. form: {
  708. ...this.paymentInfo
  709. }
  710. })
  711. },
  712. /**
  713. * 核价
  714. */
  715. async handlePricing() {
  716. const brandList = await this.$getDicts("crm_form_brand");
  717. if (!this.currentReceipt || !this.currentReceipt.clueId) {
  718. uni.$u.toast('缺少线索ID')
  719. return
  720. }
  721. if (!this.currentReceipt.brand) {
  722. uni.$u.toast('缺少品牌信息')
  723. return
  724. }
  725. if (!this.displayImages || this.displayImages.length === 0) {
  726. uni.$u.toast('请先上传图片')
  727. return
  728. }
  729. //我这里询价要用receiptid来询价
  730. this.pricingClueId = this.currentReceipt.id
  731. // brand 是品牌的 id(dictValue),itemBrand 是品牌的 label(dictLabel)
  732. const brandDictLabel = this.currentReceipt.brand
  733. const brandDictValue = brandList.find(item => item.dictLabel === this.currentReceipt.brand).dictValue;
  734. // 将图片转换为 imgsUrl 格式
  735. const imgsUrl = this.displayImages.map(item => item.fileUrl).filter(url => url)
  736. // 检查是否已有核价记录
  737. try {
  738. const data = {
  739. clueId: this.currentReceipt.clueId,
  740. type: 2
  741. }
  742. const res = await uni.$u.api.inquiryDetail(data)
  743. if (res.code === 200 && res.data) {
  744. // 编辑模式:保留原有数据,但更新品牌和图片
  745. this.pricingEditOrAdd = 'editForm'
  746. this.pricingEditInfo = {
  747. ...res.data,
  748. dictLabel: brandDictLabel || res.data.dictLabel,
  749. dictValue: brandDictValue || res.data.dictValue,
  750. imgsUrl: imgsUrl.length > 0 ? imgsUrl : (res.data.imgsUrl || [])
  751. }
  752. this.$nextTick(() => {
  753. this.$refs.pricingDialog.showDialog()
  754. })
  755. } else {
  756. // 新增模式:设置品牌和图片
  757. this.pricingEditOrAdd = 'editForm'
  758. this.pricingEditInfo = {
  759. dictLabel: brandDictLabel,
  760. dictValue: brandDictValue,
  761. imgsUrl: imgsUrl,
  762. model: this.currentReceipt.model,
  763. code: '',
  764. price: ''
  765. }
  766. this.$nextTick(() => {
  767. this.$refs.pricingDialog.showDialog()
  768. })
  769. }
  770. } catch (error) {
  771. // 如果没有记录,则新增
  772. this.pricingEditOrAdd = 'receptFormAdd'
  773. this.pricingEditInfo = {
  774. dictLabel: brandDictLabel,
  775. dictValue: brandDictValue,
  776. imgsUrl: imgsUrl,
  777. model: this.currentReceipt.model,
  778. code: '',
  779. price: ''
  780. }
  781. this.$nextTick(() => {
  782. this.$refs.pricingDialog.showDialog()
  783. })
  784. }
  785. },
  786. }
  787. }
  788. </script>
  789. <style scoped lang="scss">
  790. @import '../styles/common.scss';
  791. .page-three-container {
  792. @extend .page-container;
  793. padding-bottom: 100rpx;
  794. }
  795. .info-card {
  796. @extend .card-wrap;
  797. padding: 20rpx;
  798. margin-top: 20rpx;
  799. box-sizing: border-box;
  800. width: 100%;
  801. max-width: 100%;
  802. }
  803. .info-card-title {
  804. @include font-styles($size: title, $weight: bold, $color: primary);
  805. margin-bottom: 25rpx;
  806. padding-bottom: 15rpx;
  807. border-bottom: 1rpx solid map-get($colors, border);
  808. }
  809. .info-row {
  810. margin-bottom: 20rpx;
  811. }
  812. .info-label {
  813. @include font-styles($size: tiny, $weight: regular, $color: tertiary);
  814. margin-bottom: 8rpx;
  815. display: block;
  816. }
  817. .info-input {
  818. border-radius: 8rpx;
  819. border: 1rpx solid #e5e7eb;
  820. padding: 12rpx 16rpx;
  821. width: 100%;
  822. box-sizing: border-box;
  823. }
  824. .detail-image-section {
  825. padding: 20rpx;
  826. }
  827. .detail-image-header {
  828. display: grid;
  829. grid-template-columns: 1fr auto;
  830. margin-bottom: 20rpx;
  831. padding-bottom: 20rpx;
  832. padding-right: 100rpx; /* 右侧留空,避免被固定页面导航遮挡,使整行(含核价按钮)左移 */
  833. border-bottom: 1rpx solid map-get($colors, border);
  834. }
  835. .detail-image-title {
  836. @include font-styles($size: content, $weight: bold, $color: primary);
  837. }
  838. .detail-image-list {
  839. display: flex;
  840. flex-wrap: wrap;
  841. gap: 20rpx;
  842. position: relative;
  843. }
  844. .detail-image-item {
  845. position: relative;
  846. width: 200rpx;
  847. height: 200rpx;
  848. touch-action: none;
  849. transition: transform 0.2s ease, opacity 0.2s ease;
  850. z-index: 1;
  851. box-sizing: border-box;
  852. &.dragging {
  853. opacity: 0.6;
  854. transform: scale(1.05);
  855. z-index: 999;
  856. box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.2);
  857. }
  858. &.can-drop {
  859. outline: 2rpx dashed #108cff;
  860. outline-offset: -2rpx;
  861. background-color: rgba(16, 140, 255, 0.1);
  862. border-radius: 8rpx;
  863. }
  864. }
  865. .image-type-tag {
  866. position: absolute;
  867. top: 10rpx;
  868. left: 10rpx;
  869. background-color: rgba(0, 0, 0, 0.6);
  870. color: white;
  871. padding: 5rpx 10rpx;
  872. border-radius: 12rpx;
  873. font-size: 22rpx;
  874. z-index: 1;
  875. }
  876. .detail-delete-btn {
  877. position: absolute;
  878. top: -10rpx;
  879. right: -10rpx;
  880. width: 40rpx;
  881. height: 40rpx;
  882. background-color: #ff4d4f;
  883. color: #fff;
  884. border-radius: 50%;
  885. display: flex;
  886. align-items: center;
  887. justify-content: center;
  888. font-weight: bold;
  889. z-index: 10;
  890. cursor: pointer;
  891. }
  892. .detail-upload-btn {
  893. width: 200rpx;
  894. height: 200rpx;
  895. border: 8rpx dashed #ddd;
  896. border-radius: 30rpx;
  897. display: flex;
  898. align-items: center;
  899. justify-content: center;
  900. background-color: #f9f9f9;
  901. cursor: pointer;
  902. }
  903. .pricing-button {
  904. margin-top: 0;
  905. }
  906. .payment-card {
  907. margin-top: 20rpx;
  908. }
  909. .payment-section {
  910. padding: 20rpx;
  911. }
  912. .payment-total-container {
  913. background-color: #f5f7fa;
  914. border: 2rpx solid #e5e7eb;
  915. border-radius: 12rpx;
  916. padding: 0rpx 30rpx;
  917. display: flex;
  918. align-items: center;
  919. justify-content: space-between;
  920. margin-bottom: 24rpx;
  921. }
  922. .payment-label {
  923. font-size: 36rpx;
  924. font-weight: 700;
  925. min-width: 140rpx;
  926. flex-shrink: 0;
  927. }
  928. .payment-amount-wrap {
  929. flex: 1;
  930. display: flex;
  931. justify-content: center;
  932. align-items: center;
  933. min-width: 0;
  934. }
  935. .payment-amount-wrap .payment-amount {
  936. font-size: 48rpx;
  937. font-weight: 600;
  938. color: #f53f3f;
  939. width: 100%;
  940. max-width: 240rpx;
  941. border: none;
  942. text-align: center;
  943. }
  944. /* 让 u-input 内部输入框文字居中 */
  945. .payment-amount-wrap :deep(.u-input__content__field-wrapper__field),
  946. .payment-amount-wrap :deep(input) {
  947. text-align: center !important;
  948. }
  949. .pay-now-btn-wrap {
  950. flex-shrink: 0;
  951. display: inline-flex;
  952. }
  953. .payment-total-container .pay-now-btn-wrap .u-button,
  954. .payment-total-container .pay-now-btn-wrap .u-btn {
  955. width: auto !important;
  956. min-width: 0 !important;
  957. flex: none !important;
  958. }
  959. .payment-buttons-row {
  960. display: flex;
  961. gap: 20rpx;
  962. margin-bottom: 24rpx;
  963. }
  964. .payment-button {
  965. flex: 1;
  966. border-radius: 12rpx;
  967. padding: 5rpx 0;
  968. display: flex;
  969. flex-direction: column;
  970. align-items: center;
  971. justify-content: center;
  972. background-color: #f5f7fa;
  973. border: 2rpx solid #e5e7eb;
  974. cursor: pointer;
  975. gap: 12rpx;
  976. }
  977. .button-text {
  978. font-size: 28rpx;
  979. font-weight: 500;
  980. color: inherit;
  981. }
  982. .modal-content {
  983. width: 100%;
  984. padding: 40rpx 20rpx;
  985. display: flex;
  986. flex-direction: column;
  987. align-items: center;
  988. }
  989. .unpaid-benefit-fee-wrap {
  990. width: 100%;
  991. margin-top: 32rpx;
  992. display: flex;
  993. flex-direction: column;
  994. align-items: flex-start;
  995. }
  996. .unpaid-benefit-fee-label {
  997. font-size: 28rpx;
  998. color: #333;
  999. margin-bottom: 16rpx;
  1000. }
  1001. .unpaid-benefit-fee-input {
  1002. width: 100%;
  1003. border: 2rpx solid #e5e7eb;
  1004. border-radius: 12rpx;
  1005. padding: 20rpx 24rpx;
  1006. box-sizing: border-box;
  1007. }
  1008. .payment-amount-display {
  1009. font-size: 64rpx;
  1010. font-weight: bold;
  1011. color: #108cff;
  1012. text-align: center;
  1013. margin-bottom: 40rpx;
  1014. width: 100%;
  1015. }
  1016. .payment-info-section {
  1017. width: 100%;
  1018. background-color: #f5f7fa;
  1019. border: 2rpx solid #e5e7eb;
  1020. border-radius: 12rpx;
  1021. padding: 30rpx 20rpx;
  1022. margin-bottom: 40rpx;
  1023. }
  1024. .info-item {
  1025. display: flex;
  1026. margin-bottom: 20rpx;
  1027. align-items: center;
  1028. justify-content: space-between;
  1029. &:last-child {
  1030. margin-bottom: 0;
  1031. }
  1032. }
  1033. .next-btn {
  1034. position: fixed;
  1035. bottom: 10rpx;
  1036. left: 2.5%;
  1037. width: 95%;
  1038. height: 80rpx;
  1039. line-height: 80rpx;
  1040. text-align: center;
  1041. border-radius: 20rpx;
  1042. z-index: 1000;
  1043. }
  1044. </style>