diff --git a/src/views/BuyDetails.vue b/src/views/BuyDetails.vue
index a2718c7..0e09614 100644
--- a/src/views/BuyDetails.vue
+++ b/src/views/BuyDetails.vue
@@ -64,10 +64,10 @@
class="spec-item"
:class="{
active: selectedSpecs[specName]?.id === option.id,
- disabled: availableSpecs[specName] && !availableSpecs[specName][option.id]
+ disabled: availableSpecs[specName] && !availableSpecs[specName][option.id] && selectedSpecs[specName]?.id !== option.id
}"
@click="selectSpec(specName, option)"
- :disabled="availableSpecs[specName] && !availableSpecs[specName][option.id]"
+ :disabled="availableSpecs[specName] && !availableSpecs[specName][option.id] && selectedSpecs[specName]?.id !== option.id"
>
{{ option.name }}
@@ -158,9 +158,53 @@ const totalPointsPrice = computed(() => {
return product.value.points_price * quantity.value
})
+// 动态计算当前场景下必须选择的规格名称集合(基于已选项与后端可用组合)
+const requiredSpecNames = computed(() => {
+ const allSpecNames = Object.keys(specGroups.value)
+ if (!product.value || !Array.isArray(product.value.specifications)) {
+ return allSpecNames
+ }
+
+ const availableSpecs = product.value.specifications.filter(s => s.is_available)
+
+ // 已选的规格项ID
+ const selectedIds = allSpecNames
+ .map(name => selectedSpecs.value[name]?.id)
+ .filter(Boolean)
+
+ // 能与当前已选项兼容的可用规格规则集合
+ const candidateSpecs = availableSpecs.filter(spec => {
+ const detailIds = spec.spec_details.map(d => d.id)
+ return selectedIds.every(id => detailIds.includes(id))
+ })
+
+ if (candidateSpecs.length === 0) {
+ // 若暂无兼容组合,默认要求全部(防止误判)
+ return allSpecNames
+ }
+
+ // 计算每个候选组合涉及的规格名称集
+ const nameSets = candidateSpecs.map(spec => {
+ const names = spec.spec_details.map(d => d.spec_name)
+ return Array.from(new Set(names))
+ })
+
+ // 优先选择包含当前已选规格名称的最小集合
+ const selectedNames = allSpecNames.filter(name => !!selectedSpecs.value[name])
+ const filteredByIntent = nameSets
+ .filter(set => selectedNames.every(n => set.includes(n)))
+ .sort((a, b) => a.length - b.length)
+
+ if (filteredByIntent.length > 0) return filteredByIntent[0]
+
+ // 兜底:选择最小的集合
+ nameSets.sort((a, b) => a.length - b.length)
+ return nameSets[0]
+})
+
const canPurchase = computed(() => {
- const specNames = Object.keys(specGroups.value)
- const allSpecsSelected = specNames.every(specName => selectedSpecs.value[specName])
+ const required = requiredSpecNames.value
+ const allSpecsSelected = required.every(specName => selectedSpecs.value[specName])
return allSpecsSelected && quantity.value > 0
})
@@ -177,40 +221,38 @@ const decreaseQuantity = () => {
}
}
-// 检查规格组合是否有效
+// 检查规格组合是否有效(忽略顺序,按ID集合匹配)
const isValidCombination = (testSelection) => {
const selectedIds = []
const specNames = Object.keys(specGroups.value)
-
- // 按规格名称顺序收集选中的规格ID,转换为顺序编号
+
+ // 收集已选规格的真实ID(不再转换顺序编号)
specNames.forEach(specName => {
if (testSelection[specName]) {
- const specId = testSelection[specName].id
- const orderNumber = specIdToOrder.value[specId]
- selectedIds.push(orderNumber)
- } else {
- selectedIds.push(null) // 未选择的规格用null占位
+ selectedIds.push(testSelection[specName].id)
}
})
-
- // 如果还没有选择完所有规格,检查部分选择是否与任何有效组合兼容
- if (selectedIds.includes(null)) {
- return validCombinations.value.some(combinationKey => {
- const keyParts = combinationKey.split('-').map(k => parseInt(k))
-
- // 检查当前部分选择是否与这个combination_key兼容
- return selectedIds.every((selectedOrder, index) => {
- // 如果该位置未选择,则兼容
- if (selectedOrder === null) return true
- // 如果该位置已选择,检查是否匹配
- return selectedOrder === keyParts[index]
- })
+
+ if (!product.value || !Array.isArray(product.value.specifications)) {
+ return false
+ }
+
+ // 仅考虑后端标记为可用的规格组合
+ const availableSpecs = product.value.specifications.filter(s => s.is_available)
+
+ // 部分选择:只要存在一个规格规则,其spec_details包含所有已选ID即可
+ if (selectedIds.length < specNames.length) {
+ return availableSpecs.some(spec => {
+ const detailIds = spec.spec_details.map(d => d.id)
+ return selectedIds.every(id => detailIds.includes(id))
})
}
-
- // 如果选择了所有规格,检查完整组合是否有效
- const combinationKey = selectedIds.join('-')
- return validCombinations.value.includes(combinationKey)
+
+ // 完整选择:必须存在一个规格规则,其ID集合与所选集合相等(忽略顺序)
+ return availableSpecs.some(spec => {
+ const detailIds = spec.spec_details.map(d => d.id)
+ return detailIds.length === selectedIds.length && selectedIds.every(id => detailIds.includes(id))
+ })
}
// 更新可选规格状态
@@ -232,16 +274,20 @@ const updateAvailableSpecs = () => {
// 选择规格
const selectSpec = (specName, option) => {
- // 检查该选项是否被禁用
+ // 二次点击已选中项则取消选择
+ if (selectedSpecs.value[specName]?.id === option.id) {
+ delete selectedSpecs.value[specName]
+ updateAvailableSpecs()
+ return
+ }
+
+ // 检查该选项是否被禁用(但允许点击已选项取消)
if (availableSpecs.value[specName] && !availableSpecs.value[specName][option.id]) {
ElMessage.warning('该规格组合不可选,请选择其他规格')
return
}
selectedSpecs.value[specName] = option
- console.log(`选择${specName}:`, option)
- console.log('当前选中的所有规格:', selectedSpecs.value)
-
// 更新可选规格状态
updateAvailableSpecs()
}
@@ -307,16 +353,15 @@ const parseSpecifications = (specifications) => {
// 转换Set为数组并解析JSON
const finalSpecGroups = {}
- let orderCounter = 1
Object.keys(tempSpecGroups).forEach(specName => {
finalSpecGroups[specName] = Array.from(tempSpecGroups[specName]).map(item => JSON.parse(item))
// 按sort_order排序
finalSpecGroups[specName].sort((a, b) => a.sort_order - b.sort_order)
- // 为每个规格选项分配顺序编号(从1开始)
+ // 使用规格项自身ID作为映射,避免与后端combination_key不一致
finalSpecGroups[specName].forEach(option => {
- specIdToOrderMap[option.id] = orderCounter++
+ specIdToOrderMap[option.id] = option.id
})
})
@@ -327,7 +372,7 @@ const parseSpecifications = (specifications) => {
specIdToOrder.value = specIdToOrderMap
console.log('有效的规格组合键:', validCombinationKeys)
- console.log('规格ID到顺序编号映射:', specIdToOrderMap)
+ console.log('规格ID映射(按ID本身):', specIdToOrderMap)
// 初始化可选规格状态
updateAvailableSpecs()
@@ -336,36 +381,31 @@ const parseSpecifications = (specifications) => {
console.log('解析后的规格分组:', finalSpecGroups)
}
-// 根据选中的规格组合找到对应的规格规则ID
+// 根据选中的规格组合找到对应的规格规则ID(忽略顺序,按ID集合匹配)
const getSelectedSpecificationId = () => {
- const specNames = Object.keys(specGroups.value)
- const selectedIds = []
-
- // 按规格名称顺序收集选中的规格ID,转换为顺序编号
- specNames.forEach(specName => {
- if (selectedSpecs.value[specName]) {
- const specId = selectedSpecs.value[specName].id
- const orderNumber = specIdToOrder.value[specId]
- selectedIds.push(orderNumber)
- }
+ const required = requiredSpecNames.value
+ const selectedIds = required
+ .map(name => selectedSpecs.value[name]?.id)
+ .filter(Boolean)
+
+ // 只有在必选规格都已选择时才匹配具体规则
+ if (selectedIds.length !== required.length) return null
+
+ const availableSpecs = product.value.specifications?.filter(s => s.is_available) || []
+
+ const specification = availableSpecs.find(spec => {
+ const detailIds = spec.spec_details.map(d => d.id)
+ return detailIds.length === selectedIds.length && selectedIds.every(id => detailIds.includes(id))
})
-
- // 生成combination_key
- const combinationKey = selectedIds.join('-')
-
- // 在specifications数组中找到对应的规格规则
- const specification = product.value.specifications?.find(spec =>
- spec.combination_key === combinationKey
- )
-
+
return specification ? specification.id : null
}
// 立即购买功能
const handlePurchase = async () => {
- // 检查是否选择了所有必需的规格
- const specNames = Object.keys(specGroups.value)
- for (const specName of specNames) {
+ // 只校验当前场景下的“必选规格”是否已选择
+ const required = requiredSpecNames.value
+ for (const specName of required) {
if (!selectedSpecs.value[specName]) {
ElMessage.warning(`请选择${specName}`)
return
@@ -375,7 +415,7 @@ const handlePurchase = async () => {
// 获取选中规格对应的规格规则ID
const specificationId = getSelectedSpecificationId()
if (!specificationId) {
- ElMessage.error('所选规格组合无效,请重新选择')
+ ElMessage.error('所选规格组合无效或不可用,请重新选择')
return
}
diff --git a/src/views/CouponManage.vue b/src/views/CouponManage.vue
index b482c42..b8cb990 100644
--- a/src/views/CouponManage.vue
+++ b/src/views/CouponManage.vue
@@ -27,7 +27,6 @@ const couponList = ref([])
const getCouponList = async () => {
const response = await api.get(`/coupon/user/${user.id}`)
couponList.value = response.data.coupon
- console.log(123,couponList.value)
}
const getCouponName = (couponType) => {
diff --git a/src/views/MyProfile.vue b/src/views/MyProfile.vue
index 8d33d98..9001445 100644
--- a/src/views/MyProfile.vue
+++ b/src/views/MyProfile.vue
@@ -33,7 +33,6 @@
支付方式
@@ -284,6 +291,7 @@