2025-09-26 16:40:38 +08:00
|
|
|
|
|
|
|
|
|
|
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');
|
2025-10-20 10:15:13 +08:00
|
|
|
|
this.paymentMethodList = res.data.payConfigList || []
|
|
|
|
|
|
const list =res.data.machineRangeInfoList || []
|
2025-09-26 16:40:38 +08:00
|
|
|
|
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) {
|
2025-10-20 10:15:13 +08:00
|
|
|
|
// 禁用:已售出或售出中的机器不可选择
|
|
|
|
|
|
if (row && (row.saleState === 1 || row.saleState === 2)) {
|
|
|
|
|
|
this.$message.warning('该机器已售出或售出中,无法选择')
|
|
|
|
|
|
this.$set(row, '_selected', false)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2025-09-26 16:40:38 +08:00
|
|
|
|
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)
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 为子表中已在购物车的行添加只读样式,并阻止点击取消
|
2025-10-20 10:15:13 +08:00
|
|
|
|
handleGetInnerRowClass({ row }) {
|
|
|
|
|
|
if (!row) return ''
|
|
|
|
|
|
return (row.saleState === 1 || row.saleState === 2) ? 'sold-row' : ''
|
2025-09-26 16:40:38 +08:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 子行:减少数量
|
|
|
|
|
|
* @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 : []
|
2025-10-20 10:15:13 +08:00
|
|
|
|
const pickedAll = groups.flatMap(g => Array.isArray(g.productMachines) ? g.productMachines.filter(m => !!m && !!m._selected) : [])
|
|
|
|
|
|
const picked = pickedAll.filter(m => m && (m.saleState === 0 || m.saleState === undefined || m.saleState === null))
|
2025-09-26 16:40:38 +08:00
|
|
|
|
if (!picked.length) {
|
|
|
|
|
|
this.$message.warning('请先勾选至少一台矿机')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
2025-10-20 10:15:13 +08:00
|
|
|
|
if (picked.length < pickedAll.length) {
|
|
|
|
|
|
this.$message.warning('部分机器已售出或售出中,已自动为您排除')
|
|
|
|
|
|
}
|
2025-09-26 16:40:38 +08:00
|
|
|
|
// 使用弹窗中的固定快照,避免后续清空勾选影响弹窗显示
|
|
|
|
|
|
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('添加到购物车失败,请稍后重试')
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|