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

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