feat: 用户商品状态筛选与统计对接
Build and Deploy Vue3 / build (push) Successful in 1m46s
Build and Deploy Vue3 / deploy (push) Successful in 39s

- 新增 getUserGoodsCount 接口对接,列表页/虚拟机列表页增加状态筛选与统计卡片

- 已删除/已到期商品适配及相关页面更新

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
shiran
2026-06-24 22:12:50 +08:00
parent a8954bd85d
commit 6f82e5e79d
17 changed files with 1637 additions and 371 deletions
+60 -4
View File
@@ -711,6 +711,16 @@
/>
<div style="font-size:12px;color:#909399;margin-top:4px">限制单用户最大购买数量0 表示不限制</div>
</el-form-item>
<el-form-item label="首购最大时长" prop="max_first_purchase_duration">
<el-input-number
v-model="productForm.max_first_purchase_duration"
:min="0"
placeholder="0 表示不限"
controls-position="right"
style="width: 100%"
/>
<div style="font-size:12px;color:#909399;margin-top:4px">限制用户首次购买的最大时长单位0 表示不限制</div>
</el-form-item>
</div>
</el-tab-pane>
@@ -819,7 +829,8 @@
</template>
<script setup>
import { ref, reactive, computed, onMounted, watch } from 'vue'
import { ref, reactive, computed, onMounted, onActivated, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Plus, Refresh, Search, Folder, ArrowRight, Loading, Grid, List, Document, Picture, Delete, CollectionTag } from '@element-plus/icons-vue'
import {
@@ -832,10 +843,14 @@ import {
getProductGroupTagList,
deleteProductGroupTag,
getProductList,
getProductDetail,
createProduct,
updateProduct,
deleteProduct,
} from '@/api/admin/product'
const route = useRoute()
const router = useRouter()
import AvatarSelector from '@/components/admin/AvatarSelector.vue'
import GroupTagManager from './components/GroupTagManager.vue'
import ProductParameterManager from './components/ProductParameterManager.vue'
@@ -931,6 +946,7 @@ const productForm = reactive({
require_real_name: false,
sold_out: false,
max_per_user: 0,
max_first_purchase_duration: 0,
send_notice: false,
renew_price: 0,
renew_recommend_rebate: 0
@@ -1584,7 +1600,9 @@ const handleAddProduct = () => {
recommend: false,
recommend_rebate: 0,
arg_type: 'all',
require_real_name: false
require_real_name: false,
max_per_user: 0,
max_first_purchase_duration: 0
})
selectedProductGroup.value = null
@@ -1621,6 +1639,7 @@ const handleEditProduct = (product, parentGroupId) => {
require_real_name: product.requireRealName ?? product.require_real_name ?? false,
sold_out: !!product.soldOut,
max_per_user: product.maxPerUser ?? product.max_per_user ?? 0,
max_first_purchase_duration: product.maxFirstPurchaseDuration ?? product.max_first_purchase_duration ?? 0,
send_notice: !!product.sendNotice,
renew_price: (product.renewPrice ?? product.renew_price ?? 0) / 100,
renew_recommend_rebate: product.renewRecommendRebate ?? product.renew_recommend_rebate ?? 0
@@ -1652,6 +1671,7 @@ const submitProductForm = () => {
require_real_name: productForm.require_real_name,
sold_out: productForm.sold_out === true,
max_per_user: Number(productForm.max_per_user) || 0,
max_first_purchase_duration: Number(productForm.max_first_purchase_duration) || 0,
send_notice: productForm.send_notice === true,
renew_price: Number(productForm.renew_price) || 0,
renew_recommend_rebate: Number(productForm.renew_recommend_rebate) || 0
@@ -1866,9 +1886,45 @@ watch(activeTab, (newVal) => {
const groupTagManagerRef = ref(null)
// 初始化
onMounted(() => {
fetchGroupList()
// 根据 good_id 请求商品详情并弹出对应商品弹窗(用于从订单详情等页面跳转)
const openProductByGoodId = async (goodId) => {
const id = Number(goodId)
if (!id) return
try {
const res = await getProductDetail({ good_id: id })
if (res.data.code === 200 && res.data.data) {
handleEditProduct(res.data.data)
} else {
ElMessage.error(res.data.message || '获取商品详情失败')
}
} catch (error) {
console.error('获取商品详情失败:', error)
ElMessage.error('获取商品详情失败')
}
}
// 检查并处理 good_id 参数
const checkAndOpenGoodId = async () => {
if (route.query.good_id) {
await openProductByGoodId(route.query.good_id)
router.replace({ query: {} })
}
}
onMounted(async () => {
await fetchGroupList()
fetchAllTagOptions()
await checkAndOpenGoodId()
})
// 组件被 keep-alive 缓存后重新激活时,再次检查 good_id
onActivated(() => {
checkAndOpenGoodId()
})
// 同一页面内 query 变化时也触发(如从其他标签页点击跳转)
watch(() => route.query.good_id, (newId) => {
if (newId) checkAndOpenGoodId()
})
</script>