PageTwo.vue 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860
  1. <template>
  2. <view class="page-two-container">
  3. <!-- 上门地址卡片 -->
  4. <view class="card-wrap">
  5. <view class="address-section">
  6. <view class="address-header">
  7. <up-icon name="map" size="36rpx" color="#108cff" class="location-icon" />
  8. <text class="address-title">上门地址</text>
  9. </view>
  10. <view class="address-content">
  11. <text class="address-text">{{ orderDetail.address || '未填写' }}</text>
  12. </view>
  13. </view>
  14. </view>
  15. <!-- 跟进清单卡片 -->
  16. <view class="card-wrap checklist-card">
  17. <view class="checklist-section">
  18. <up-checkbox-group v-model="selectedCheckbox" placement="column">
  19. <!-- 联系师傅 -->
  20. <view class="checklist-item">
  21. <view class="checkbox-text-container">
  22. <up-checkbox name="contactMaster" size="40rpx" color="#108cff" />
  23. <text class="checklist-text">联系师傅</text>
  24. </view>
  25. <up-input
  26. v-if="selectedCheckbox.includes('contactMaster')"
  27. v-model="formData.contactPhone"
  28. placeholder="请输入师傅手机号"
  29. class="checklist-input"
  30. @blur="saveContactMaster"
  31. />
  32. </view>
  33. <!-- 师傅拍图技巧 -->
  34. <view class="checklist-item">
  35. <view class="checkbox-text-container">
  36. <up-checkbox name="photoTips" size="40rpx" color="#108cff" />
  37. <text class="checklist-text">师傅拍图技巧</text>
  38. </view>
  39. <up-input
  40. v-if="selectedCheckbox.includes('photoTips')"
  41. v-model="formData.photoTips"
  42. type="textarea"
  43. placeholder="请输入拍图技巧"
  44. rows="3"
  45. class="checklist-textarea"
  46. @blur="savePhotoTips"
  47. />
  48. <view v-if="selectedCheckbox.includes('photoTips')" class="upload-btn-container">
  49. <view class="upload-btn" @click="handleUpload('photoTips')">
  50. <up-icon name="camera" size="32rpx" color="#108cff" />
  51. <text class="upload-btn-text">上传图片</text>
  52. </view>
  53. </view>
  54. <view
  55. v-if="selectedCheckbox.includes('photoTips') && photoTipsImages.length > 0"
  56. class="image-list"
  57. >
  58. <view
  59. v-for="(image, index) in photoTipsImages"
  60. :key="index"
  61. class="image-item"
  62. @click="previewPhotoTips(image)"
  63. >
  64. <image :src="image" mode="aspectFill" class="image-thumb" />
  65. </view>
  66. </view>
  67. </view>
  68. <!-- 到达客户面对面 -->
  69. <view class="checklist-item">
  70. <view class="checkbox-text-container">
  71. <up-checkbox name="faceToFace" size="40rpx" color="#108cff" />
  72. <text class="checklist-text">到达客户面对面</text>
  73. </view>
  74. <up-input
  75. v-if="selectedCheckbox.includes('faceToFace')"
  76. v-model="formData.faceToFaceNotes"
  77. type="textarea"
  78. placeholder="请输入备注信息"
  79. rows="3"
  80. class="checklist-textarea"
  81. @blur="saveFaceToFace"
  82. />
  83. <view v-if="selectedCheckbox.includes('faceToFace')" class="upload-btn-container">
  84. <view class="upload-btn" @click="handleUpload('faceToFace')">
  85. <up-icon name="camera" size="32rpx" color="#108cff" />
  86. <text class="upload-btn-text">上传图片</text>
  87. </view>
  88. </view>
  89. <view
  90. v-if="selectedCheckbox.includes('faceToFace') && faceToFaceImages.length > 0"
  91. class="image-list"
  92. >
  93. <view
  94. v-for="(image, index) in faceToFaceImages"
  95. :key="index"
  96. class="image-item"
  97. @click="previewFaceToFace(image)"
  98. >
  99. <image :src="image" mode="aspectFill" class="image-thumb" />
  100. </view>
  101. </view>
  102. </view>
  103. </up-checkbox-group>
  104. </view>
  105. </view>
  106. <!-- 核准价卡片 -->
  107. <view class="card-wrap price-card">
  108. <view class="price-section">
  109. <view class="price-picker-container">
  110. <view class="quick-actions top-actions">
  111. <view class="quick-btn increase" @click="quickChangePrice(100)">+100</view>
  112. <view class="quick-btn increase" @click="quickChangePrice(1000)">+1000</view>
  113. </view>
  114. <view class="number-box-container">
  115. <view class="price-input-box">
  116. <text class="price-label">核准价</text>
  117. <view class="price-value-wrap">
  118. <text class="yuan-prefix">¥</text>
  119. <input
  120. type="number"
  121. v-model="approvedPrice"
  122. class="price-input"
  123. placeholder="0"
  124. min="0"
  125. @input="onPriceInput"
  126. @blur="saveApprovedPrice"
  127. />
  128. </view>
  129. </view>
  130. </view>
  131. <view class="quick-actions bottom-actions">
  132. <view class="quick-btn decrease" @click="quickChangePrice(-100)">-100</view>
  133. <view class="quick-btn decrease" @click="quickChangePrice(-1000)">-1000</view>
  134. </view>
  135. </view>
  136. </view>
  137. </view>
  138. <!-- 高清细节图卡片 -->
  139. <view class="card-wrap detail-image-card">
  140. <view class="detail-image-section">
  141. <view class="detail-image-header">
  142. <text class="detail-image-title">上传高清细节图(支持多选)</text>
  143. <view class="copy-btn" @click="copyAllDetailImages">
  144. <text>一键保存</text>
  145. </view>
  146. </view>
  147. <view class="detail-image-upload-container">
  148. <view class="detail-image-list">
  149. <view
  150. v-for="(item, index) in detailImages"
  151. :key="`detail-${index}`"
  152. class="detail-image-item"
  153. >
  154. <PicComp
  155. :src="item.fileUrl"
  156. @needPreviewPic="previewImageDetail"
  157. />
  158. <view class="detail-delete-btn" @click="handleDeleteImage(item)">×</view>
  159. </view>
  160. <view
  161. class="detail-upload-btn"
  162. @click="handleUploadImage('detailImages')"
  163. >
  164. <up-icon name="plus" size="40rpx" color="#999" />
  165. </view>
  166. </view>
  167. </view>
  168. </view>
  169. </view>
  170. <!-- 下一步按钮
  171. <up-button
  172. class="next-btn"
  173. @click="handleNext"
  174. type="primary"
  175. size="middle"
  176. >
  177. 下一步
  178. </up-button> -->
  179. </view>
  180. </template>
  181. <script>
  182. import PicComp from './PicComp.vue'
  183. import imageUpload from '../utils/imageUpload.js'
  184. export default {
  185. name: 'PageTwo',
  186. components: {
  187. PicComp
  188. },
  189. props: {
  190. orderDetail: {
  191. type: Object,
  192. default: () => ({})
  193. },
  194. orderId: {
  195. type: String,
  196. default: ''
  197. },
  198. currentReceipt: {
  199. type: Object,
  200. default: () => ({})
  201. },
  202. followUpList: {
  203. type: Array,
  204. default: () => []
  205. }
  206. },
  207. data() {
  208. return {
  209. selectedCheckbox: [],
  210. formData: {
  211. contactPhone: '',
  212. photoTips: '',
  213. faceToFaceNotes: ''
  214. },
  215. photoTipsImages: [],
  216. faceToFaceImages: [],
  217. approvedPrice: 0,
  218. detailImages: []
  219. }
  220. },
  221. watch: {
  222. currentReceipt: {
  223. handler(newVal) {
  224. if (newVal && newVal.id) {
  225. this.approvedPrice = Number(newVal.sellingPrice) || 0
  226. this.loadDetailImages()
  227. }
  228. },
  229. immediate: true,
  230. deep: true
  231. },
  232. followUpList: {
  233. handler(newVal) {
  234. if (newVal && newVal.length > 0) {
  235. this.checkFollowUpContent(newVal)
  236. }
  237. },
  238. deep: true
  239. }
  240. },
  241. methods: {
  242. /**
  243. * 加载细节图
  244. */
  245. async loadDetailImages() {
  246. if (!this.currentReceipt.id || !this.orderDetail.itemBrand) return
  247. try {
  248. const list = await imageUpload.getFileList(
  249. '2',
  250. '3',
  251. this.currentReceipt.id,
  252. this.orderDetail.itemBrand,
  253. this.currentReceipt.clueId
  254. )
  255. // 按照 fileIds 排序
  256. if (this.currentReceipt.fileIds && list && list.length > 0) {
  257. const sortedIds = this.currentReceipt.fileIds.split(',')
  258. list.sort((a, b) => {
  259. const indexA = sortedIds.indexOf(a.id)
  260. const indexB = sortedIds.indexOf(b.id)
  261. // 如果都不在列表中,保持原顺序
  262. if (indexA === -1 && indexB === -1) return 0
  263. // 如果 a 不在列表中,放到后面
  264. if (indexA === -1) return 1
  265. // 如果 b 不在列表中,放到后面
  266. if (indexB === -1) return -1
  267. // 都在列表中,按 index 排序
  268. return indexA - indexB
  269. })
  270. }
  271. this.detailImages = list || []
  272. } catch (error) {
  273. console.error('加载细节图失败:', error)
  274. }
  275. },
  276. /**
  277. * 检查跟进内容:用最新一条记录回显(支持再次进入时回显)
  278. */
  279. checkFollowUpContent(followUpList) {
  280. if (!followUpList || followUpList.length === 0) return
  281. const contentList = followUpList.map(item => item.content || '')
  282. // 按顺序遍历,后面的覆盖前面的,得到每类最新一条
  283. let lastContactMaster = null
  284. let lastPhotoTips = null
  285. let lastFaceToFace = null
  286. contentList.forEach(item => {
  287. if (item.includes('联系师傅')) lastContactMaster = item
  288. if (item.includes('师傅拍图技巧')) lastPhotoTips = item
  289. if (item.includes('到达客户面对面')) lastFaceToFace = item
  290. })
  291. if (lastContactMaster) {
  292. if (!this.selectedCheckbox.includes('contactMaster')) this.selectedCheckbox.push('contactMaster')
  293. this.formData.contactPhone = (lastContactMaster.split(';')[1] || '').trim()
  294. }
  295. if (lastPhotoTips) {
  296. if (!this.selectedCheckbox.includes('photoTips')) this.selectedCheckbox.push('photoTips')
  297. this.formData.photoTips = (lastPhotoTips.split(';')[1] || '').trim()
  298. const urls = (lastPhotoTips.split(';')[2] || '').split(',').filter(url => url.trim())
  299. this.photoTipsImages = urls
  300. }
  301. if (lastFaceToFace) {
  302. if (!this.selectedCheckbox.includes('faceToFace')) this.selectedCheckbox.push('faceToFace')
  303. this.formData.faceToFaceNotes = (lastFaceToFace.split(';')[1] || '').trim()
  304. const urls = (lastFaceToFace.split(';')[2] || '').split(',').filter(url => url.trim())
  305. this.faceToFaceImages = urls
  306. }
  307. },
  308. /**
  309. * 价格输入处理
  310. */
  311. onPriceInput(e) {
  312. let value = Number(e.detail.value)
  313. if (isNaN(value)) value = 0
  314. value = Math.max(0, value)
  315. this.approvedPrice = value
  316. },
  317. /**
  318. * 快速调整价格
  319. */
  320. async quickChangePrice(amount) {
  321. let newPrice = this.approvedPrice + amount
  322. newPrice = Math.max(0, newPrice)
  323. this.approvedPrice = newPrice
  324. await uni.$u.api.updateReceiptForm({
  325. id: this.currentReceipt.id,
  326. sellingPrice: newPrice
  327. })
  328. this.$emit('price-updated')
  329. },
  330. async saveApprovedPrice() {
  331. await uni.$u.api.updateReceiptForm({
  332. id: this.currentReceipt.id,
  333. sellingPrice: this.approvedPrice
  334. })
  335. this.$emit('price-updated')
  336. },
  337. /**
  338. * 上传图片(跟进清单):有新图片即保存
  339. */
  340. async handleUpload(field) {
  341. try {
  342. const filePaths = await imageUpload.chooseImage(9)
  343. const uploadResults = await imageUpload.uploadFiles(filePaths)
  344. const urls = uploadResults.map(item => item.fileUrl)
  345. if (field === 'photoTips') {
  346. this.photoTipsImages = [...this.photoTipsImages, ...urls]
  347. await this.savePhotoTips()
  348. } else if (field === 'faceToFace') {
  349. this.faceToFaceImages = [...this.faceToFaceImages, ...urls]
  350. await this.saveFaceToFace()
  351. }
  352. } catch (error) {
  353. console.error('上传失败:', error)
  354. uni.$u.toast('上传失败')
  355. }
  356. },
  357. /**
  358. * 失焦/有图时保存:联系师傅
  359. */
  360. async saveContactMaster() {
  361. if (!this.selectedCheckbox.includes('contactMaster')) return
  362. try {
  363. await uni.$u.api.addOrderFollow({
  364. orderId: this.orderId,
  365. content: `联系师傅;${this.formData.contactPhone || ''}`
  366. })
  367. this.$emit('follow-saved')
  368. } catch (e) {
  369. console.error('保存联系师傅失败:', e)
  370. }
  371. },
  372. /**
  373. * 失焦/有图时保存:师傅拍图技巧
  374. */
  375. async savePhotoTips() {
  376. if (!this.selectedCheckbox.includes('photoTips')) return
  377. try {
  378. const urls = (this.photoTipsImages || []).join(',')
  379. await uni.$u.api.addOrderFollow({
  380. orderId: this.orderId,
  381. content: `师傅拍图技巧;${this.formData.photoTips || ''};${urls}`
  382. })
  383. this.$emit('follow-saved')
  384. } catch (e) {
  385. console.error('保存师傅拍图技巧失败:', e)
  386. }
  387. },
  388. /**
  389. * 失焦/有图时保存:到达客户面对面
  390. */
  391. async saveFaceToFace() {
  392. if (!this.selectedCheckbox.includes('faceToFace')) return
  393. try {
  394. const urls = (this.faceToFaceImages || []).join(',')
  395. await uni.$u.api.addOrderFollow({
  396. orderId: this.orderId,
  397. content: `到达客户面对面;${this.formData.faceToFaceNotes || ''};${urls}`
  398. })
  399. this.$emit('follow-saved')
  400. } catch (e) {
  401. console.error('保存到达客户面对面失败:', e)
  402. }
  403. },
  404. /**
  405. * 上传细节图
  406. */
  407. async handleUploadImage() {
  408. try {
  409. const filePaths = await imageUpload.chooseImage(9)
  410. const uploadResults = await imageUpload.uploadFiles(filePaths)
  411. await imageUpload.bindOrderFile(
  412. this.currentReceipt.clueId,
  413. this.currentReceipt.id,
  414. '3',
  415. uploadResults
  416. )
  417. await this.loadDetailImages()
  418. // 更新 fileIds
  419. const fileIds = this.detailImages.map(item => item.id).join(',')
  420. await uni.$u.api.updateReceiptForm({
  421. id: this.currentReceipt.id,
  422. fileIds: fileIds
  423. })
  424. this.$emit('update-file-ids', fileIds)
  425. } catch (error) {
  426. console.error('上传失败:', error)
  427. }
  428. },
  429. /**
  430. * 删除图片
  431. */
  432. async handleDeleteImage(item) {
  433. uni.showModal({
  434. title: '提示',
  435. content: '确定要删除这张图片吗?',
  436. success: async (res) => {
  437. if (res.confirm) {
  438. try {
  439. await imageUpload.deleteFile(item.id)
  440. await this.loadDetailImages()
  441. // 更新 fileIds
  442. const fileIds = this.detailImages.map(item => item.id).join(',')
  443. await uni.$u.api.updateReceiptForm({
  444. id: this.currentReceipt.id,
  445. fileIds: fileIds
  446. })
  447. this.$emit('update-file-ids', fileIds)
  448. } catch (error) {
  449. console.error('删除失败:', error)
  450. }
  451. }
  452. }
  453. })
  454. },
  455. /**
  456. * 复制所有细节图
  457. */
  458. async copyAllDetailImages() {
  459. const allUrls = this.detailImages.map(item => item.fileUrl)
  460. if (allUrls.length === 0) {
  461. uni.showToast({
  462. title: '没有图片可复制',
  463. icon: 'none'
  464. })
  465. return
  466. }
  467. uni.showModal({
  468. title: '保存图片',
  469. content: `是否将 ${allUrls.length} 张图片保存到本地相册?`,
  470. confirmText: '保存',
  471. success: (res) => {
  472. if (res.confirm) {
  473. imageUpload.saveImagesToLocal(allUrls)
  474. }
  475. }
  476. })
  477. },
  478. /**
  479. * 预览细节图
  480. */
  481. previewImageDetail(src) {
  482. const urlList = this.detailImages.map(item => item.fileUrl)
  483. uni.previewImage({
  484. urls: urlList,
  485. current: src
  486. })
  487. },
  488. /**
  489. * 预览师傅拍图技巧图片
  490. */
  491. previewPhotoTips(src) {
  492. uni.previewImage({
  493. urls: this.photoTipsImages,
  494. current: src
  495. })
  496. },
  497. /**
  498. * 预览到达客户面对面图片
  499. */
  500. previewFaceToFace(src) {
  501. uni.previewImage({
  502. urls: this.faceToFaceImages,
  503. current: src
  504. })
  505. },
  506. /**
  507. * 下一步
  508. */
  509. async handleNext() {
  510. // 保存核准价
  511. await uni.$u.api.updateReceiptForm({
  512. id: this.currentReceipt.id,
  513. sellingPrice: this.approvedPrice
  514. })
  515. // 保存跟进记录
  516. if (this.selectedCheckbox.includes('contactMaster')) {
  517. await uni.$u.api.addOrderFollow({
  518. orderId: this.orderId,
  519. content: `联系师傅;${this.formData.contactPhone}`
  520. })
  521. }
  522. if (this.selectedCheckbox.includes('photoTips')) {
  523. const urls = this.photoTipsImages.join(',')
  524. await uni.$u.api.addOrderFollow({
  525. orderId: this.orderId,
  526. content: `师傅拍图技巧;${this.formData.photoTips};${urls}`
  527. })
  528. }
  529. if (this.selectedCheckbox.includes('faceToFace')) {
  530. const urls = this.faceToFaceImages.join(',')
  531. await uni.$u.api.addOrderFollow({
  532. orderId: this.orderId,
  533. content: `到达客户面对面;${this.formData.faceToFaceNotes};${urls}`
  534. })
  535. }
  536. this.$emit('next', {
  537. nowPage: 'formTwo',
  538. form: {
  539. approvedPrice: this.approvedPrice,
  540. detailImages: this.detailImages
  541. }
  542. })
  543. }
  544. }
  545. }
  546. </script>
  547. <style scoped lang="scss">
  548. @import '../styles/common.scss';
  549. .page-two-container {
  550. @extend .page-container;
  551. padding-bottom: 100rpx;
  552. }
  553. .address-section {
  554. padding: 20rpx;
  555. }
  556. .checklist-card {
  557. margin-top: 20rpx;
  558. }
  559. .checklist-section {
  560. padding: 20rpx;
  561. }
  562. .checklist-item {
  563. padding: 16rpx 0;
  564. border-bottom: 1rpx solid map-get($colors, border);
  565. &:last-child {
  566. border-bottom: none;
  567. }
  568. }
  569. .checkbox-text-container {
  570. @include flex-center;
  571. margin-bottom: 12rpx;
  572. }
  573. .checklist-text {
  574. @include font-styles;
  575. margin-left: 16rpx;
  576. }
  577. .checklist-input,
  578. .checklist-textarea {
  579. margin-top: 12rpx;
  580. margin-left: 56rpx;
  581. width: calc(100% - 72rpx);
  582. border-radius: 8rpx;
  583. border: 1rpx solid #e5e7eb;
  584. padding: 12rpx 16rpx;
  585. }
  586. .upload-btn-container {
  587. margin-top: 16rpx;
  588. margin-left: 56rpx;
  589. }
  590. .upload-btn {
  591. display: inline-flex;
  592. align-items: center;
  593. gap: 12rpx;
  594. padding: 20rpx 40rpx;
  595. border-radius: 8rpx;
  596. background-color: map-get($colors, bg);
  597. border: 2rpx dashed map-get($colors, primary);
  598. color: map-get($colors, primary);
  599. cursor: pointer;
  600. }
  601. .upload-btn-text {
  602. @include font-styles($size: small, $weight: medium);
  603. }
  604. .image-list {
  605. margin-top: 16rpx;
  606. margin-left: 56rpx;
  607. display: flex;
  608. flex-wrap: wrap;
  609. gap: 16rpx;
  610. }
  611. .image-item {
  612. width: 120rpx;
  613. height: 120rpx;
  614. border-radius: 8rpx;
  615. overflow: hidden;
  616. cursor: pointer;
  617. transition: opacity 0.2s;
  618. &:active {
  619. opacity: 0.7;
  620. }
  621. }
  622. .image-thumb {
  623. width: 100%;
  624. height: 100%;
  625. object-fit: cover;
  626. }
  627. .price-card {
  628. margin-top: 20rpx;
  629. }
  630. .price-section {
  631. padding: 20rpx;
  632. padding-right: 20rpx; /* 整体往左挪,避免被右侧固定导航遮挡 */
  633. }
  634. .price-picker-container {
  635. display: flex;
  636. flex-direction: column;
  637. align-items: center;
  638. padding: 20rpx 0;
  639. }
  640. .quick-actions {
  641. display: flex;
  642. justify-content: center;
  643. gap: 16rpx;
  644. margin: 5rpx 0;
  645. width: 100%;
  646. }
  647. .quick-btn {
  648. flex: 1;
  649. border-radius: 12rpx;
  650. font-size: 36rpx;
  651. padding: 10rpx 0;
  652. text-align: center;
  653. font-weight: 600;
  654. cursor: pointer;
  655. &.increase {
  656. background-color: #e6f7ed;
  657. color: #00b42a;
  658. }
  659. &.decrease {
  660. background-color: #fff1f0;
  661. color: #f53f3f;
  662. }
  663. }
  664. .number-box-container {
  665. display: flex;
  666. align-items: center;
  667. margin: 20rpx 0;
  668. width: 100%;
  669. justify-content: center;
  670. }
  671. .price-input-box {
  672. flex: 1;
  673. max-width: 800rpx;
  674. background-color: #f5f7fa;
  675. border: 2rpx solid #e5e7eb;
  676. border-radius: 12rpx;
  677. padding: 0rpx 24rpx;
  678. display: flex;
  679. align-items: center;
  680. justify-content: space-between;
  681. }
  682. .price-label {
  683. font-size: 30rpx;
  684. font-weight: 700;
  685. min-width: 120rpx;
  686. }
  687. .price-value-wrap {
  688. flex: 1;
  689. display: flex;
  690. align-items: center;
  691. min-width: 0;
  692. }
  693. .yuan-prefix {
  694. font-size: 48rpx;
  695. font-weight: 600;
  696. margin-right: 8rpx;
  697. flex-shrink: 0;
  698. }
  699. .price-input {
  700. flex: 1;
  701. min-width: 0;
  702. border: none;
  703. outline: none;
  704. background-color: transparent;
  705. text-align: left;
  706. font-size: 48rpx;
  707. font-weight: 600;
  708. padding: 0 10rpx 0 0;
  709. }
  710. .detail-image-card {
  711. margin-top: 20rpx;
  712. }
  713. .detail-image-section {
  714. padding: 20rpx;
  715. }
  716. .detail-image-header {
  717. display: flex;
  718. justify-content: space-between;
  719. align-items: center;
  720. margin-bottom: 20rpx;
  721. padding-bottom: 20rpx;
  722. border-bottom: 1rpx solid map-get($colors, border);
  723. }
  724. .detail-image-title {
  725. @include font-styles($size: content, $weight: bold, $color: primary);
  726. }
  727. .copy-btn {
  728. border-radius: 20rpx;
  729. border: 1rpx solid #007AFF;
  730. background-color: transparent;
  731. color: #007AFF;
  732. padding: 0 24rpx;
  733. height: 64rpx;
  734. line-height: 64rpx;
  735. cursor: pointer;
  736. }
  737. .detail-image-list {
  738. display: flex;
  739. flex-wrap: wrap;
  740. gap: 20rpx;
  741. }
  742. .detail-image-item {
  743. position: relative;
  744. width: 200rpx;
  745. height: 200rpx;
  746. }
  747. .detail-delete-btn {
  748. position: absolute;
  749. top: -10rpx;
  750. right: -10rpx;
  751. width: 40rpx;
  752. height: 40rpx;
  753. background-color: #ff4d4f;
  754. color: #fff;
  755. border-radius: 50%;
  756. display: flex;
  757. align-items: center;
  758. justify-content: center;
  759. font-weight: bold;
  760. z-index: 10;
  761. cursor: pointer;
  762. }
  763. .detail-upload-btn {
  764. width: 200rpx;
  765. height: 200rpx;
  766. border: 8rpx dashed #ddd;
  767. border-radius: 30rpx;
  768. display: flex;
  769. align-items: center;
  770. justify-content: center;
  771. background-color: #f9f9f9;
  772. cursor: pointer;
  773. }
  774. .next-btn {
  775. position: fixed;
  776. bottom: 10rpx;
  777. left: 2.5%;
  778. width: 95%;
  779. height: 80rpx;
  780. line-height: 80rpx;
  781. text-align: center;
  782. border-radius: 20rpx;
  783. }
  784. </style>