import { getProductById } from '../../utils/productService' import { addToCart } from '../../utils/cartManager' import { getMachineInfo } from '../../api/products' import { addCart, getGoodsList } from '../../api/shoppingCart' export default { name: 'ProductDetail', data() { return { product: null, loading: false, // 默认展开的行keys expandedRowKeys: [], selectedMap: {}, params: { id: "", }, confirmAddDialog: { visible: false, items: [] }, // 购物车中已存在的当前商品机器集合:id 与 user|miner 组合键 cartMachineIdSet: new Set(), cartCompositeKeySet: new Set(), cartLoaded: false, machinesLoaded: false, /** * 可展开的产品系列数据 * 每个系列(group)包含多个可选条目(variants) */ productListData: [ // { // id: 'grp-1', // group: 'A系列', // summary: { // theoryPower: '56T', // computingPower: '54T', // powerDissipation: '3200W', // algorithm: 'power', // type: 'A-Pro', // count: 3, // price: '¥1000+' // }, // variants: [ // { id: 'A-1', model: 'A1', theoryPower: '14T', computingPower: '13.5T', powerDissipation: '780W', algorithm: 'power', stock: 50, price: 999, quantity: 1 }, // { id: 'A-2', model: 'A2', theoryPower: '18T', computingPower: '17.2T', powerDissipation: '900W', algorithm: 'power', stock: 40, price: 1299, quantity: 1 }, // { id: 'A-3', model: 'A3', theoryPower: '24T', computingPower: '23.1T', powerDissipation: '1520W', algorithm: 'power', stock: 30, price: 1699, quantity: 1 } // ] // }, // { // id: 'grp-2', // group: 'B系列', // summary: { // theoryPower: '72T', // computingPower: '70T', // powerDissipation: '4100W', // algorithm: 'power', // type: 'B-Max', // count: 2, // price: '¥2000+' // }, // variants: [ // { id: 'B-1', model: 'B1', theoryPower: '32T', computingPower: '31.2T', powerDissipation: '1800W', algorithm: 'power', stock: 28, price: 2199, quantity: 1 }, // { id: 'B-2', model: 'B2', theoryPower: '40T', computingPower: '38.8T', powerDissipation: '2300W', algorithm: 'power', stock: 18, price: 2699, quantity: 1 } // ] // } ], tableData: [ // { // theoryPower: "55656",//理论算力 // computingPower: "44545",//实际算力 // powerDissipation: "5565",//功耗 // algorithm: "power",//算法 // type: "型号1",//矿机型号 // number:2001, // cost:"1000",//价格 // }, // { // theoryPower: "55656",//理论算力 // computingPower: "44545",//实际算力 // powerDissipation: "5565",//功耗 // algorithm: "power",//算法 // type: "型号1",//矿机型号 // number:2001, // cost:"1000",//价格 // }, // { // theoryPower: "55656",//理论算力 // computingPower: "44545",//实际算力 // powerDissipation: "5565",//功耗 // algorithm: "power",//算法 // type: "型号1",//矿机型号 // number:2001, // cost:"1000",//价格 // }, // { // theoryPower: "55656",//理论算力 // computingPower: "44545",//实际算力 // powerDissipation: "5565",//功耗 // algorithm: "power",//算法 // type: "型号1",//矿机型号 // number:2001, // cost:"1000",//价格 // }, ], productDetailLoading:false } }, mounted() { console.log(this.$route.params.id, "i叫哦附加费") if (this.$route.params.id) { this.params.id = this.$route.params.id this.product = true // 默认展开第一行 if (this.productListData && this.productListData.length) { this.expandedRowKeys = [this.productListData[0].id] } this.fetchGetMachineInfo(this.params) } else { this.$message.error('商品不存在') this.product = false } this.fetchGetGoodsList() }, methods: { async fetchGetMachineInfo(params) { this.productDetailLoading = true const res = await getMachineInfo(params) console.log(res) if (res && res.code === 200) { console.log(res.data, 'res.rows'); const list = Array.isArray(res.data) ? res.data : [] const withKeys = list.map((group, idx) => { const fallbackId = `grp-${idx}` const groupId = group.id || group.onlyKey || (group.productMachineRangeGroupDto && group.productMachineRangeGroupDto.id) const firstMachineId = Array.isArray(group.productMachines) && group.productMachines.length > 0 ? group.productMachines[0].id : undefined // 为机器行设置默认租赁天数为1,并确保未选中状态 const normalizedMachines = Array.isArray(group.productMachines) ? group.productMachines.map(m => ({ ...m, leaseTime: (m && m.leaseTime && Number(m.leaseTime) > 0) ? Number(m.leaseTime) : 1, _selected: false // 确保所有机器行初始状态为未选中 })) : [] return { ...group, id: groupId || (firstMachineId ? `m-${firstMachineId}` : fallbackId), productMachines: normalizedMachines } }) this.productListData = withKeys if (this.productListData.length && (!this.expandedRowKeys || !this.expandedRowKeys.length)) { this.expandedRowKeys = [this.productListData[0].id] } // 产品机器加载完成后,依据购物车集合执行一次本地禁用与勾选 this.$nextTick(() => { this.machinesLoaded = true // 已取消与购物车对比:不再自动禁用或勾选 }) } this.productDetailLoading = false }, /** * 加载商品详情 */ async loadProduct() { try { this.loading = true const productId = this.$route.params.id this.product = await getProductById(productId) if (!this.product) { this.$message({ message: '商品不存在', type: 'error', showClose: true }) } } catch (error) { console.error('加载商品详情失败:', error) this.$message({ message: '加载商品详情失败,请稍后重试', type: 'error', showClose: true }) } finally { this.loading = false } }, //加入购物车 async fetchAddCart(params) { const res = await addCart(params) return res }, //查询购物车列表 async fetchGetGoodsList(params) { const res = await getGoodsList(params) // 统计当前商品在购物车中已有的机器ID,用于禁用和默认勾选 try { const productId = this.params && this.params.id ? Number(this.params.id) : Number(this.$route.params.id) // 兼容两种返回结构:1) 旧:直接是商品分组数组 2) 新:店铺数组 → shoppingCartInfoDtoList const rawRows = Array.isArray(res && res.rows) ? res.rows : Array.isArray(res && res.data && res.data.rows) ? res.data.rows : Array.isArray(res && res.data) ? res.data : [] // 扁平化为商品分组 const groups = rawRows.length && rawRows[0] && Array.isArray(rawRows[0].shoppingCartInfoDtoList) ? rawRows.flatMap(shop => Array.isArray(shop.shoppingCartInfoDtoList) ? shop.shoppingCartInfoDtoList : []) : rawRows const matched = groups.filter(g => Number(g.productId) === productId) const ids = new Set() const compositeKeys = new Set() matched.forEach(r => { const list = Array.isArray(r.productMachineDtoList) ? r.productMachineDtoList : [] list.forEach(m => { if (!m) return if (m.id !== undefined && m.id !== null) ids.add(String(m.id)) if (m.user && m.miner) compositeKeys.add(`${String(m.user)}|${String(m.miner)}`) }) }) this.cartMachineIdSet = ids this.cartCompositeKeySet = compositeKeys // 计算购物车总数量并通知头部,避免页面初次加载时徽标显示为0 try { const totalCount = groups.reduce((sum, g) => sum + (Array.isArray(g && g.productMachineDtoList) ? g.productMachineDtoList.length : 0), 0) if (Number.isFinite(totalCount)) { window.dispatchEvent(new CustomEvent('cart-updated', { detail: { count: totalCount } })) } } catch (e) { /* noop */ } // 展开表格渲染后,默认勾选并禁用这些行 this.$nextTick(() => { this.cartLoaded = true this.autoSelectAndDisable() }) } catch (e) { console.warn('解析购物车数据失败', e) } }, /** * 处理返回 */ handleBack() { this.$router.push('/productList') }, /** * 点击系列行:切换展开/收起 * @param {Object} row - 当前行 */ handleSeriesRowClick(row) { const key = row.id const lockedIds = Object.keys(this.selectedMap).filter(k => (this.selectedMap[k] || []).length > 0) const opened = this.expandedRowKeys.includes(key) if (opened) { // 关闭当前行,仅保留已勾选的行展开 this.expandedRowKeys = lockedIds } else { // 打开当前行,同时保留已勾选的行展开 this.expandedRowKeys = Array.from(new Set([key, ...lockedIds])) } }, /** * 外层系列行样式 */ handleGetSeriesRowClassName() { return 'series-clickable-row' }, // 子表选择变化 handleInnerSelectionChange(parentRow, selections) { const key = parentRow.id this.$set(this.selectedMap, key, selections) const lockedIds = Object.keys(this.selectedMap).filter(k => (this.selectedMap[k] || []).length > 0) // 更新展开:锁定的行始终展开 const openedSet = new Set(this.expandedRowKeys) lockedIds.forEach(id => openedSet.add(id)) // 清理不再勾选且不是当前展开的行 this.expandedRowKeys = Array.from(openedSet).filter(id => lockedIds.includes(id) || id === key || this.expandedRowKeys.includes(id)) }, // 展开行变化时:已取消自动与购物车对比,无需勾选/禁用 handleExpandChange(row, expandedRows) { // no-op }, // 已取消对比购物车的自动勾选/禁用逻辑 autoSelectAndDisable() {}, // 选择器可选控制:已在购物车中的机器不可再选 isSelectable(row, index) { // 不再通过 selectable 禁用,以便勾选可见;通过行样式和交互阻止点击 return true }, // 判断在特定父行下是否已选择(配合自定义checkbox使用) isSelectedByParent(parentRow, row) { const key = parentRow && parentRow.id const list = (key && this.selectedMap[key]) || [] return !!list.find(it => it && it.id === row.id) }, // 手动切换选择(自定义checkbox与 selectedMap 同步),并维护每行的 _selected 状态 handleManualSelect(parentRow, row, checked) { const key = parentRow.id const list = (this.selectedMap[key] && [...this.selectedMap[key]]) || [] const idx = list.findIndex(it => it && it.id === row.id) if (checked && idx === -1) list.push(row) if (!checked && idx > -1) list.splice(idx, 1) this.$set(this.selectedMap, key, list) this.$set(row, '_selected', !!checked) }, // 为子表中已在购物车的行添加只读样式,并阻止点击取消 getInnerRowClass() { return '' }, /** * 子行:减少数量 * @param {number} groupIndex - 系列索引 * @param {number} variantIndex - 变体索引 */ handleDecreaseVariantQuantity(groupIndex, variantIndex) { const item = this.productListData[groupIndex].variants[variantIndex] if (item.quantity > 1) { item.quantity-- } }, /** * 子行:增加数量 * @param {number} groupIndex - 系列索引 * @param {number} variantIndex - 变体索引 */ handleIncreaseVariantQuantity(groupIndex, variantIndex) { const item = this.productListData[groupIndex].variants[variantIndex] if (item.quantity < 99) { item.quantity++ } }, /** * 子行:输入数量校验 * @param {number} groupIndex - 系列索引 * @param {number} variantIndex - 变体索引 */ handleVariantQuantityInput(groupIndex, variantIndex) { const item = this.productListData[groupIndex].variants[variantIndex] const q = Number(item.quantity) if (!q || q < 1) item.quantity = 1 if (q > 99) item.quantity = 99 }, /** * 子行:加入购物车 * @param {Object} variant - 子项行数据 */ handleAddVariantToCart(variant) { if (!variant || !variant.onlyKey) return try { addToCart({ id: variant.onlyKey, title: variant.model, price: variant.price, quantity: variant.quantity }) this.$message.success(`已添加 ${variant.quantity} 件 ${variant.model} 到购物车`) variant.quantity = 1 } catch (error) { console.error('添加到购物车失败:', error) } }, // 统一加入购物车 handleAddSelectedToCart() { const allSelected = Object.values(this.selectedMap).flat().filter(Boolean) if (!allSelected.length) { this.$message.warning('请先勾选至少一台矿机') return } try { allSelected.forEach(item => { addToCart({ id: item.onlyKey || item.id, title: item.type || item.model || '矿机', price: item.price, quantity: 1, leaseTime: Number(item.leaseTime || 1) }) }) this.$message.success(`已加入 ${allSelected.length} 台矿机到购物车`) this.selectedMap = {} } catch (e) { console.error('统一加入购物车失败', e) } }, // 打开确认弹窗:以当前界面勾选(_selected)为准,并在打开后清空左侧勾选状态 handleOpenAddToCartDialog() { // 扫描当前所有系列下被勾选的机器 const groups = Array.isArray(this.productListData) ? this.productListData : [] const picked = groups.flatMap(g => Array.isArray(g.productMachines) ? g.productMachines.filter(m => !!m && !!m._selected) : []) if (!picked.length) { this.$message.warning('请先勾选至少一台矿机') return } // 使用弹窗中的固定快照,避免后续清空勾选影响弹窗显示 this.confirmAddDialog.items = picked.slice() this.confirmAddDialog.visible = true // 打开后立即把左侧复选框清空,避免“勾选了两个但弹窗只有一条”的不一致问题 this.$nextTick(() => { try { this.clearAllSelections() } catch (e) { /* noop */ } }) }, // 确认加入:调用后端购物车接口,传入裸数组 [{ productId, productMachineId }] async handleConfirmAddSelectedToCart() { // 以弹窗中的列表为准,避免与左侧勾选状态不一致 const allSelected = Array.isArray(this.confirmAddDialog.items) ? this.confirmAddDialog.items.filter(Boolean) : [] if (!allSelected.length) { this.$message.warning('请先勾选至少一台矿机') return } const productId = this.params && this.params.id ? this.params.id : (this.$route && this.$route.params && this.$route.params.id) if (!productId) { this.$message.error('商品ID缺失,无法加入购物车') return } // 裸数组,仅包含后端要求的两个字段 const payload = allSelected.map(item => ({ productId: productId, productMachineId: item.id, leaseTime: Number(item.leaseTime || 1) })) try { const res = await this.fetchAddCart(payload) // 若后端返回码存在,这里做一下兜底提示 if (!res || (res.code && Number(res.code) !== 200)) { this.$message.error(res && res.msg ? res.msg : '加入购物车失败,请稍后重试') return } // 立即本地更新禁用状态:把刚加入的机器ID合并进本地集合 try { allSelected.forEach(item => { if (item && item.id) this.cartMachineIdSet.add(item.id) this.$set(item, '_selected', false) this.$set(item, '_inCart', true) if (!item.leaseTime || Number(item.leaseTime) <= 0) this.$set(item, 'leaseTime', 1) }) this.$nextTick(() => this.autoSelectAndDisable()) } catch (e) { /* noop */ } this.$message({ message: `已加入 ${allSelected.length} 台矿机到购物车`, type: 'success', duration: 3000, showClose: true, }); this.confirmAddDialog.visible = false // 清空选中映射,然后重新加载数据(数据加载时会自动设置 _selected: false) this.selectedMap = {} // 重新加载机器信息和购物车数据 this.fetchGetMachineInfo(this.params) this.fetchGetGoodsList() // 通知头部刷新服务端购物车数量 try { // 如果没有传数量,header 会主动拉取服务端数量 window.dispatchEvent(new CustomEvent('cart-updated')) } catch (e) { /* noop */ } } catch (e) { console.error('加入购物车失败: ', e) this.$message.error('加入购物车失败,请稍后重试') } }, // 取消所有商品勾选(内层表格的自定义 checkbox) clearAllSelections() { try { // 清空选中映射 this.selectedMap = {} // 遍历所有系列与机器,复位 _selected const groups = Array.isArray(this.productListData) ? this.productListData : [] groups.forEach(g => { const list = Array.isArray(g.productMachines) ? g.productMachines : [] list.forEach(m => { if (m) this.$set(m, '_selected', false) }) }) } catch (e) { /* noop */ } }, /** * 减少数量 * @param {number} rowIndex - 表格行索引 */ handleDecreaseQuantity(rowIndex) { if (this.tableData[rowIndex].quantity > 1) { this.tableData[rowIndex].quantity-- } }, /** * 增加数量 * @param {number} rowIndex - 表格行索引 */ handleIncreaseQuantity(rowIndex) { if (this.tableData[rowIndex].quantity < 99) { this.tableData[rowIndex].quantity++ } }, /** * 处理数量输入 * @param {number} rowIndex - 表格行索引 */ handleQuantityInput(rowIndex) { const quantity = this.tableData[rowIndex].quantity if (quantity < 1) { this.tableData[rowIndex].quantity = 1 } else if (quantity > 99) { this.tableData[rowIndex].quantity = 99 } }, /** * 处理数量输入框失焦 * @param {number} rowIndex - 表格行索引 */ handleQuantityBlur(rowIndex) { const quantity = this.tableData[rowIndex].quantity if (!quantity || quantity < 1) { this.tableData[rowIndex].quantity = 1 } else if (quantity > 99) { this.tableData[rowIndex].quantity = 99 } }, /** * 添加到购物车 * @param {Object} rowData - 表格行数据 */ handleAddToCart(rowData) { if (!rowData || rowData.quantity < 1) { this.$message.warning('请选择有效的数量') return } try { addToCart({ id: rowData.date, // 使用矿机名称作为ID title: rowData.date, price: rowData.price, quantity: rowData.quantity, leaseTime: Number(rowData.leaseTime || 1) }) this.$message.success(`已添加 ${rowData.quantity} 件 ${rowData.date} 到购物车`) // 重置数量 rowData.quantity = 1 } catch (error) { console.error('添加到购物车失败:', error) this.$message.error('添加到购物车失败,请稍后重试') } } } }