重构资产订阅功能,将弹窗模式改为外部页面跳转模式,移除AssetSubscribeDialog组件及其在App.vue中的引用,修改assetSubscribe工具类将showAssetSubscribeDialog方法替换为redirectToSubscribePage方法,当检测到402状态码时直接跳转到外部开通页面并携带资产ID和返回地址参数

This commit is contained in:
WUSIJIAN
2026-01-21 14:02:42 +08:00
parent aec7f3a017
commit af17d81422
5 changed files with 596 additions and 319 deletions

View File

@@ -1,277 +0,0 @@
<template>
<el-dialog
v-model="dialogVisible"
title="开通服务"
width="600px"
:close-on-click-modal="false"
@close="handleClose"
>
<div class="subscribe-container">
<div class="service-info">
<el-icon class="warning-icon"><WarningFilled /></el-icon>
<span class="service-name">{{ serviceName }}</span>
<span class="service-tip">服务未开通请选择套餐进行开通</span>
</div>
<div v-if="loading" class="loading-container">
<el-skeleton :rows="3" animated />
</div>
<div v-else-if="skuList.length === 0" class="empty-container">
<el-empty description="暂无可用套餐" />
</div>
<div v-else class="sku-list">
<div
v-for="sku in skuList"
:key="sku.id"
class="sku-item"
:class="{ active: selectedSku?.id === sku.id }"
@click="selectSku(sku)"
>
<div class="sku-header">
<span class="sku-name">{{ sku.skuName }}</span>
<el-tag v-if="sku.unlimitedStock" type="success" size="small">不限库存</el-tag>
</div>
<div class="sku-body">
<div class="sku-specs">
<span class="specs-count">{{ sku.specsCount }}</span>
<span class="specs-unit">{{ sku.specsUnit?.value || '' }}</span>
</div>
<div class="sku-price">
<span class="price-symbol">¥</span>
<span class="price-value">{{ (sku.price / 100).toFixed(2) }}</span>
</div>
</div>
</div>
</div>
</div>
<template #footer>
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" :disabled="!selectedSku" :loading="submitLoading" @click="handleSubscribe">
立即开通
</el-button>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { ref, computed, watch } from 'vue';
import { WarningFilled } from '@element-plus/icons-vue';
import { ElMessage } from 'element-plus';
import { getAssetAndSku, subscribeAsset } from '/@/api/assets/asset/index';
interface SkuItem {
id: string;
assetId: string;
assetName: string;
skuName: string;
price: number;
specsCount: number;
specsUnit: { key: string; value: string } | null;
unlimitedStock: boolean;
stock: number;
status: number;
}
interface Props {
modelValue: boolean;
assetId: string;
serviceName: string;
}
const props = withDefaults(defineProps<Props>(), {
modelValue: false,
assetId: '',
serviceName: '',
});
const emit = defineEmits<{
(e: 'update:modelValue', value: boolean): void;
(e: 'success', sku: SkuItem): void;
}>();
const dialogVisible = computed({
get: () => props.modelValue,
set: (val) => emit('update:modelValue', val),
});
const loading = ref(false);
const submitLoading = ref(false);
const skuList = ref<SkuItem[]>([]);
const selectedSku = ref<SkuItem | null>(null);
// 监听弹窗打开加载SKU列表
watch(
() => props.modelValue,
(val) => {
if (val && props.assetId) {
loadSkuList();
}
}
);
// 加载SKU列表
const loadSkuList = async () => {
loading.value = true;
selectedSku.value = null;
try {
const res = await getAssetAndSku({
assetId: props.assetId,
});
skuList.value = res.data?.skuList || res.data?.list || [];
// 默认选中第一个
if (skuList.value.length > 0) {
selectedSku.value = skuList.value[0];
}
} catch (error) {
console.error('加载套餐列表失败:', error);
skuList.value = [];
} finally {
loading.value = false;
}
};
// 选择SKU
const selectSku = (sku: SkuItem) => {
selectedSku.value = sku;
};
// 开通服务
const handleSubscribe = async () => {
if (!selectedSku.value) {
ElMessage.warning('请选择套餐');
return;
}
submitLoading.value = true;
try {
await subscribeAsset({
skuId: selectedSku.value.id,
assetId: selectedSku.value.assetId,
});
ElMessage.success('开通成功');
emit('success', selectedSku.value);
handleClose();
// 刷新页面以重新加载数据
window.location.reload();
} catch (error) {
console.error('开通失败:', error);
} finally {
submitLoading.value = false;
}
};
// 关闭弹窗
const handleClose = () => {
dialogVisible.value = false;
selectedSku.value = null;
skuList.value = [];
};
</script>
<style scoped lang="scss">
.subscribe-container {
.service-info {
display: flex;
align-items: center;
gap: 8px;
padding: 16px;
background: #fff7e6;
border-radius: 8px;
margin-bottom: 20px;
.warning-icon {
font-size: 20px;
color: #fa8c16;
}
.service-name {
font-weight: 600;
color: #333;
}
.service-tip {
color: #666;
font-size: 14px;
}
}
.loading-container,
.empty-container {
padding: 40px 0;
}
.sku-list {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 16px;
.sku-item {
border: 2px solid #e8e8e8;
border-radius: 12px;
padding: 16px;
cursor: pointer;
transition: all 0.3s;
&:hover {
border-color: #409eff;
box-shadow: 0 2px 12px rgba(64, 158, 255, 0.2);
}
&.active {
border-color: #409eff;
background: #ecf5ff;
}
.sku-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
.sku-name {
font-size: 16px;
font-weight: 600;
color: #333;
}
}
.sku-body {
display: flex;
justify-content: space-between;
align-items: flex-end;
.sku-specs {
.specs-count {
font-size: 24px;
font-weight: 700;
color: #409eff;
}
.specs-unit {
font-size: 14px;
color: #666;
margin-left: 4px;
}
}
.sku-price {
color: #f56c6c;
.price-symbol {
font-size: 14px;
}
.price-value {
font-size: 20px;
font-weight: 700;
}
}
}
}
}
}
</style>