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

1348 lines
39 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. <template>
  2. <view class="inspect-container">
  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="scan" size="24" color="#222" />
  11. </view>
  12. </view>
  13. <view class="main-content">
  14. <!-- 左侧分类导航 -->
  15. <view class="category-nav">
  16. <view v-for="(cat, idx) in categories" :key="cat.title"
  17. :class="['category-item', { active: idx === currentCategory }]" @tap="switchCategory(idx)">
  18. <text>{{ cat.title }}</text>
  19. <view v-if="cat.badge" class="category-badge">{{ cat.badge }}</view>
  20. </view>
  21. </view>
  22. <!-- 右侧商品卡片区 -->
  23. <scroll-view class="goods-list" scroll-y @scrolltolower="loadMoreGoods">
  24. <view v-for="(item, idx) in currentGoods" :key="item.id" class="goods-card">
  25. <view class="goods-header">
  26. <image :src="item.image" class="goods-img" />
  27. <view class="goods-info">
  28. <view class="goods-title-row">
  29. <text class="goods-name">{{ item.name }}</text>
  30. <text class="goods-price">¥ {{ item.price }} <text class="goods-unit">/{{item.unit}}</text></text>
  31. </view>
  32. <text class="goods-desc">{{ item.desc }}</text>
  33. </view>
  34. </view>
  35. <view class="goods-row">
  36. <text class="row-label">合格数量</text>
  37. <view class="num-ctrl">
  38. <button class="num-btn" @tap="changeNum(item, 'qualified', -1)">-</button>
  39. <text class="num">{{ item.qualified }}</text>
  40. <button
  41. class="num-btn"
  42. @tap="changeNum(item, 'qualified', 1)"
  43. >+</button>
  44. </view>
  45. </view>
  46. <view class="goods-row">
  47. <text class="row-label">总金额</text>
  48. <input class="amount-input" :value="getInspectPrice(item)" @input="updateInspectPrice(item, $event)" placeholder="请输入金额" :disabled="!item.qualified || item.qualified === 0" />
  49. </view>
  50. </view>
  51. <view v-if="loadingMore" class="loading-more">加载中...</view>
  52. <view v-else-if="finished" class="loading-more">没有更多了</view>
  53. </scroll-view>
  54. </view>
  55. <!-- 底部操作按钮 -->
  56. <view class="footer-btns">
  57. <button class="btn-outline" @tap="goBack">返回订单详情</button>
  58. <button class="btn-main" @tap="goNext">下一步</button>
  59. </view>
  60. <!-- 品牌索引弹窗 -->
  61. <view v-if="showBrandPopup" class="brand-popup-mask">
  62. <view class="brand-popup">
  63. <view class="brand-popup-header">
  64. <text class="brand-popup-close" @click="closeBrandPopup">关闭</text>
  65. <text class="brand-popup-title">可回收的品牌</text>
  66. </view>
  67. <view class="brand-popup-search">
  68. <input class="brand-search-input" v-model="brandSearch" placeholder="请输入要查询的内容" @input="onBrandSearchInput" />
  69. </view>
  70. <scroll-view class="brand-popup-list" scroll-y>
  71. <view v-for="letter in brandIndexList" :key="letter" :id="'brand-letter-' + letter">
  72. <view class="brand-letter">{{letter}}</view>
  73. <view v-for="brand in brandList.filter(b => b.letter === letter)" :key="brand.name" class="brand-item" @click="openBrandConfirm(brand)">
  74. <image :src="brand.logo" class="brand-logo" mode="aspectFit" />
  75. <text class="brand-name">{{brand.name}}</text>
  76. </view>
  77. </view>
  78. </scroll-view>
  79. </view>
  80. </view>
  81. <!-- 品牌确认弹窗 -->
  82. <view v-if="showBrandConfirm" class="brand-confirm-mask" @click.self="closeBrandConfirm">
  83. <view class="brand-confirm-popup">
  84. <view class="brand-confirm-title">品牌确认提示</view>
  85. <view class="brand-confirm-logo-wrap">
  86. <image :src="brandConfirmInfo.logo" class="brand-confirm-logo" mode="aspectFit" />
  87. </view>
  88. <view class="brand-confirm-name">{{ brandConfirmInfo.name }}</view>
  89. <view class="brand-confirm-desc">请确认所选品牌是否与实物品牌信息一致否则将无法进行回收</view>
  90. <view class="brand-confirm-btn-row">
  91. <button class="brand-confirm-btn retry" @click="closeBrandConfirm">重新选择</button>
  92. <button class="brand-confirm-btn confirm" @click="confirmBrand">确认一致</button>
  93. </view>
  94. </view>
  95. </view>
  96. </view>
  97. </template>
  98. <script>
  99. export default {
  100. data() {
  101. return {
  102. statusBarHeight: 0,
  103. currentCategory: 0,
  104. orderId: '',
  105. order: null, // 订单数据
  106. currentGoods: [], // 当前显示的商品列表
  107. inspectResult: {}, // 质检结果对象,按照新的数据格式
  108. allProducts: {}, // { [categoryId]: [商品数组] }
  109. allProductsPage: {}, // { [categoryId]: 当前已加载页码 }
  110. allProductsTotal: {}, // { [categoryId]: 总数 }
  111. pageSize: 10,
  112. loadingMore: false,
  113. finished: false,
  114. // 品牌选择相关
  115. showBrandPopup: false,
  116. brandList: [],
  117. brandSearch: '',
  118. currentProductId: null,
  119. pendingBrandIndex: null,
  120. brandConfirmInfo: {},
  121. showBrandConfirm: false,
  122. searchTimer: null,
  123. brandIndexList: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'],
  124. }
  125. },
  126. computed: {
  127. categories() {
  128. const list = getApp().globalData.pricePreviewList || []
  129. console.log('categories计算 - pricePreviewList:', list.length)
  130. // 显示所有分类,不再根据订单数据筛选
  131. const allCategories = list.filter(item => item.pid === '0').sort((a, b) => a.sort - b.sort)
  132. console.log('categories计算 - allCategories:', allCategories.length)
  133. // 为每个分类计算数量
  134. const categoriesWithCount = allCategories.map(category => {
  135. const count = this.getCategoryItemCountDirect(category.id)
  136. return {
  137. ...category,
  138. badge: count > 0 ? count : null
  139. }
  140. })
  141. // 新增不可回收和质量问题分类
  142. const extra = [
  143. { id: 'unrecyclable', title: '不可回收', badge: this.getUnrecyclableCount() },
  144. { id: 'quality_issue', title: '质量问题', badge: this.getQualityIssueCount() }
  145. ]
  146. const result = [...categoriesWithCount, ...extra]
  147. console.log('categories计算 - 最终结果:', result.map(c => ({ id: c.id, title: c.title, badge: c.badge })))
  148. return result
  149. }
  150. },
  151. methods: {
  152. initInspectResult() {
  153. // 只初始化空的list
  154. this.inspectResult = {
  155. id: this.order ? this.order.id : '',
  156. list: []
  157. }
  158. },
  159. // 获取分类商品数量(直接方法,避免递归)
  160. getCategoryItemCountDirect(categoryId) {
  161. if (categoryId === 'unrecyclable') {
  162. return this.getUnrecyclableCount()
  163. }
  164. if (categoryId === 'quality_issue') {
  165. return this.getQualityIssueCount()
  166. }
  167. let totalCount = 0
  168. if (this.inspectResult.list && this.inspectResult.list.length > 0) {
  169. this.inspectResult.list.forEach(inspectItem => {
  170. if (inspectItem.categoryId === categoryId) {
  171. totalCount += inspectItem.qualifiedNum || 0
  172. }
  173. })
  174. }
  175. return totalCount
  176. },
  177. // 获取当前分类ID(避免递归)
  178. getCurrentCategoryId() {
  179. const list = getApp().globalData.pricePreviewList || []
  180. const allCategories = list.filter(item => item.pid === '0').sort((a, b) => a.sort - b.sort)
  181. const extra = [
  182. { id: 'unrecyclable', title: '不可回收' },
  183. { id: 'quality_issue', title: '质量问题' }
  184. ]
  185. const allCategoriesWithExtra = [...allCategories, ...extra]
  186. return allCategoriesWithExtra[this.currentCategory]?.id
  187. },
  188. // 获取分类商品数量(保持原有方法名兼容性)
  189. getCategoryItemCount(categoryId) {
  190. return this.getCategoryItemCountDirect(categoryId)
  191. },
  192. // 获取不可回收数量
  193. getUnrecyclableCount() {
  194. // 从inspectResult中获取不可回收的数量
  195. const unrecyclableItem = this.inspectResult.list?.find(item => item.id === 'unrecyclable')
  196. return unrecyclableItem ? unrecyclableItem.unrecyclable : 0
  197. },
  198. // 获取质量问题数量
  199. getQualityIssueCount() {
  200. // 从inspectResult中获取质量问题的数量
  201. const qualityIssueItem = this.inspectResult.list?.find(item => item.id === 'quality_issue')
  202. return qualityIssueItem ? qualityIssueItem.noQualifiedNum : 0
  203. },
  204. fetchGoodsList(categoryId, page = 1, callback) {
  205. this.$api('getClassGoodsList', {
  206. classId: categoryId,
  207. pageNo: page,
  208. pageSize: this.pageSize
  209. }, res => {
  210. if (res.code === 200 && res.result && Array.isArray(res.result.records)) {
  211. const oldList = this.allProducts[categoryId] || []
  212. const newList = page === 1 ? res.result.records : oldList.concat(res.result.records)
  213. this.$set(this.allProducts, categoryId, newList)
  214. this.$set(this.allProductsPage, categoryId, page)
  215. this.$set(this.allProductsTotal, categoryId, res.result.total)
  216. this.updateCurrentGoods()
  217. }
  218. if (callback) callback()
  219. })
  220. },
  221. updateCurrentGoods() {
  222. const currentCategoryId = this.categories[this.currentCategory]?.id
  223. // 不可回收分类内容
  224. if (currentCategoryId === 'unrecyclable') {
  225. // 从inspectResult中获取不可回收的数量
  226. const unrecyclableItem = this.inspectResult.list?.find(item => item.id === 'unrecyclable')
  227. const unrecyclableCount = unrecyclableItem ? unrecyclableItem.unrecyclable : 0
  228. this.currentGoods = [{
  229. id: 'unrecyclable-1',
  230. image: '/static/回收/衣物.png',
  231. name: '不可回收品类',
  232. price: '—',
  233. desc: '允许脏破烂,160码以上',
  234. qualified: unrecyclableCount,
  235. amount: '',
  236. originalNum: 0 // 不设置最大数量限制
  237. }]
  238. return
  239. }
  240. // 质量问题分类内容
  241. if (currentCategoryId === 'quality_issue') {
  242. // 从inspectResult中获取质量问题的数量
  243. const qualityIssueItem = this.inspectResult.list?.find(item => item.id === 'quality_issue')
  244. const qualityIssueCount = qualityIssueItem ? qualityIssueItem.noQualifiedNum : 0
  245. this.currentGoods = [{
  246. id: 'quality-issue-1',
  247. image: '/static/回收/衣物.png',
  248. name: '质量问题品类',
  249. price: '—',
  250. desc: '存在质量问题,无法正常回收',
  251. qualified: qualityIssueCount,
  252. amount: '',
  253. originalNum: 0 // 不设置最大数量限制
  254. }]
  255. return
  256. }
  257. // 从API获取的商品数据
  258. const categoryGoods = this.allProducts[currentCategoryId] || []
  259. // 将API商品数据转换为质检页面格式,并合并inspectResult.list中已选状态(shopId和id双向查找)
  260. const goodsList = categoryGoods.map((item, index) => {
  261. // 从订单数据中查找对应的商品
  262. const orderItem = this.getOrderItemByProductId(item.id)
  263. let itemId = item.id
  264. if (orderItem && orderItem.id) {
  265. itemId = orderItem.id
  266. }
  267. const inspectItem = this.inspectResult.list?.find(listItem =>
  268. listItem.shopId == item.id || listItem.shopId == itemId || listItem.id == item.id || listItem.id == itemId
  269. )
  270. return {
  271. id: item.id,
  272. image: item.image || '/static/回收/衣物.png',
  273. name: item.name,
  274. price: item.price || 0,
  275. desc: item.service || '允许脏破烂,160码以上',
  276. qualified: inspectItem ? inspectItem.qualifiedNum : 0,
  277. amount: inspectItem ? inspectItem.price : '', // 只用inspectResult里的金额
  278. originalNum: 0,
  279. estimatedPrice: orderItem ? orderItem.estimatedPrice : 0,
  280. originalId: item.id,
  281. isPin: item.isPin || 'N',
  282. orderItem: orderItem
  283. }
  284. })
  285. this.currentGoods = goodsList
  286. },
  287. // 根据商品ID从订单数据中查找对应商品
  288. getOrderItemByProductId(productId) {
  289. if (!this.order || !this.order.commonOrderList) {
  290. return null
  291. }
  292. // 支持id和shopId双向查找
  293. return this.order.commonOrderList.find(item => item.id == productId || item.shopId == productId)
  294. },
  295. goBack() {
  296. uni.navigateBack()
  297. },
  298. goNext() {
  299. // 检测是否所有商品都已完成质检和填写价格
  300. const validationResult = this.validateInspectData()
  301. if (!validationResult.isValid) {
  302. uni.showToast({
  303. title: validationResult.message,
  304. icon: 'none',
  305. duration: 2000
  306. })
  307. return
  308. }
  309. // 构造传递给步骤二的完整数据
  310. const resultData = {
  311. inspectResult: this.inspectResult,
  312. order: this.order // 同时传递订单信息
  313. }
  314. console.log('resultDataStr:', resultData)
  315. const resultDataStr = encodeURIComponent(JSON.stringify(resultData))
  316. uni.navigateTo({
  317. url: `/pages/manager/inspect-result?resultData=${resultDataStr}`
  318. })
  319. },
  320. validateInspectData() {
  321. if (!this.inspectResult.list || this.inspectResult.list.length === 0) {
  322. return {
  323. isValid: false,
  324. message: '没有质检数据'
  325. }
  326. }
  327. for (const item of this.inspectResult.list) {
  328. // 不可回收和质量问题不需要校验金额
  329. if (item.id === 'unrecyclable' || item.id === 'quality_issue') {
  330. continue
  331. }
  332. // 只判断是否填写了commonOrderList(即有质检结果)
  333. if (!item.commonOrderList || item.commonOrderList.length === 0) {
  334. return {
  335. isValid: false,
  336. message: '有商品未完成质检选择'
  337. }
  338. }
  339. // 检查是否有空的testingStatus
  340. const hasEmptyStatus = item.commonOrderList.some(commonItem =>
  341. commonItem.testingStatus === '' || commonItem.testingStatus === null || commonItem.testingStatus === undefined
  342. )
  343. if (hasEmptyStatus) {
  344. return {
  345. isValid: false,
  346. message: '有商品未完成质检选择'
  347. }
  348. }
  349. // 检查总金额(price)是否填写(不可回收和质量问题不校验)
  350. if (!item.price || item.price === 0 || item.price === '') {
  351. return {
  352. isValid: false,
  353. message: '有商品未填写总金额'
  354. }
  355. }
  356. }
  357. return {
  358. isValid: true,
  359. message: ''
  360. }
  361. },
  362. switchCategory(idx) {
  363. this.currentCategory = idx
  364. this.loadingMore = false
  365. this.finished = false
  366. const categoryId = this.categories[idx]?.id
  367. if (categoryId === 'unrecyclable' || categoryId === 'quality_issue') {
  368. // 不可回收和质量问题分类直接更新商品列表
  369. this.updateCurrentGoods()
  370. return
  371. }
  372. // 如果该分类的商品还没有加载,调用API获取
  373. if (!this.allProducts[categoryId]) {
  374. this.fetchGoodsList(categoryId, 1)
  375. } else {
  376. // 已有数据,直接更新显示
  377. this.updateCurrentGoods()
  378. }
  379. },
  380. changeNum(item, key, delta) {
  381. if (key === 'qualified') {
  382. const currentCategoryId = this.categories[this.currentCategory]?.id
  383. // 处理不可回收和质量问题
  384. if (item.id === 'unrecyclable-1' || item.id === 'quality-issue-1') {
  385. const newQualified = Math.max(0, (item.qualified || 0) + delta)
  386. this.$set(item, 'qualified', newQualified)
  387. this.updateInspectResult(item, 'qualified', delta, currentCategoryId)
  388. this.$forceUpdate()
  389. return
  390. }
  391. // 处理普通商品
  392. const newQualified = Math.max(0, (item.qualified || 0) + delta)
  393. this.$set(item, 'qualified', newQualified)
  394. this.updateInspectResult(item, 'qualified', delta, currentCategoryId)
  395. // 新增:如果数量为0,移除该商品对象(包括不可回收和质量问题)
  396. if (newQualified === 0) {
  397. this.removeInspectItem(item)
  398. }
  399. this.$forceUpdate()
  400. console.log('更新后的inspectResult:', JSON.stringify(this.inspectResult, null, 2))
  401. }
  402. },
  403. // 修改:移除商品对象方法,支持不可回收和质量问题
  404. removeInspectItem(item) {
  405. // 判断不可回收和质量问题
  406. if (item.id === 'unrecyclable-1' || item.id === 'unrecyclable') {
  407. const idx = this.inspectResult.list.findIndex(listItem => listItem.id === 'unrecyclable')
  408. if (idx !== -1) {
  409. this.inspectResult.list.splice(idx, 1)
  410. }
  411. return
  412. }
  413. if (item.id === 'quality-issue-1' || item.id === 'quality_issue') {
  414. const idx = this.inspectResult.list.findIndex(listItem => listItem.id === 'quality_issue')
  415. if (idx !== -1) {
  416. this.inspectResult.list.splice(idx, 1)
  417. }
  418. return
  419. }
  420. let itemId = item.originalId || item.id
  421. const orderItem = this.getOrderItemByProductId(itemId)
  422. if (orderItem && orderItem.id) {
  423. itemId = orderItem.id
  424. }
  425. // shopId 匹配
  426. const idx = this.inspectResult.list.findIndex(listItem => listItem.shopId == (orderItem ? (orderItem.shopId || orderItem.id) : itemId))
  427. if (idx !== -1) {
  428. this.inspectResult.list.splice(idx, 1)
  429. }
  430. },
  431. updateInspectResult(item, type, delta, currentCategoryId) {
  432. // 处理不可回收分类
  433. if (item.id === 'unrecyclable-1') {
  434. let inspectItem = this.inspectResult.list.find(listItem => listItem.id === 'unrecyclable')
  435. if (!inspectItem && delta > 0) {
  436. inspectItem = {
  437. id: 'unrecyclable',
  438. price: 0,
  439. qualifiedNum: 0,
  440. noQualifiedNum: 0,
  441. unrecyclable: 0, // 初始值为0
  442. shopId: '',
  443. pinId: '',
  444. categoryId: '',
  445. commonOrderList: [] // 初始为空数组
  446. }
  447. this.inspectResult.list.push(inspectItem)
  448. }
  449. if (!inspectItem) return
  450. if (type === 'qualified' && delta > 0) {
  451. inspectItem.unrecyclable++
  452. // 增加一个commonOrderList对象
  453. inspectItem.commonOrderList.push({
  454. testingInstructions: '',
  455. testingImages: '',
  456. testingStatus: 2
  457. })
  458. } else if (type === 'qualified' && delta < 0) {
  459. inspectItem.unrecyclable = Math.max(0, inspectItem.unrecyclable - 1)
  460. // 减少一个commonOrderList对象
  461. if (inspectItem.commonOrderList.length > 0) {
  462. inspectItem.commonOrderList.pop()
  463. }
  464. // 新增:如果减到0,移除该对象
  465. if (inspectItem.unrecyclable === 0) {
  466. const idx = this.inspectResult.list.findIndex(listItem => listItem === inspectItem)
  467. if (idx !== -1) {
  468. this.inspectResult.list.splice(idx, 1)
  469. }
  470. return
  471. }
  472. }
  473. console.log('不可回收对象:', inspectItem)
  474. return
  475. }
  476. if (item.id === 'quality-issue-1') {
  477. let inspectItem = this.inspectResult.list.find(listItem => listItem.id === 'quality_issue')
  478. if (!inspectItem && delta > 0) {
  479. inspectItem = {
  480. id: 'quality_issue',
  481. price: 0,
  482. qualifiedNum: 0,
  483. noQualifiedNum: 0, // 初始值为0
  484. unrecyclable: 0,
  485. shopId: '',
  486. pinId: '',
  487. categoryId: '',
  488. commonOrderList: [] // 初始为空数组
  489. }
  490. this.inspectResult.list.push(inspectItem)
  491. }
  492. if (!inspectItem) return
  493. if (type === 'qualified' && delta > 0) {
  494. inspectItem.noQualifiedNum++
  495. // 增加一个commonOrderList对象
  496. inspectItem.commonOrderList.push({
  497. testingInstructions: '',
  498. testingImages: '',
  499. testingStatus: 1
  500. })
  501. } else if (type === 'qualified' && delta < 0) {
  502. inspectItem.noQualifiedNum = Math.max(0, inspectItem.noQualifiedNum - 1)
  503. // 减少一个commonOrderList对象
  504. if (inspectItem.commonOrderList.length > 0) {
  505. inspectItem.commonOrderList.pop()
  506. }
  507. // 新增:如果减到0,移除该对象
  508. if (inspectItem.noQualifiedNum === 0) {
  509. const idx = this.inspectResult.list.findIndex(listItem => listItem === inspectItem)
  510. if (idx !== -1) {
  511. this.inspectResult.list.splice(idx, 1)
  512. }
  513. return
  514. }
  515. }
  516. console.log('质量问题对象:', inspectItem)
  517. return
  518. }
  519. let itemId = item.originalId || item.id
  520. const orderItem = this.getOrderItemByProductId(itemId)
  521. if (orderItem && orderItem.id) {
  522. itemId = orderItem.id
  523. }
  524. let inspectItem = this.inspectResult.list.find(listItem => listItem.shopId == (orderItem ? (orderItem.shopId || orderItem.id) : itemId))
  525. if (!inspectItem && delta > 0) {
  526. inspectItem = {
  527. price: 0,
  528. qualifiedNum: 0,
  529. noQualifiedNum: 0,
  530. unrecyclable: 0,
  531. shopId: orderItem ? (orderItem.shopId || orderItem.id) : itemId,
  532. pinId: '',
  533. categoryId: currentCategoryId || '', // 直接用当前分类id
  534. commonOrderList: [
  535. {
  536. testingInstructions: '',
  537. testingImages: '',
  538. testingStatus: 0
  539. }
  540. ]
  541. }
  542. this.inspectResult.list.push(inspectItem)
  543. }
  544. if (!inspectItem) return
  545. if (type === 'qualified' && delta > 0) {
  546. inspectItem.qualifiedNum++
  547. } else if (type === 'qualified' && delta < 0) {
  548. inspectItem.qualifiedNum = Math.max(0, inspectItem.qualifiedNum - 1)
  549. // 新增:如果减到0,移除该商品对象
  550. if (inspectItem.qualifiedNum === 0) {
  551. const idx = this.inspectResult.list.findIndex(listItem => listItem === inspectItem)
  552. if (idx !== -1) {
  553. this.inspectResult.list.splice(idx, 1)
  554. }
  555. return
  556. }
  557. }
  558. console.log('更新后的inspectResult:', JSON.stringify(this.inspectResult, null, 2))
  559. },
  560. getInspectPrice(item) {
  561. // 优化:不可回收和质量问题通过id匹配
  562. if (item.id === 'unrecyclable-1' || item.id === 'unrecyclable') {
  563. const inspectItem = this.inspectResult.list?.find(listItem => listItem.id === 'unrecyclable')
  564. return inspectItem ? inspectItem.price : ''
  565. }
  566. if (item.id === 'quality-issue-1' || item.id === 'quality_issue') {
  567. const inspectItem = this.inspectResult.list?.find(listItem => listItem.id === 'quality_issue')
  568. return inspectItem ? inspectItem.price : ''
  569. }
  570. // 普通商品
  571. const inspectItem = this.inspectResult.list?.find(listItem => listItem.shopId == item.id)
  572. return inspectItem ? inspectItem.price : ''
  573. },
  574. updateInspectPrice(item, event) {
  575. // 优化:不可回收和质量问题通过id匹配
  576. if (item.id === 'unrecyclable-1' || item.id === 'unrecyclable') {
  577. const inspectItem = this.inspectResult.list?.find(listItem => listItem.id === 'unrecyclable')
  578. if (!inspectItem) return
  579. inspectItem.price = parseFloat(event.detail.value) || 0
  580. return
  581. }
  582. if (item.id === 'quality-issue-1' || item.id === 'quality_issue') {
  583. const inspectItem = this.inspectResult.list?.find(listItem => listItem.id === 'quality_issue')
  584. if (!inspectItem) return
  585. inspectItem.price = parseFloat(event.detail.value) || 0
  586. return
  587. }
  588. // 普通商品
  589. let itemId = item.originalId || item.id
  590. const orderItem = this.getOrderItemByProductId(itemId)
  591. if (orderItem && orderItem.id) {
  592. itemId = orderItem.id // 使用订单中的商品ID
  593. }
  594. // 只查找,不新建
  595. let inspectItem = this.inspectResult.list?.find(listItem => listItem.shopId == (orderItem ? (orderItem.shopId || orderItem.id) : itemId))
  596. if (!inspectItem) return // 没有就不处理
  597. const newPrice = parseFloat(event.detail.value) || 0
  598. inspectItem.price = newPrice
  599. console.log('更新价格:', itemId, newPrice)
  600. console.log('当前商品对象:', inspectItem)
  601. },
  602. loadMoreGoods() {
  603. const categoryId = this.categories[this.currentCategory]?.id
  604. // 不可回收和质量问题分类不支持加载更多
  605. if (categoryId === 'unrecyclable' || categoryId === 'quality_issue') {
  606. return
  607. }
  608. const page = (this.allProductsPage[categoryId] || 1) + 1
  609. const total = this.allProductsTotal[categoryId] || 0
  610. const loaded = (this.allProducts[categoryId] || []).length
  611. if (this.loadingMore || this.finished) return
  612. if (loaded < total) {
  613. this.loadingMore = true
  614. this.fetchGoodsList(categoryId, page, () => {
  615. this.loadingMore = false
  616. // 判断是否加载完
  617. const newLoaded = (this.allProducts[categoryId] || []).length
  618. this.finished = newLoaded >= (this.allProductsTotal[categoryId] || 0)
  619. })
  620. } else {
  621. this.finished = true
  622. }
  623. },
  624. // 获取品牌列表
  625. getGoodsBrandList(productId, searchName = '') {
  626. this.currentProductId = productId
  627. const params = { productId }
  628. if (searchName.trim()) {
  629. params.name = searchName.trim()
  630. }
  631. this.$api('getGoodsBrandList', params, res => {
  632. if (res && res.success && res.result && res.result.records) {
  633. this.brandList = res.result.records.map(item => {
  634. // 获取品牌名称的拼音首字母
  635. const firstChar = this.getPinyinFirstLetter(item.name)
  636. return {
  637. id: item.id,
  638. logo: item.image || '/static/brand/alexander.png',
  639. name: item.name,
  640. letter: firstChar
  641. }
  642. })
  643. }
  644. })
  645. },
  646. // 获取中文拼音首字母
  647. getPinyinFirstLetter(str) {
  648. if (!str) return '#'
  649. const firstChar = str.charAt(0)
  650. let index = this.brandIndexList.indexOf(firstChar.toUpperCase())
  651. if (index != -1) {
  652. return this.brandIndexList[index]
  653. }
  654. return '#'
  655. },
  656. // 品牌搜索输入事件处理
  657. onBrandSearchInput(e) {
  658. const searchValue = e.detail.value
  659. // 清除之前的定时器
  660. if (this.searchTimer) {
  661. clearTimeout(this.searchTimer)
  662. }
  663. // 设置防抖,500ms后执行搜索
  664. this.searchTimer = setTimeout(() => {
  665. if (this.currentProductId) {
  666. this.getGoodsBrandList(this.currentProductId, searchValue)
  667. }
  668. }, 500)
  669. },
  670. // 打开品牌确认弹窗
  671. openBrandConfirm(brand) {
  672. this.brandConfirmInfo = {
  673. id: brand.id,
  674. logo: brand.logo,
  675. name: brand.name
  676. }
  677. this.showBrandConfirm = true
  678. },
  679. // 关闭品牌确认弹窗
  680. closeBrandConfirm() {
  681. this.showBrandConfirm = false
  682. },
  683. // 确认品牌选择
  684. confirmBrand() {
  685. this.showBrandConfirm = false
  686. this.showBrandPopup = false
  687. // 确认后增加商品数量
  688. if (this.pendingBrandIndex !== null) {
  689. const item = this.currentGoods[this.pendingBrandIndex]
  690. if (item) {
  691. const newQualified = Math.max(0, (item.qualified || 0) + 1)
  692. this.$set(item, 'qualified', newQualified)
  693. this.updateInspectResult(item, 'qualified', 1, this.categories[this.currentCategory]?.id)
  694. // 强制更新categories计算属性,使左侧分类数字变化
  695. this.$forceUpdate()
  696. }
  697. this.pendingBrandIndex = null
  698. }
  699. },
  700. // 关闭品牌弹窗
  701. closeBrandPopup() {
  702. this.showBrandPopup = false
  703. this.pendingBrandIndex = null
  704. this.brandSearch = ''
  705. this.currentProductId = null
  706. },
  707. },
  708. created() {
  709. this.currentCategory = 0
  710. },
  711. onLoad(options) {
  712. // 接收订单数据
  713. if (options && options.orderData) {
  714. try {
  715. this.order = JSON.parse(decodeURIComponent(options.orderData))
  716. console.log('接收到的订单数据:', this.order)
  717. // 订单数据加载完成后初始化质检结果
  718. this.$nextTick(() => {
  719. this.initInspectResult()
  720. })
  721. } catch (error) {
  722. console.error('解析订单数据失败:', error)
  723. }
  724. }
  725. if (options && options.orderId) {
  726. this.orderId = options.orderId
  727. }
  728. // 初始化加载第一个分类的商品
  729. this.$nextTick(() => {
  730. // 确保categories已经计算完成
  731. if (this.categories.length > 0) {
  732. const firstCategoryId = this.categories[0]?.id
  733. console.log('第一个分类ID:', firstCategoryId, '所有分类:', this.categories.map(c => c.id))
  734. if (firstCategoryId && firstCategoryId !== 'unrecyclable' && firstCategoryId !== 'quality_issue') {
  735. this.fetchGoodsList(firstCategoryId, 1)
  736. } else if (firstCategoryId === 'unrecyclable' || firstCategoryId === 'quality_issue') {
  737. this.updateCurrentGoods()
  738. }
  739. } else {
  740. console.log('categories为空,等待数据加载')
  741. // 如果categories为空,等待一下再尝试
  742. setTimeout(() => {
  743. if (this.categories.length > 0) {
  744. const firstCategoryId = this.categories[0]?.id
  745. if (firstCategoryId && firstCategoryId !== 'unrecyclable' && firstCategoryId !== 'quality_issue') {
  746. this.fetchGoodsList(firstCategoryId, 1)
  747. } else if (firstCategoryId === 'unrecyclable' || firstCategoryId === 'quality_issue') {
  748. this.updateCurrentGoods()
  749. }
  750. }
  751. }, 500)
  752. }
  753. })
  754. console.log(this.orderId, 'orderId')
  755. },
  756. onShow() {
  757. // 确保在页面显示时categories已经正确计算
  758. console.log('onShow - categories:', this.categories.map(c => ({ id: c.id, title: c.title, badge: c.badge })))
  759. // 强制更新视图,确保所有分类都显示
  760. this.$nextTick(() => {
  761. console.log('分类导航应该显示的分类数量:', this.categories.length)
  762. this.categories.forEach((cat, index) => {
  763. console.log(`分类 ${index}: ${cat.title} (${cat.id})`)
  764. })
  765. })
  766. },
  767. }
  768. </script>
  769. <style lang="scss" scoped>
  770. .inspect-container {
  771. min-height: 100vh;
  772. background: #f8f8f8;
  773. display: flex;
  774. flex-direction: column;
  775. }
  776. .nav-bar {
  777. display: flex;
  778. align-items: center;
  779. height: calc(150rpx + var(--status-bar-height));
  780. padding: 0 32rpx;
  781. padding-top: var(--status-bar-height);
  782. background: #fff;
  783. position: fixed;
  784. top: 0;
  785. left: 0;
  786. right: 0;
  787. z-index: 999;
  788. box-sizing: border-box;
  789. box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.03);
  790. .back {
  791. padding: 20rpx;
  792. margin-left: -20rpx;
  793. }
  794. .nav-title {
  795. flex: 1;
  796. text-align: center;
  797. font-size: 32rpx;
  798. font-weight: 500;
  799. color: #222;
  800. }
  801. .nav-icons {
  802. display: flex;
  803. align-items: center;
  804. gap: 12px;
  805. }
  806. }
  807. .main-content {
  808. margin-top: calc(200rpx + var(--status-bar-height));
  809. display: flex;
  810. background: none;
  811. height: calc(100vh - 200rpx - var(--status-bar-height));
  812. min-height: calc(100vh - 200rpx - var(--status-bar-height));
  813. }
  814. .category-nav {
  815. width: 80px;
  816. background: #fff;
  817. border-radius: 24px 0 0 24px;
  818. // padding: 24px 0;
  819. display: flex;
  820. flex-direction: column;
  821. align-items: center;
  822. box-shadow: 0 2px 8px rgba(0, 0, 0, 0.03);
  823. height: calc(100vh - 200rpx - var(--status-bar-height) - 80px);
  824. max-height: calc(100vh - 200rpx - var(--status-bar-height) - 80px);
  825. overflow-y: scroll;
  826. overflow-x: hidden;
  827. position: relative;
  828. z-index: 2;
  829. -webkit-overflow-scrolling: touch;
  830. scrollbar-width: thin;
  831. scrollbar-color: #ddd transparent;
  832. &::-webkit-scrollbar {
  833. width: 4px;
  834. }
  835. &::-webkit-scrollbar-track {
  836. background: transparent;
  837. }
  838. &::-webkit-scrollbar-thumb {
  839. background: #ddd;
  840. border-radius: 2px;
  841. }
  842. &::-webkit-scrollbar-thumb:hover {
  843. background: #ccc;
  844. }
  845. .category-item {
  846. width: 64px;
  847. height: 44px;
  848. border-radius: 16px 0 0 16px;
  849. display: flex;
  850. align-items: center;
  851. justify-content: flex-start;
  852. font-size: 16px;
  853. color: #222;
  854. margin-bottom: 12px;
  855. background: #fff;
  856. position: relative;
  857. transition: background 0.2s, color 0.2s, font-weight 0.2s;
  858. padding-left: 12px;
  859. &.active {
  860. background: linear-gradient(90deg, #fff7e6 80%, #fff 100%);
  861. color: #ffb400;
  862. font-weight: bold;
  863. &::before {
  864. content: '';
  865. position: absolute;
  866. left: 0;
  867. top: 30%;
  868. height: 40%;
  869. width: 2px;
  870. border-radius: 4px;
  871. background: #ffb400;
  872. bottom: auto;
  873. }
  874. }
  875. .category-badge {
  876. position: absolute;
  877. top: 6px;
  878. right: 10px;
  879. background: #ff4d4f;
  880. color: #fff;
  881. font-size: 12px;
  882. border-radius: 50%;
  883. width: 18px;
  884. height: 18px;
  885. display: flex;
  886. align-items: center;
  887. justify-content: center;
  888. }
  889. }
  890. }
  891. .goods-list {
  892. flex: 1;
  893. height: calc(100vh - 200rpx - var(--status-bar-height) - 80px);
  894. padding: 0 0 0 16px;
  895. overflow-y: auto;
  896. background: none;
  897. }
  898. .goods-card {
  899. background: #fff;
  900. border-radius: 24px;
  901. box-shadow: 0 4px 24px rgba(0, 0, 0, 0.06);
  902. margin-bottom: 18px;
  903. padding: 18px 18px 9px 18px;
  904. }
  905. .goods-header {
  906. display: flex;
  907. align-items: center;
  908. margin-bottom: 12px;
  909. .goods-img {
  910. width: 56px;
  911. height: 56px;
  912. border-radius: 16px;
  913. margin-right: 12px;
  914. background: #f8f8f8;
  915. object-fit: contain;
  916. }
  917. .goods-info {
  918. flex: 1;
  919. display: flex;
  920. flex-direction: column;
  921. justify-content: center;
  922. min-width: 0;
  923. .goods-title-row {
  924. display: flex;
  925. align-items: baseline;
  926. .goods-name {
  927. font-size: 16px;
  928. font-weight: bold;
  929. color: #222;
  930. margin-right: 8px;
  931. }
  932. .goods-price {
  933. font-size: 15px;
  934. color: #ffb400;
  935. font-weight: bold;
  936. .goods-unit {
  937. font-size: 13px;
  938. color: #bbb;
  939. }
  940. }
  941. }
  942. .goods-desc {
  943. font-size: 13px;
  944. color: #999;
  945. margin-top: 4px;
  946. }
  947. }
  948. }
  949. .goods-row {
  950. display: flex;
  951. align-items: center;
  952. margin-bottom: 12px;
  953. .row-label {
  954. font-size: 14px;
  955. color: #888;
  956. width: 80px;
  957. flex-shrink: 0;
  958. }
  959. .num-ctrl {
  960. display: flex;
  961. align-items: center;
  962. .num-btn {
  963. width: 60rpx;
  964. height: 60rpx;
  965. padding: 0;
  966. margin: 0;
  967. display: flex;
  968. align-items: center;
  969. justify-content: center;
  970. font-size: 28rpx;
  971. color: #666;
  972. background: #ffffff;
  973. border: none;
  974. border-radius: 50%;
  975. &::after {
  976. border: none;
  977. }
  978. &:active {
  979. opacity: 0.8;
  980. }
  981. }
  982. .num {
  983. width: 80rpx;
  984. text-align: center;
  985. font-size: 32rpx;
  986. color: #333;
  987. }
  988. }
  989. .amount-input {
  990. flex: 1;
  991. height: 32px;
  992. border-radius: 12px;
  993. background: #f6f6f6;
  994. border: none;
  995. font-size: 15px;
  996. color: #222;
  997. padding-left: 10px;
  998. margin-left: 8px;
  999. }
  1000. }
  1001. .footer-btns {
  1002. position: fixed;
  1003. left: 0;
  1004. right: 0;
  1005. bottom: 0;
  1006. background: #fff;
  1007. display: flex;
  1008. gap: 16px;
  1009. padding: 12px 16px 24px 16px;
  1010. z-index: 101;
  1011. .btn-outline {
  1012. flex: 1;
  1013. height: 40px;
  1014. border-radius: 16px;
  1015. border: 1px solid #ffe09a;
  1016. color: #ffb400;
  1017. background: #fff0d2;
  1018. font-size: 15px;
  1019. font-weight: 500;
  1020. box-shadow: none;
  1021. padding: 0 18px;
  1022. }
  1023. .btn-main {
  1024. flex: 1;
  1025. height: 40px;
  1026. border-radius: 16px;
  1027. background: linear-gradient(90deg, #ffd01e 0%, #ffac04 100%);
  1028. color: #fff;
  1029. border: none;
  1030. font-size: 15px;
  1031. font-weight: 500;
  1032. box-shadow: none;
  1033. padding: 0 18px;
  1034. }
  1035. }
  1036. .loading-more {
  1037. text-align: center;
  1038. color: #999;
  1039. padding: 20rpx 0;
  1040. font-size: 26rpx;
  1041. }
  1042. // 品牌选择弹窗样式
  1043. .brand-popup-mask {
  1044. position: fixed;
  1045. left: 0;
  1046. right: 0;
  1047. top: 0;
  1048. bottom: 0;
  1049. background: rgba(0,0,0,0.35);
  1050. z-index: 3000;
  1051. display: flex;
  1052. align-items: flex-end;
  1053. justify-content: center;
  1054. }
  1055. .brand-popup {
  1056. position: relative;
  1057. width: 100%;
  1058. max-width: 750px;
  1059. background: #fff;
  1060. border-radius: 32rpx 32rpx 0 0;
  1061. box-shadow: 0 -4rpx 24rpx rgba(0,0,0,0.08);
  1062. padding-bottom: 40rpx;
  1063. max-height: 90vh;
  1064. display: flex;
  1065. flex-direction: column;
  1066. overflow: hidden;
  1067. }
  1068. .brand-popup-header {
  1069. display: flex;
  1070. align-items: center;
  1071. justify-content: center;
  1072. padding: 32rpx 24rpx 0 24rpx;
  1073. font-size: 32rpx;
  1074. font-weight: bold;
  1075. position: relative;
  1076. }
  1077. .brand-popup-close {
  1078. position: absolute;
  1079. left: 24rpx;
  1080. font-size: 28rpx;
  1081. color: #888;
  1082. }
  1083. .brand-popup-title {
  1084. font-size: 32rpx;
  1085. color: #222;
  1086. font-weight: bold;
  1087. }
  1088. .brand-popup-search {
  1089. padding: 20rpx 24rpx 0 24rpx;
  1090. }
  1091. .brand-search-input {
  1092. width: 100%;
  1093. height: 60rpx;
  1094. border-radius: 30rpx;
  1095. background: #f5f5f5;
  1096. border: none;
  1097. padding-left: 40rpx;
  1098. font-size: 28rpx;
  1099. color: #888;
  1100. }
  1101. .brand-popup-list {
  1102. flex: 1;
  1103. overflow-y: auto;
  1104. max-height: 60vh;
  1105. padding: 0 24rpx;
  1106. scrollbar-width: none; /* Firefox */
  1107. -ms-overflow-style: none; /* IE and Edge */
  1108. &::-webkit-scrollbar {
  1109. width: 0 !important;
  1110. display: none; /* Chrome, Safari, Opera */
  1111. }
  1112. }
  1113. .brand-letter {
  1114. font-size: 28rpx;
  1115. color: #888;
  1116. margin: 24rpx 0 8rpx 0;
  1117. font-weight: bold;
  1118. }
  1119. .brand-item {
  1120. display: flex;
  1121. align-items: center;
  1122. padding: 16rpx 0;
  1123. border-bottom: 1px solid #f0f0f0;
  1124. }
  1125. .brand-logo {
  1126. width: 60rpx;
  1127. height: 60rpx;
  1128. margin-right: 20rpx;
  1129. border-radius: 8rpx;
  1130. background: #f8f8f8;
  1131. }
  1132. .brand-name {
  1133. font-size: 28rpx;
  1134. color: #222;
  1135. }
  1136. // 品牌确认弹窗样式
  1137. .brand-confirm-mask {
  1138. position: fixed;
  1139. left: 0;
  1140. right: 0;
  1141. top: 0;
  1142. bottom: 0;
  1143. background: rgba(0,0,0,0.25);
  1144. z-index: 5001;
  1145. display: flex;
  1146. align-items: center;
  1147. justify-content: center;
  1148. }
  1149. .brand-confirm-popup {
  1150. width: 70vw;
  1151. max-width: 270px;
  1152. background: #fff;
  1153. border-radius: 32rpx;
  1154. box-shadow: 0 8rpx 32rpx rgba(0,0,0,0.12);
  1155. display: flex;
  1156. flex-direction: column;
  1157. align-items: center;
  1158. padding: 48rpx 20rpx 36rpx 20rpx;
  1159. position: relative;
  1160. }
  1161. .brand-confirm-title {
  1162. font-size: 36rpx;
  1163. color: #222;
  1164. font-weight: bold;
  1165. text-align: center;
  1166. margin-bottom: 24rpx;
  1167. }
  1168. .brand-confirm-logo-wrap {
  1169. width: 120rpx;
  1170. height: 120rpx;
  1171. background: #f8f8f8;
  1172. border-radius: 50%;
  1173. display: flex;
  1174. align-items: center;
  1175. justify-content: center;
  1176. margin-bottom: 18rpx;
  1177. }
  1178. .brand-confirm-logo {
  1179. width: 80rpx;
  1180. height: 80rpx;
  1181. border-radius: 50%;
  1182. }
  1183. .brand-confirm-name {
  1184. font-size: 28rpx;
  1185. color: #222;
  1186. font-weight: bold;
  1187. text-align: center;
  1188. margin-bottom: 16rpx;
  1189. }
  1190. .brand-confirm-desc {
  1191. font-size: 24rpx;
  1192. color: #999;
  1193. text-align: center;
  1194. margin-bottom: 32rpx;
  1195. line-height: 1.6;
  1196. }
  1197. .brand-confirm-btn-row {
  1198. width: 100%;
  1199. display: flex;
  1200. justify-content: space-between;
  1201. gap: 24rpx;
  1202. }
  1203. .brand-confirm-btn {
  1204. flex: 1;
  1205. height: 72rpx;
  1206. border-radius: 36rpx;
  1207. font-size: 28rpx;
  1208. font-weight: bold;
  1209. display: flex;
  1210. align-items: center;
  1211. justify-content: center;
  1212. border: none;
  1213. margin: 0 0;
  1214. }
  1215. .brand-confirm-btn.retry {
  1216. background: #fff;
  1217. color: #ff9c00;
  1218. border: 2rpx solid #ff9c00;
  1219. }
  1220. .brand-confirm-btn.confirm {
  1221. background: linear-gradient(to right, #ffd01e, #ff8917);
  1222. color: #fff;
  1223. border: none;
  1224. }
  1225. </style>