爱简收旧衣按件回收前端代码仓库
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.

995 lines
28 KiB

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