新增资产服务订阅功能,在应用中集成模块未开通弹窗组件,当检测到402状态码时自动弹出订阅对话框引导用户开通服务,同时新增资产订阅相关API接口包括获取资产SKU信息和订阅服务接口,在请求拦截器中添加402状态码处理逻辑并过滤SKU查询接口避免循环触发
This commit is contained in:
277
src/components/assetSubscribe/index.vue
Normal file
277
src/components/assetSubscribe/index.vue
Normal file
@@ -0,0 +1,277 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user