PageThree.vue 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154
  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 端一致:先做必填校验
  564. if (this.paymentAmount <= 0) {
  565. uni.$u.toast('请填写正确的支付总额')
  566. return
  567. }
  568. // 支付方式必填(与 PC 一致)
  569. if (!this.paymentMethod) {
  570. uni.$u.toast('请填写支付方式')
  571. return
  572. }
  573. // 仅线上支付时校验开户人/银行/身份证;线下支付不校验(与 PC 一致)
  574. if (this.paymentMethodRadio === 'online' || this.paymentMethodRadio === 'online_alipay') {
  575. if (!this.paymentInfo.customName || !this.paymentInfo.bankName ||
  576. !this.paymentInfo.bankAccount || !this.paymentInfo.idNumber) {
  577. uni.$u.toast('请填写完整的开户人信息、身份证号、银行名称、银行卡号')
  578. return
  579. }
  580. }
  581. // 保存支付信息到回单表(后端需 sendFormId 才能更新发单表的开户人/银行/身份证等)
  582. await uni.$u.api.updateReceiptForm({
  583. id: this.currentReceipt.id,
  584. sendFormId: this.currentReceipt.sendFormId || this.orderDetail.id,
  585. tableFee: this.paymentAmount,
  586. fileIds: this.detailImages.map(item => item.id).join(','),
  587. customName: this.paymentInfo.customName || '',
  588. bankName: this.paymentInfo.bankName || '',
  589. bankCardNumber: this.paymentInfo.bankAccount || '',
  590. idCard: this.paymentInfo.idNumber || ''
  591. })
  592. // 湖南耒阳身份证号确认(仅当已填写身份证时校验,与 PC 一致)
  593. if (this.paymentInfo.idNumber && String(this.paymentInfo.idNumber).startsWith('430481')) {
  594. this.riskWarningModalVisible = true
  595. return
  596. }
  597. // 线下支付:不弹确认框,直接更新支付方式并进入下一步(后端需 sendFormId 更新发单表支付方式)
  598. if (this.paymentMethodRadio === 'offline') {
  599. await uni.$u.api.updateReceiptForm({
  600. id: this.currentReceipt.id,
  601. sendFormId: this.currentReceipt.sendFormId || this.orderDetail.id,
  602. paymentMethod: this.paymentMethod || ''
  603. })
  604. this.handleNext()
  605. return
  606. }
  607. this.payNowModalVisible = true
  608. },
  609. /**
  610. * 确认风险警示
  611. */
  612. async confirmRiskWarning() {
  613. this.riskWarningModalVisible = false
  614. // 线下支付:不弹确认框,直接更新支付方式并进入下一步(后端需 sendFormId 更新发单表支付方式)
  615. if (this.paymentMethodRadio === 'offline') {
  616. await uni.$u.api.updateReceiptForm({
  617. id: this.currentReceipt.id,
  618. sendFormId: this.currentReceipt.sendFormId || this.orderDetail.id,
  619. paymentMethod: this.paymentMethod || ''
  620. })
  621. this.handleNext()
  622. return
  623. }
  624. this.payNowModalVisible = true
  625. },
  626. /**
  627. * 确认转账
  628. */
  629. async confirmTransfer() {
  630. this.payNowModalVisible = false
  631. // 支付方式保存(后端需 sendFormId 更新发单表)
  632. await uni.$u.api.updateReceiptForm({
  633. id: this.currentReceipt.id,
  634. sendFormId: this.currentReceipt.sendFormId || this.orderDetail.id,
  635. paymentMethod: this.paymentMethod || ''
  636. })
  637. // 根据 radio 选择判断:线上支付(online / online_alipay)需调用支付接口,线下则直接下一步
  638. if (this.paymentMethodRadio === 'online' || this.paymentMethodRadio === 'online_alipay') {
  639. this.$emit('confirm-pay')
  640. } else {
  641. this.handleNext()
  642. }
  643. },
  644. /**
  645. * 确认未收
  646. */
  647. async confirmUnpaid() {
  648. try {
  649. // 若填写了好处费,先更新回单表
  650. if (this.currentReceipt?.id && (this.unpaidBenefitFee !== '' && this.unpaidBenefitFee != null)) {
  651. await uni.$u.api.updateReceiptForm({
  652. id: this.currentReceipt.id,
  653. benefitFee: this.unpaidBenefitFee
  654. })
  655. }
  656. await uni.$u.api.addOrderFollow({
  657. orderId: this.orderId,
  658. content: `未收评分_${this.unpaidRating}`
  659. })
  660. await uni.$u.api.oderForm({
  661. status: '4',
  662. id: this.orderId
  663. })
  664. uni.$u.toast('提交未收评级成功')
  665. this.unpaidModalVisible = false
  666. this.unpaidBenefitFee = ''
  667. // 刷新当前收单数据,以便第四页好处费等字段同步
  668. this.$emit('price-updated')
  669. } catch (error) {
  670. console.error('提交失败:', error)
  671. uni.$u.toast('提交失败')
  672. }
  673. },
  674. /**
  675. * 确认跟进
  676. */
  677. async confirmFollowUp() {
  678. try {
  679. await uni.$u.api.addOrderFollow({
  680. orderId: this.orderId,
  681. content: `待跟进_${this.followUpNotes}`
  682. })
  683. uni.$u.toast('提交待跟进记录成功')
  684. this.followUpModalVisible = false
  685. this.followUpNotes = ''
  686. } catch (error) {
  687. console.error('提交失败:', error)
  688. uni.$u.toast('提交失败')
  689. }
  690. },
  691. /**
  692. * 下一步
  693. */
  694. async handleNext() {
  695. // 与 PC 端一致:支付信息通过 updateReceiptForm 保存;后端需 sendFormId 才能更新发单表的开户人/银行/身份证等
  696. await uni.$u.api.updateReceiptForm({
  697. id: this.currentReceipt.id,
  698. sendFormId: this.currentReceipt.sendFormId || this.orderDetail.id,
  699. tableFee: this.paymentAmount,
  700. fileIds: this.detailImages.map(item => item.id).join(','),
  701. customName: this.paymentInfo.customName || '',
  702. bankName: this.paymentInfo.bankName || '',
  703. bankCardNumber: this.paymentInfo.bankAccount || '',
  704. idCard: this.paymentInfo.idNumber || '',
  705. // paymentMethod: this.paymentMethod || ''
  706. })
  707. this.$emit('save', {
  708. nowPage: 'formThree',
  709. form: {
  710. ...this.paymentInfo
  711. },
  712. fileIds: this.detailImages.map(item => item.id).join(',')
  713. })
  714. this.$emit('next', {
  715. nowPage: 'formThree',
  716. form: {
  717. ...this.paymentInfo
  718. }
  719. })
  720. },
  721. /**
  722. * 核价
  723. */
  724. async handlePricing() {
  725. const brandList = await this.$getDicts("crm_form_brand");
  726. if (!this.currentReceipt || !this.currentReceipt.clueId) {
  727. uni.$u.toast('缺少线索ID')
  728. return
  729. }
  730. if (!this.currentReceipt.brand) {
  731. uni.$u.toast('缺少品牌信息')
  732. return
  733. }
  734. if (!this.displayImages || this.displayImages.length === 0) {
  735. uni.$u.toast('请先上传图片')
  736. return
  737. }
  738. //我这里询价要用receiptid来询价
  739. this.pricingClueId = this.currentReceipt.id
  740. // brand 是品牌的 id(dictValue),itemBrand 是品牌的 label(dictLabel)
  741. const brandDictLabel = this.currentReceipt.brand
  742. const brandDictValue = brandList.find(item => item.dictLabel === this.currentReceipt.brand).dictValue;
  743. // 将图片转换为 imgsUrl 格式
  744. const imgsUrl = this.displayImages.map(item => item.fileUrl).filter(url => url)
  745. // 检查是否已有核价记录
  746. try {
  747. const data = {
  748. clueId: this.currentReceipt.clueId,
  749. type: 2
  750. }
  751. const res = await uni.$u.api.inquiryDetail(data)
  752. if (res.code === 200 && res.data) {
  753. // 编辑模式:保留原有数据,但更新品牌和图片
  754. this.pricingEditOrAdd = 'editForm'
  755. this.pricingEditInfo = {
  756. ...res.data,
  757. dictLabel: brandDictLabel || res.data.dictLabel,
  758. dictValue: brandDictValue || res.data.dictValue,
  759. imgsUrl: imgsUrl.length > 0 ? imgsUrl : (res.data.imgsUrl || [])
  760. }
  761. this.$nextTick(() => {
  762. this.$refs.pricingDialog.showDialog()
  763. })
  764. } else {
  765. // 新增模式:设置品牌和图片
  766. this.pricingEditOrAdd = 'editForm'
  767. this.pricingEditInfo = {
  768. dictLabel: brandDictLabel,
  769. dictValue: brandDictValue,
  770. imgsUrl: imgsUrl,
  771. model: this.currentReceipt.model,
  772. code: '',
  773. price: ''
  774. }
  775. this.$nextTick(() => {
  776. this.$refs.pricingDialog.showDialog()
  777. })
  778. }
  779. } catch (error) {
  780. // 如果没有记录,则新增
  781. this.pricingEditOrAdd = 'receptFormAdd'
  782. this.pricingEditInfo = {
  783. dictLabel: brandDictLabel,
  784. dictValue: brandDictValue,
  785. imgsUrl: imgsUrl,
  786. model: this.currentReceipt.model,
  787. code: '',
  788. price: ''
  789. }
  790. this.$nextTick(() => {
  791. this.$refs.pricingDialog.showDialog()
  792. })
  793. }
  794. },
  795. }
  796. }
  797. </script>
  798. <style scoped lang="scss">
  799. @import '../styles/common.scss';
  800. .page-three-container {
  801. @extend .page-container;
  802. padding-bottom: 100rpx;
  803. }
  804. .info-card {
  805. @extend .card-wrap;
  806. padding: 20rpx;
  807. margin-top: 20rpx;
  808. box-sizing: border-box;
  809. width: 100%;
  810. max-width: 100%;
  811. }
  812. .info-card-title {
  813. @include font-styles($size: title, $weight: bold, $color: primary);
  814. margin-bottom: 25rpx;
  815. padding-bottom: 15rpx;
  816. border-bottom: 1rpx solid map-get($colors, border);
  817. }
  818. .info-row {
  819. margin-bottom: 20rpx;
  820. }
  821. .info-label {
  822. @include font-styles($size: tiny, $weight: regular, $color: tertiary);
  823. margin-bottom: 8rpx;
  824. display: block;
  825. }
  826. .info-input {
  827. border-radius: 8rpx;
  828. border: 1rpx solid #e5e7eb;
  829. padding: 12rpx 16rpx;
  830. width: 100%;
  831. box-sizing: border-box;
  832. }
  833. .detail-image-section {
  834. padding: 20rpx;
  835. }
  836. .detail-image-header {
  837. display: grid;
  838. grid-template-columns: 1fr auto;
  839. margin-bottom: 20rpx;
  840. padding-bottom: 20rpx;
  841. padding-right: 100rpx; /* 右侧留空,避免被固定页面导航遮挡,使整行(含核价按钮)左移 */
  842. border-bottom: 1rpx solid map-get($colors, border);
  843. }
  844. .detail-image-title {
  845. @include font-styles($size: content, $weight: bold, $color: primary);
  846. }
  847. .detail-image-list {
  848. display: flex;
  849. flex-wrap: wrap;
  850. gap: 20rpx;
  851. position: relative;
  852. }
  853. .detail-image-item {
  854. position: relative;
  855. width: 200rpx;
  856. height: 200rpx;
  857. touch-action: none;
  858. transition: transform 0.2s ease, opacity 0.2s ease;
  859. z-index: 1;
  860. box-sizing: border-box;
  861. &.dragging {
  862. opacity: 0.6;
  863. transform: scale(1.05);
  864. z-index: 999;
  865. box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.2);
  866. }
  867. &.can-drop {
  868. outline: 2rpx dashed #108cff;
  869. outline-offset: -2rpx;
  870. background-color: rgba(16, 140, 255, 0.1);
  871. border-radius: 8rpx;
  872. }
  873. }
  874. .image-type-tag {
  875. position: absolute;
  876. top: 10rpx;
  877. left: 10rpx;
  878. background-color: rgba(0, 0, 0, 0.6);
  879. color: white;
  880. padding: 5rpx 10rpx;
  881. border-radius: 12rpx;
  882. font-size: 22rpx;
  883. z-index: 1;
  884. }
  885. .detail-delete-btn {
  886. position: absolute;
  887. top: -10rpx;
  888. right: -10rpx;
  889. width: 40rpx;
  890. height: 40rpx;
  891. background-color: #ff4d4f;
  892. color: #fff;
  893. border-radius: 50%;
  894. display: flex;
  895. align-items: center;
  896. justify-content: center;
  897. font-weight: bold;
  898. z-index: 10;
  899. cursor: pointer;
  900. }
  901. .detail-upload-btn {
  902. width: 200rpx;
  903. height: 200rpx;
  904. border: 8rpx dashed #ddd;
  905. border-radius: 30rpx;
  906. display: flex;
  907. align-items: center;
  908. justify-content: center;
  909. background-color: #f9f9f9;
  910. cursor: pointer;
  911. }
  912. .pricing-button {
  913. margin-top: 0;
  914. }
  915. .payment-card {
  916. margin-top: 20rpx;
  917. }
  918. .payment-section {
  919. padding: 20rpx;
  920. }
  921. .payment-total-container {
  922. background-color: #f5f7fa;
  923. border: 2rpx solid #e5e7eb;
  924. border-radius: 12rpx;
  925. padding: 0rpx 30rpx;
  926. display: flex;
  927. align-items: center;
  928. justify-content: space-between;
  929. margin-bottom: 24rpx;
  930. }
  931. .payment-label {
  932. font-size: 36rpx;
  933. font-weight: 700;
  934. min-width: 140rpx;
  935. flex-shrink: 0;
  936. }
  937. .payment-amount-wrap {
  938. flex: 1;
  939. display: flex;
  940. justify-content: center;
  941. align-items: center;
  942. min-width: 0;
  943. }
  944. .payment-amount-wrap .payment-amount {
  945. font-size: 48rpx;
  946. font-weight: 600;
  947. color: #f53f3f;
  948. width: 100%;
  949. max-width: 240rpx;
  950. border: none;
  951. text-align: center;
  952. }
  953. /* 让 u-input 内部输入框文字居中 */
  954. .payment-amount-wrap :deep(.u-input__content__field-wrapper__field),
  955. .payment-amount-wrap :deep(input) {
  956. text-align: center !important;
  957. }
  958. .pay-now-btn-wrap {
  959. flex-shrink: 0;
  960. display: inline-flex;
  961. }
  962. .payment-total-container .pay-now-btn-wrap .u-button,
  963. .payment-total-container .pay-now-btn-wrap .u-btn {
  964. width: auto !important;
  965. min-width: 0 !important;
  966. flex: none !important;
  967. }
  968. .payment-buttons-row {
  969. display: flex;
  970. gap: 20rpx;
  971. margin-bottom: 24rpx;
  972. }
  973. .payment-button {
  974. flex: 1;
  975. border-radius: 12rpx;
  976. padding: 5rpx 0;
  977. display: flex;
  978. flex-direction: column;
  979. align-items: center;
  980. justify-content: center;
  981. background-color: #f5f7fa;
  982. border: 2rpx solid #e5e7eb;
  983. cursor: pointer;
  984. gap: 12rpx;
  985. }
  986. .button-text {
  987. font-size: 28rpx;
  988. font-weight: 500;
  989. color: inherit;
  990. }
  991. .modal-content {
  992. width: 100%;
  993. padding: 40rpx 20rpx;
  994. display: flex;
  995. flex-direction: column;
  996. align-items: center;
  997. }
  998. .unpaid-benefit-fee-wrap {
  999. width: 100%;
  1000. margin-top: 32rpx;
  1001. display: flex;
  1002. flex-direction: column;
  1003. align-items: flex-start;
  1004. }
  1005. .unpaid-benefit-fee-label {
  1006. font-size: 28rpx;
  1007. color: #333;
  1008. margin-bottom: 16rpx;
  1009. }
  1010. .unpaid-benefit-fee-input {
  1011. width: 100%;
  1012. border: 2rpx solid #e5e7eb;
  1013. border-radius: 12rpx;
  1014. padding: 20rpx 24rpx;
  1015. box-sizing: border-box;
  1016. }
  1017. .payment-amount-display {
  1018. font-size: 64rpx;
  1019. font-weight: bold;
  1020. color: #108cff;
  1021. text-align: center;
  1022. margin-bottom: 40rpx;
  1023. width: 100%;
  1024. }
  1025. .payment-info-section {
  1026. width: 100%;
  1027. background-color: #f5f7fa;
  1028. border: 2rpx solid #e5e7eb;
  1029. border-radius: 12rpx;
  1030. padding: 30rpx 20rpx;
  1031. margin-bottom: 40rpx;
  1032. }
  1033. .info-item {
  1034. display: flex;
  1035. margin-bottom: 20rpx;
  1036. align-items: center;
  1037. justify-content: space-between;
  1038. &:last-child {
  1039. margin-bottom: 0;
  1040. }
  1041. }
  1042. .next-btn {
  1043. position: fixed;
  1044. bottom: 10rpx;
  1045. left: 2.5%;
  1046. width: 95%;
  1047. height: 80rpx;
  1048. line-height: 80rpx;
  1049. text-align: center;
  1050. border-radius: 20rpx;
  1051. z-index: 1000;
  1052. }
  1053. </style>