爱简收旧衣按件回收前端代码仓库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

964 lines
30 KiB

1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
  1. <template>
  2. <view class="inspect-result-container" :class="{ 'popup-open': isPopupOpen }">
  3. <!-- 顶部导航栏 -->
  4. <view class="nav-bar">
  5. <view class="back" @tap="goBack">
  6. <uni-icons type="left" size="20" color="#222" />
  7. </view>
  8. <text class="nav-title">步骤二合格质检</text>
  9. <view class="nav-icons">
  10. <uni-icons type="more" size="24" color="#222" />
  11. </view>
  12. </view>
  13. <view class="main-content">
  14. <!-- 合格产品卡片 -->
  15. <view v-if="hasQualifiedItems" class="result-card">
  16. <view class="card-title">合格产品</view>
  17. <view v-for="item in qualifiedList" :key="item.id" class="result-row">
  18. <view class="row-main">
  19. <view class="goods-name-section">
  20. <text class="goods-name">{{getItemName(item.id)}}</text>
  21. <text class="goods-brand">{{getItemBrand(item.id)}}</text>
  22. </view>
  23. <text class="row-price">¥ {{item.price}} <text class="row-unit">/</text></text>
  24. <text class="row-count">x{{item.count}}</text>
  25. <text class="row-total">¥{{item.total}}</text>
  26. </view>
  27. <view class="row-reason">
  28. <text class="reason-label">{{getItemName(item.id)}}</text>
  29. <view class="reason-select" @tap="selectReasonForQualified(item)">
  30. <text class="reason-placeholder" :class="{ 'selected': hasSelectedReason(item) }">
  31. {{ hasSelectedReason(item) ? '已选择' : '请选择理由(选填)' }}
  32. </text>
  33. <uni-icons type="right" size="18" color="#bbb" />
  34. </view>
  35. </view>
  36. </view>
  37. </view>
  38. <!-- 不合格产品卡片 -->
  39. <view v-if="hasUnqualifiedItems" class="result-card">
  40. <view class="card-title">不合格产品</view>
  41. <view v-for="group in unqualifiedGroups" :key="group.name" class="result-group">
  42. <view class="row-main">
  43. <view class="goods-name-section">
  44. <text class="goods-name">{{getItemName(group.id)}}</text>
  45. <text class="goods-brand">{{getItemBrand(group.id)}}</text>
  46. </view>
  47. <text class="row-price">¥ {{group.price}} <text class="row-unit">/</text></text>
  48. <text class="row-total">¥{{group.total}}</text>
  49. </view>
  50. <view v-for="item in group.items" :key="item.id" class="row-reason">
  51. <text class="reason-label">{{item.name}}</text>
  52. <view class="reason-select" @tap="selectReason(item)">
  53. <text class="reason-placeholder" :class="{ 'selected': hasSelectedReason(item) }">
  54. {{ hasSelectedReason(item) ? '已选择' : '请选择理由' }}
  55. </text>
  56. <uni-icons type="right" size="18" color="#bbb" />
  57. </view>
  58. </view>
  59. </view>
  60. </view>
  61. <!-- 不可回收产品卡片 -->
  62. <view v-if="hasUnrecyclableItems" class="result-card">
  63. <view class="card-title">不可回收产品</view>
  64. <view v-for="item in unrecyclableList" :key="item.id" class="result-row">
  65. <view class="row-main">
  66. <view class="goods-name-section">
  67. <text class="goods-name">{{getItemName(item.id)}}</text>
  68. <text class="goods-brand">{{getItemBrand(item.id)}}</text>
  69. </view>
  70. <text class="row-price">¥ {{item.price || '—'}} <text class="row-unit">/</text></text>
  71. <text class="row-count">x{{item.count}}</text>
  72. <text class="row-total">¥{{item.total}}</text>
  73. </view>
  74. <view class="row-reason">
  75. <text class="reason-label">{{getItemName(item.id)}}</text>
  76. <view class="reason-select" @tap="selectReasonForUnrecyclable(item)">
  77. <text class="reason-placeholder" :class="{ 'selected': hasSelectedReason(item) }">
  78. {{ hasSelectedReason(item) ? '已选择' : '请选择理由(选填)' }}
  79. </text>
  80. <uni-icons type="right" size="18" color="#bbb" />
  81. </view>
  82. </view>
  83. </view>
  84. </view>
  85. <!-- 回收信息卡片 -->
  86. <view class="result-card info-card">
  87. <view class="card-title-row">
  88. <text class="card-title">回收信息</text>
  89. <view class="status-tag">待质检</view>
  90. </view>
  91. <view class="info-row">
  92. <text class="info-label">订单编号</text>
  93. <text class="info-value copy-btn">{{ order?.ordeNo || 'RE82738127861525' }} 复制</text>
  94. </view>
  95. <view class="info-row">
  96. <text class="info-label">取件时间</text>
  97. <text class="info-value">{{ order?.goTime || '2025-03-20 11:00' }}</text>
  98. </view>
  99. </view>
  100. </view>
  101. <!-- 底部操作按钮 -->
  102. <view class="footer-btns">
  103. <button class="btn-outline" @tap="goPrev">上一步</button>
  104. <button class="btn-main" @tap="finishInspect">完成质检</button>
  105. </view>
  106. <!-- 理由弹窗 -->
  107. <uni-popup ref="reasonPopup" type="bottom" :mask-click="false" :safe-area="false" class="reason-popup-wrapper">
  108. <view class="reason-popup">
  109. <view class="popup-header">
  110. <text class="popup-close" @tap="closeReasonPopup">关闭</text>
  111. <text class="popup-title">{{ currentReasonTitle }}</text>
  112. </view>
  113. <scroll-view class="popup-content" scroll-y="true">
  114. <view class="popup-section">
  115. <text class="section-label">上传图片</text>
  116. <view class="img-list">
  117. <view class="img-item add" @tap="addReasonImg">
  118. <uni-icons type="plusempty" size="32" color="#bbb" />
  119. </view>
  120. <view v-for="(img, idx) in reasonImages" :key="idx" class="img-item">
  121. <image :src="img" class="img" />
  122. <view class="img-del" @tap="removeReasonImg(idx)">×</view>
  123. </view>
  124. </view>
  125. </view>
  126. <view class="popup-section">
  127. <text class="section-label">选择理由</text>
  128. <view v-for="(item, idx) in reasonOptions" :key="item" class="reason-row" @tap="toggleReason(idx)">
  129. <view :class="['checkbox', {checked: reasonChecked[idx]}]" />
  130. <text class="reason-text">{{ item }}</text>
  131. </view>
  132. </view>
  133. </scroll-view>
  134. <button class="popup-save-btn" @tap="saveReason">保存</button>
  135. </view>
  136. </uni-popup>
  137. </view>
  138. </template>
  139. <script>
  140. import pullRefreshMixin from '../mixins/pullRefreshMixin.js'
  141. import OSS from '@/utils/oss-upload/oss/index.js'
  142. export default {
  143. mixins: [pullRefreshMixin],
  144. data() {
  145. return {
  146. inspectData: null, // 从步骤一传过来的质检数据
  147. order: null, // 订单数据
  148. qualifiedList: [],
  149. unqualifiedGroups: [],
  150. unrecyclableList: [],
  151. reasonPopupVisible: false,
  152. currentReasonTitle: '',
  153. reasonImages: [],
  154. reasonOptions: ['大面积破损', '不可回收', '顽固污渍', '污渍无法清除', '异味严重', '带有危险物品'],
  155. reasonChecked: [false, true, false, true, false, false],
  156. currentReasonItem: null,
  157. isPopupOpen: false // 控制弹窗状态
  158. }
  159. },
  160. computed: {
  161. hasQualifiedItems() {
  162. return this.qualifiedList && this.qualifiedList.length > 0
  163. },
  164. hasUnqualifiedItems() {
  165. return this.unqualifiedGroups && this.unqualifiedGroups.length > 0
  166. },
  167. hasUnrecyclableItems() {
  168. return this.unrecyclableList && this.unrecyclableList.length > 0
  169. }
  170. },
  171. onLoad(options) {
  172. // 接收质检数据和订单数据
  173. if (options && options.resultData) {
  174. try {
  175. const resultData = JSON.parse(decodeURIComponent(options.resultData))
  176. this.inspectData = resultData.inspectResult
  177. this.order = resultData.order
  178. console.log('接收到的质检数据:', this.inspectData)
  179. console.log('接收到的订单数据:', this.order)
  180. this.processInspectData()
  181. } catch (error) {
  182. console.error('解析数据失败:', error)
  183. }
  184. }
  185. // 兼容旧的参数格式
  186. if (options && options.inspectData && !options.resultData) {
  187. try {
  188. this.inspectData = JSON.parse(decodeURIComponent(options.inspectData))
  189. console.log('接收到的质检数据:', this.inspectData)
  190. this.processInspectData()
  191. } catch (error) {
  192. console.error('解析质检数据失败:', error)
  193. }
  194. }
  195. },
  196. methods: {
  197. processInspectData() {
  198. if (!this.inspectData || !this.inspectData.list) return
  199. this.qualifiedList = []
  200. this.unqualifiedGroups = []
  201. this.unrecyclableList = []
  202. this.inspectData.list.forEach(item => {
  203. // 跳过不可回收分类
  204. if (item.id === 'unrecyclable') return
  205. // 从order中获取对应的商品信息
  206. const orderItem = this.order?.commonOrderList?.find(orderGoods => orderGoods.id == item.id)
  207. if (!orderItem) return
  208. const itemName = orderItem.title || '未知商品'
  209. const itemBrand = orderItem.pinName || ''
  210. const displayName = itemBrand ? `${itemName} ${itemBrand}` : itemName
  211. const itemNames = itemBrand ? `${itemName}` : itemName
  212. const itemPrice = orderItem.onePrice || 0
  213. // 统计各种状态的数量
  214. let qualifiedCount = 0
  215. let unqualifiedCount = 0
  216. let unrecyclableCount = 0
  217. item.commonOrderList.forEach(commonItem => {
  218. if (commonItem.testingStatus === 0) {
  219. qualifiedCount++
  220. } else if (commonItem.testingStatus === 1) {
  221. unqualifiedCount++
  222. } else if (commonItem.testingStatus === 2) {
  223. unrecyclableCount++
  224. }
  225. })
  226. // 处理合格产品
  227. if (qualifiedCount > 0) {
  228. // 获取合格商品的理由和图片信息
  229. const qualifiedCommonItem = item.commonOrderList.find(commonItem => commonItem.testingStatus === 0)
  230. const savedImages = qualifiedCommonItem?.testingImages ? qualifiedCommonItem.testingImages.split(',').filter(img => img) : []
  231. const savedReasons = this.parseTestingInstructions(qualifiedCommonItem?.testingInstructions || '')
  232. this.qualifiedList.push({
  233. id: item.id,
  234. name: displayName,
  235. shopClass: item.shopClass,
  236. shopId: item.shopId,
  237. price: itemPrice,
  238. count: qualifiedCount,
  239. total: item.price || 0, // 使用质检时填写的总金额
  240. reason: '',
  241. images: savedImages,
  242. reasons: savedReasons
  243. })
  244. }
  245. // 处理不合格产品
  246. if (unqualifiedCount > 0) {
  247. const unqualifiedItems = []
  248. let index = 1
  249. item.commonOrderList.forEach(commonItem => {
  250. if (commonItem.testingStatus === 1) {
  251. const savedImages = commonItem.testingImages ? commonItem.testingImages.split(',').filter(img => img) : []
  252. const savedReasons = this.parseTestingInstructions(commonItem.testingInstructions || '')
  253. unqualifiedItems.push({
  254. id: commonItem.id,
  255. name: `${itemNames}${index}`,
  256. shopClass: item.shopClass,
  257. shopId: item.shopId,
  258. reason: '',
  259. images: savedImages,
  260. reasons: savedReasons
  261. })
  262. index++
  263. }
  264. })
  265. if (unqualifiedItems.length > 0) {
  266. this.unqualifiedGroups.push({
  267. id: item.id,
  268. name: displayName,
  269. shopClass: item.shopClass,
  270. shopId: item.shopId,
  271. price: itemPrice,
  272. total: 0, // 不合格产品总价为0
  273. items: unqualifiedItems
  274. })
  275. }
  276. }
  277. // 处理不可回收产品
  278. if (unrecyclableCount > 0) {
  279. // 获取不可回收商品的理由和图片信息
  280. const unrecyclableCommonItem = item.commonOrderList.find(commonItem => commonItem.testingStatus === 2)
  281. const savedImages = unrecyclableCommonItem?.testingImages ? unrecyclableCommonItem.testingImages.split(',').filter(img => img) : []
  282. const savedReasons = this.parseTestingInstructions(unrecyclableCommonItem?.testingInstructions || '')
  283. this.unrecyclableList.push({
  284. id: item.id,
  285. name: displayName,
  286. shopClass: item.shopClass,
  287. shopId: item.shopId,
  288. price: '', // 不可回收产品不显示价格
  289. count: unrecyclableCount,
  290. total: 0,
  291. reason: '',
  292. images: savedImages,
  293. reasons: savedReasons
  294. })
  295. }
  296. })
  297. console.log('处理后的数据:', {
  298. qualifiedList: this.qualifiedList,
  299. unqualifiedGroups: this.unqualifiedGroups,
  300. unrecyclableList: this.unrecyclableList
  301. })
  302. },
  303. goBack() {
  304. uni.navigateBack()
  305. },
  306. goPrev() {
  307. uni.navigateBack()
  308. },
  309. finishInspect() {
  310. // 这里可以将最终的inspectData提交到服务器
  311. console.log('最终的质检数据:', JSON.stringify(this.inspectData, null, 2))
  312. // 可以调用API提交数据
  313. // this.$api('submitInspectResult', { inspectData: this.inspectData }, res => {
  314. // if (res && res.code === 200) {
  315. // uni.showToast({ title: '质检完成', icon: 'success' })
  316. // uni.navigateBack()
  317. // }
  318. // })
  319. uni.showToast({ title: '完成质检', icon: 'success' })
  320. },
  321. selectReason(item) {
  322. this.currentReasonItem = item
  323. this.currentReasonTitle = item.name
  324. this.reasonImages = item.images || []
  325. this.reasonChecked = item.reasons || Array(this.reasonOptions.length).fill(false)
  326. this.lockScroll()
  327. this.$refs.reasonPopup.open()
  328. },
  329. selectReasonForQualified(item) {
  330. this.currentReasonItem = item
  331. this.currentReasonTitle = this.getItemName(item.id)
  332. this.reasonImages = item.images || []
  333. this.reasonChecked = item.reasons || Array(this.reasonOptions.length).fill(false)
  334. this.lockScroll()
  335. this.$refs.reasonPopup.open()
  336. },
  337. selectReasonForUnrecyclable(item) {
  338. this.currentReasonItem = item
  339. this.currentReasonTitle = this.getItemName(item.id)
  340. this.reasonImages = item.images || []
  341. this.reasonChecked = item.reasons || Array(this.reasonOptions.length).fill(false)
  342. this.lockScroll()
  343. this.$refs.reasonPopup.open()
  344. },
  345. closeReasonPopup() {
  346. this.unlockScroll()
  347. this.$refs.reasonPopup.close()
  348. },
  349. addReasonImg() {
  350. // 检查是否已达到最大数量限制
  351. if (this.reasonImages.length >= 3) {
  352. uni.showToast({
  353. title: '最多只能上传3张图片',
  354. icon: 'none'
  355. })
  356. return
  357. }
  358. uni.chooseMedia({
  359. count: 3 - this.reasonImages.length, // 剩余可选择数量
  360. mediaType: ['image'], // 只选择图片
  361. sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机
  362. maxDuration: 30,
  363. camera: 'back',
  364. success: (res) => {
  365. console.log('选择的图片:', res.tempFiles)
  366. // 显示上传进度
  367. uni.showLoading({
  368. title: '上传中...'
  369. })
  370. // 处理选择的图片
  371. const uploadPromises = res.tempFiles.map(file => {
  372. return this.uploadImageToOSS(file.tempFilePath)
  373. })
  374. // 批量上传
  375. Promise.all(uploadPromises).then(urls => {
  376. this.reasonImages = this.reasonImages.concat(urls)
  377. uni.hideLoading()
  378. uni.showToast({
  379. title: '上传成功',
  380. icon: 'success'
  381. })
  382. }).catch(error => {
  383. console.error('上传失败:', error)
  384. uni.hideLoading()
  385. uni.showToast({
  386. title: '上传失败,请重试',
  387. icon: 'none'
  388. })
  389. })
  390. },
  391. fail: (error) => {
  392. console.error('选择图片失败:', error)
  393. uni.showToast({
  394. title: '选择图片失败',
  395. icon: 'none'
  396. })
  397. }
  398. })
  399. },
  400. async uploadImageToOSS(filePath) {
  401. try {
  402. // 使用项目中的OSS上传工具
  403. const url = await OSS.ossUpload(filePath)
  404. return url
  405. } catch (error) {
  406. console.error('OSS上传失败:', error)
  407. throw error
  408. }
  409. },
  410. removeReasonImg(idx) {
  411. this.reasonImages.splice(idx, 1)
  412. },
  413. toggleReason(idx) {
  414. this.$set(this.reasonChecked, idx, !this.reasonChecked[idx])
  415. },
  416. saveReason() {
  417. // 保存到当前item
  418. if (this.currentReasonItem) {
  419. this.currentReasonItem.images = [...this.reasonImages]
  420. this.currentReasonItem.reasons = [...this.reasonChecked]
  421. // 更新inspectData中的数据
  422. this.updateInspectData()
  423. }
  424. this.closeReasonPopup()
  425. },
  426. updateInspectData() {
  427. if (!this.inspectData || !this.currentReasonItem) return
  428. // 获取选中的理由ID(假设理由选项的索引就是ID)
  429. const selectedReasonIds = []
  430. this.reasonChecked.forEach((checked, index) => {
  431. if (checked) {
  432. selectedReasonIds.push(index.toString())
  433. }
  434. })
  435. // 将理由ID用逗号分割保存
  436. const testingInstructions = selectedReasonIds.join(',')
  437. // 将图片URL用逗号分割保存
  438. const testingImages = this.reasonImages.join(',')
  439. // 根据当前选择的商品类型,找到对应的inspectData项进行更新
  440. if (this.isQualifiedItem(this.currentReasonItem)) {
  441. this.updateQualifiedItemInspectData(testingInstructions, testingImages)
  442. } else if (this.isUnqualifiedItem(this.currentReasonItem)) {
  443. this.updateUnqualifiedItemInspectData(testingInstructions, testingImages)
  444. } else if (this.isUnrecyclableItem(this.currentReasonItem)) {
  445. this.updateUnrecyclableItemInspectData(testingInstructions, testingImages)
  446. }
  447. console.log('更新后的inspectData:', JSON.stringify(this.inspectData, null, 2))
  448. },
  449. isQualifiedItem(item) {
  450. return this.qualifiedList.some(qualified => qualified.id === item.id)
  451. },
  452. isUnqualifiedItem(item) {
  453. return this.unqualifiedGroups.some(group =>
  454. group.items.some(unqualified => unqualified.id === item.id)
  455. )
  456. },
  457. isUnrecyclableItem(item) {
  458. return this.unrecyclableList.some(unrecyclable => unrecyclable.id === item.id)
  459. },
  460. updateQualifiedItemInspectData(testingInstructions, testingImages) {
  461. const inspectItem = this.inspectData.list.find(item => item.id == this.currentReasonItem.id)
  462. if (inspectItem && inspectItem.commonOrderList) {
  463. // 为合格的商品更新所有状态为0的项
  464. inspectItem.commonOrderList.forEach(commonItem => {
  465. if (commonItem.testingStatus === 0) {
  466. commonItem.testingInstructions = testingInstructions
  467. commonItem.testingImages = testingImages
  468. }
  469. })
  470. }
  471. },
  472. updateUnqualifiedItemInspectData(testingInstructions, testingImages) {
  473. // 对于不合格商品,需要根据具体的commonOrderList项ID来更新
  474. this.unqualifiedGroups.forEach(group => {
  475. const targetItem = group.items.find(item => item.id === this.currentReasonItem.id)
  476. if (targetItem) {
  477. // 找到对应的inspectData项
  478. const inspectItem = this.inspectData.list.find(item => item.id == group.id)
  479. if (inspectItem && inspectItem.commonOrderList) {
  480. // 根据不合格项的ID找到对应的commonOrderList项
  481. const commonItem = inspectItem.commonOrderList.find(common => common.id === targetItem.id)
  482. if (commonItem) {
  483. commonItem.testingInstructions = testingInstructions
  484. commonItem.testingImages = testingImages
  485. }
  486. }
  487. }
  488. })
  489. },
  490. updateUnrecyclableItemInspectData(testingInstructions, testingImages) {
  491. const inspectItem = this.inspectData.list.find(item => item.id == this.currentReasonItem.id)
  492. if (inspectItem && inspectItem.commonOrderList) {
  493. // 为不可回收的商品更新所有状态为2的项
  494. inspectItem.commonOrderList.forEach(commonItem => {
  495. if (commonItem.testingStatus === 2) {
  496. commonItem.testingInstructions = testingInstructions
  497. commonItem.testingImages = testingImages
  498. }
  499. })
  500. }
  501. },
  502. lockScroll() {
  503. // 禁用页面滚动
  504. this.isPopupOpen = true
  505. },
  506. unlockScroll() {
  507. // 恢复页面滚动
  508. this.isPopupOpen = false
  509. },
  510. async onRefresh() {
  511. await this.refreshData && this.refreshData()
  512. },
  513. refreshData() {
  514. // 可根据实际需求刷新质检结果数据
  515. // 例如重新请求接口或重置数据
  516. },
  517. getItemName(id) {
  518. if (this.order && this.order.commonOrderList) {
  519. const orderItem = this.order.commonOrderList.find(item => item.id == id)
  520. return orderItem ? (orderItem.title || '未知商品') : '未知商品'
  521. }
  522. return '未知商品'
  523. },
  524. getItemBrand(id) {
  525. if (this.order && this.order.commonOrderList) {
  526. const orderItem = this.order.commonOrderList.find(item => item.id == id)
  527. return orderItem ? (orderItem.pinName || '') : ''
  528. }
  529. return ''
  530. },
  531. parseTestingInstructions(instructions) {
  532. // 解析理由ID字符串,返回布尔数组
  533. const reasonArray = Array(this.reasonOptions.length).fill(false)
  534. if (instructions) {
  535. const selectedIds = instructions.split(',').map(id => id.trim()).filter(id => id)
  536. selectedIds.forEach(id => {
  537. const index = parseInt(id)
  538. if (index >= 0 && index < reasonArray.length) {
  539. reasonArray[index] = true
  540. }
  541. })
  542. }
  543. return reasonArray
  544. },
  545. hasSelectedReason(item) {
  546. // 检查该商品是否已选择理由或上传图片
  547. return (item.reasons && item.reasons.some(checked => checked)) ||
  548. (item.images && item.images.length > 0)
  549. }
  550. }
  551. }
  552. </script>
  553. <style lang="scss" scoped>
  554. .inspect-result-container {
  555. min-height: 100vh;
  556. background: #f8f8f8;
  557. display: flex;
  558. flex-direction: column;
  559. &.popup-open {
  560. overflow: hidden;
  561. position: fixed;
  562. width: 100%;
  563. height: 100vh;
  564. }
  565. }
  566. .nav-bar {
  567. display: flex;
  568. align-items: center;
  569. height: calc(150rpx + var(--status-bar-height));
  570. padding: 0 32rpx;
  571. padding-top: var(--status-bar-height);
  572. background: #fff;
  573. position: fixed;
  574. top: 0;
  575. left: 0;
  576. right: 0;
  577. z-index: 999;
  578. box-sizing: border-box;
  579. box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.03);
  580. .back {
  581. padding: 20rpx;
  582. margin-left: -20rpx;
  583. }
  584. .nav-title {
  585. flex: 1;
  586. text-align: center;
  587. font-size: 32rpx;
  588. font-weight: 500;
  589. color: #222;
  590. }
  591. .nav-icons {
  592. display: flex;
  593. align-items: center;
  594. gap: 12px;
  595. }
  596. }
  597. .main-content {
  598. margin-top: calc(200rpx + var(--status-bar-height));
  599. display: flex;
  600. flex-direction: column;
  601. background: none;
  602. padding-bottom: 120px;
  603. }
  604. .result-card {
  605. background: #fff;
  606. border-radius: 24px;
  607. box-shadow: 0 4px 24px rgba(0,0,0,0.06);
  608. margin: 0 24px 24px 24px;
  609. padding: 24px 24px 0 24px;
  610. }
  611. .card-title {
  612. font-size: 16px;
  613. font-weight: bold;
  614. color: #222;
  615. margin-bottom: 18px;
  616. }
  617. .card-title-row {
  618. display: flex;
  619. align-items: center;
  620. justify-content: space-between;
  621. margin-bottom: 18px;
  622. }
  623. .status-tag {
  624. background: #fff7e6;
  625. color: #ffb400;
  626. font-size: 12px;
  627. border-radius: 12px;
  628. padding: 2px 14px;
  629. font-weight: 400;
  630. height: 22px;
  631. display: flex;
  632. align-items: center;
  633. }
  634. .result-row, .result-group {
  635. margin-bottom: 18px;
  636. }
  637. .row-main {
  638. display: flex;
  639. align-items: center;
  640. .goods-name-section {
  641. display: flex;
  642. flex-direction: column;
  643. margin-right: 8px;
  644. .goods-name {
  645. font-size: 14px;
  646. font-weight: bold;
  647. color: #222;
  648. line-height: 1.2;
  649. }
  650. .goods-brand {
  651. font-size: 11px;
  652. color: #999;
  653. font-weight: normal;
  654. line-height: 1.2;
  655. margin-top: 2px;
  656. }
  657. }
  658. .row-name {
  659. font-size: 14px;
  660. font-weight: bold;
  661. color: #222;
  662. margin-right: 8px;
  663. }
  664. .row-price {
  665. font-size: 13px;
  666. color: #ffb400;
  667. font-weight: bold;
  668. margin-right: 8px;
  669. .row-unit {
  670. font-size: 11px;
  671. color: #bbb;
  672. }
  673. }
  674. .row-count {
  675. font-size: 13px;
  676. color: #888;
  677. margin-right: 8px;
  678. }
  679. .row-total {
  680. font-size: 14px;
  681. color: #222;
  682. font-weight: bold;
  683. margin-left: auto;
  684. }
  685. }
  686. .row-reason {
  687. display: flex;
  688. align-items: center;
  689. margin-top: 8px;
  690. .reason-label {
  691. font-size: 13px;
  692. color: #bbb;
  693. min-width: 80px;
  694. }
  695. .reason-input {
  696. flex: 1;
  697. height: 36px;
  698. border-radius: 12px;
  699. background: #f6f6f6;
  700. border: none;
  701. font-size: 14px;
  702. color: #222;
  703. padding-left: 12px;
  704. margin-left: 8px;
  705. }
  706. .reason-select {
  707. flex: 1;
  708. display: flex;
  709. align-items: center;
  710. height: 36px;
  711. border-radius: 12px;
  712. background: #f6f6f6;
  713. font-size: 14px;
  714. color: #bbb;
  715. padding-left: 12px;
  716. margin-left: 8px;
  717. justify-content: flex-end;
  718. }
  719. .reason-placeholder {
  720. color: #bbb;
  721. font-size: 14px;
  722. &.selected {
  723. color: #52c41a;
  724. font-weight: 500;
  725. }
  726. }
  727. }
  728. .info-card {
  729. background: #fff;
  730. border-radius: 24px;
  731. box-shadow: 0 4px 24px rgba(0,0,0,0.06);
  732. margin: 0 24px 24px 24px;
  733. padding: 24px 24px 0 24px;
  734. }
  735. .info-row {
  736. display: flex;
  737. align-items: center;
  738. margin-bottom: 16px;
  739. .info-label {
  740. font-size: 13px;
  741. color: #bbb;
  742. min-width: 80px;
  743. }
  744. .info-value {
  745. font-size: 13px;
  746. color: #222;
  747. margin-left: 8px;
  748. }
  749. .copy-btn {
  750. color: #ffb400;
  751. margin-left: 8px;
  752. }
  753. }
  754. .footer-btns {
  755. position: fixed;
  756. left: 0;
  757. right: 0;
  758. bottom: 0;
  759. background: #fff;
  760. display: flex;
  761. gap: 16px;
  762. padding: 12px 16px 24px 16px;
  763. z-index: 101;
  764. .btn-outline {
  765. flex: 1;
  766. height: 40px;
  767. border-radius: 16px;
  768. border: 1px solid #ffe09a;
  769. color: #ffb400;
  770. background: #fff0d2;
  771. font-size: 15px;
  772. font-weight: 500;
  773. box-shadow: none;
  774. padding: 0 18px;
  775. }
  776. .btn-main {
  777. flex: 1;
  778. height: 40px;
  779. border-radius: 16px;
  780. background: linear-gradient(90deg, #ffd01e 0%, #ffac04 100%);
  781. color: #fff;
  782. border: none;
  783. font-size: 15px;
  784. font-weight: 500;
  785. box-shadow: none;
  786. padding: 0 18px;
  787. }
  788. }
  789. .reason-popup-wrapper {
  790. z-index: 10000 !important;
  791. }
  792. .reason-popup-wrapper .uni-popup__wrapper {
  793. z-index: 10000 !important;
  794. }
  795. .reason-popup {
  796. background: #fff;
  797. border-radius: 24px 24px 0 0;
  798. padding: 0;
  799. height: 75vh;
  800. position: relative;
  801. z-index: 9999;
  802. .popup-header {
  803. display: flex;
  804. align-items: center;
  805. justify-content: center;
  806. height: 60px;
  807. border-bottom: 1px solid #f0f0f0;
  808. position: relative;
  809. .popup-close {
  810. position: absolute;
  811. left: 20px;
  812. color: #666;
  813. font-size: 16px;
  814. }
  815. .popup-title {
  816. font-size: 18px;
  817. font-weight: 600;
  818. color: #222;
  819. }
  820. }
  821. .popup-content {
  822. height: calc(75vh - 60px - 82px);
  823. overflow-y: auto;
  824. }
  825. .popup-section {
  826. padding: 0 20px 24px 20px;
  827. &:first-child {
  828. padding-top: 24px;
  829. }
  830. .section-label {
  831. font-size: 16px;
  832. color: #222;
  833. margin-bottom: 16px;
  834. display: block;
  835. font-weight: 500;
  836. }
  837. .img-list {
  838. display: flex;
  839. gap: 12px;
  840. margin-bottom: 32px;
  841. .img-item {
  842. width: 80px;
  843. height: 80px;
  844. border-radius: 12px;
  845. background: #f8f8f8;
  846. position: relative;
  847. .img {
  848. width: 100%;
  849. height: 100%;
  850. border-radius: 12px;
  851. object-fit: cover;
  852. }
  853. .img-del {
  854. position: absolute;
  855. top: -6px;
  856. right: -6px;
  857. width: 24px;
  858. height: 24px;
  859. background: rgba(0,0,0,0.6);
  860. color: #fff;
  861. border-radius: 50%;
  862. text-align: center;
  863. line-height: 24px;
  864. font-size: 16px;
  865. font-weight: bold;
  866. }
  867. &.add {
  868. display: flex;
  869. align-items: center;
  870. justify-content: center;
  871. background: #f8f8f8;
  872. border: 2px dashed #ddd;
  873. color: #bbb;
  874. }
  875. }
  876. }
  877. .reason-row {
  878. display: flex;
  879. align-items: center;
  880. padding: 16px 0;
  881. border-bottom: 1px solid #f0f0f0;
  882. &:last-child {
  883. border-bottom: none;
  884. }
  885. .checkbox {
  886. width: 20px;
  887. height: 20px;
  888. border-radius: 4px;
  889. border: 2px solid #ddd;
  890. margin-right: 12px;
  891. background: #fff;
  892. position: relative;
  893. &.checked {
  894. border-color: #ffb400;
  895. background: #ffb400;
  896. &::after {
  897. content: '✓';
  898. position: absolute;
  899. top: 50%;
  900. left: 50%;
  901. transform: translate(-50%, -50%);
  902. color: #fff;
  903. font-size: 12px;
  904. font-weight: bold;
  905. }
  906. }
  907. }
  908. .reason-text {
  909. font-size: 16px;
  910. color: #222;
  911. line-height: 1.4;
  912. }
  913. }
  914. }
  915. .popup-save-btn {
  916. position: fixed;
  917. bottom: 34px;
  918. left: 20px;
  919. right: 20px;
  920. height: 48px;
  921. border-radius: 24px;
  922. background: linear-gradient(90deg, #ffd01e 0%, #ffac04 100%);
  923. color: #fff;
  924. font-size: 18px;
  925. font-weight: 600;
  926. border: none;
  927. display: flex;
  928. align-items: center;
  929. justify-content: center;
  930. box-shadow: 0 4px 12px rgba(255, 172, 4, 0.3);
  931. }
  932. }
  933. </style>