新增服务器升级弹窗面板及功能实现
This commit is contained in:
parent
e51fe71dfe
commit
18b3d56e5e
@ -30,6 +30,13 @@
|
|||||||
>
|
>
|
||||||
续费
|
续费
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button
|
||||||
|
@click="upgradeServer"
|
||||||
|
:loading="upgradeLoading"
|
||||||
|
class="px-5 !bg-[#3b82f6] !hover:bg-[#2563eb] !text-white"
|
||||||
|
>
|
||||||
|
升级
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -230,6 +237,7 @@ export default {
|
|||||||
resetPasswordLoading: false,
|
resetPasswordLoading: false,
|
||||||
resetKeyPairLoading: false,
|
resetKeyPairLoading: false,
|
||||||
resetSystemLoading: false,
|
resetSystemLoading: false,
|
||||||
|
upgradeLoading: false,
|
||||||
copySuccess: false,
|
copySuccess: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@ -300,6 +308,15 @@ export default {
|
|||||||
onSuccess: this.onRenewalSuccess
|
onSuccess: this.onRenewalSuccess
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
upgradeServer() {
|
||||||
|
const JsiteServerUpgradeDialog = defineAsyncComponent(() => import('./JsiteServerUpgradeDialog.vue'));
|
||||||
|
|
||||||
|
renderDialog(h(JsiteServerUpgradeDialog, {
|
||||||
|
server: this.server,
|
||||||
|
serverDoc: this.$jsiteServer.pg,
|
||||||
|
onSuccess: this.onUpgradeSuccess
|
||||||
|
}));
|
||||||
|
},
|
||||||
async restartServer() {
|
async restartServer() {
|
||||||
if (!this.$jsiteServer.pg.instance_id) {
|
if (!this.$jsiteServer.pg.instance_id) {
|
||||||
toast.error('服务器实例ID不存在');
|
toast.error('服务器实例ID不存在');
|
||||||
@ -492,6 +509,11 @@ export default {
|
|||||||
// 刷新服务器数据
|
// 刷新服务器数据
|
||||||
this.$jsiteServer.reload();
|
this.$jsiteServer.reload();
|
||||||
},
|
},
|
||||||
|
onUpgradeSuccess(data) {
|
||||||
|
toast.success('服务器升级成功!');
|
||||||
|
// 刷新服务器数据
|
||||||
|
this.$jsiteServer.reload();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
serverInformation() {
|
serverInformation() {
|
||||||
|
|||||||
644
dashboard/src2/components/JsiteServerUpgradeDialog.vue
Normal file
644
dashboard/src2/components/JsiteServerUpgradeDialog.vue
Normal file
@ -0,0 +1,644 @@
|
|||||||
|
<template>
|
||||||
|
<Dialog :options="{ title: '服务器升级', size: 'lg' }" v-model="show">
|
||||||
|
<!-- 第一步:选择升级套餐和支付方式 -->
|
||||||
|
<template v-if="!showPaymentProcessing" #body-content>
|
||||||
|
<div class="p-4 sm:p-6">
|
||||||
|
<div class="mb-6">
|
||||||
|
<p class="mt-1 text-sm text-gray-600">
|
||||||
|
选择新的套餐配置,升级您的服务器性能
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 当前配置 -->
|
||||||
|
<div class="mb-6 border-t border-gray-200 pt-4">
|
||||||
|
<h3 class="text-lg font-medium text-gray-900 mb-4">当前配置</h3>
|
||||||
|
<div class="grid grid-cols-2 gap-4">
|
||||||
|
<div class="bg-gray-50 p-3 rounded-lg">
|
||||||
|
<div class="text-sm text-gray-600">CPU</div>
|
||||||
|
<div class="font-medium">{{ serverInfo?.cpu || '未知' }}核</div>
|
||||||
|
</div>
|
||||||
|
<div class="bg-gray-50 p-3 rounded-lg">
|
||||||
|
<div class="text-sm text-gray-600">内存</div>
|
||||||
|
<div class="font-medium">{{ serverInfo?.memory || '未知' }}GB</div>
|
||||||
|
</div>
|
||||||
|
<div class="bg-gray-50 p-3 rounded-lg">
|
||||||
|
<div class="text-sm text-gray-600">系统盘</div>
|
||||||
|
<div class="font-medium">{{ serverInfo?.disk_size || '未知' }}GB</div>
|
||||||
|
</div>
|
||||||
|
<div class="bg-gray-50 p-3 rounded-lg">
|
||||||
|
<div class="text-sm text-gray-600">带宽</div>
|
||||||
|
<div class="font-medium">{{ serverInfo?.bandwidth || '未知' }}Mbps</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-3 text-sm text-gray-600">
|
||||||
|
当前月费:¥{{ serverInfo?.plan_price || '0' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 可升级套餐列表 -->
|
||||||
|
<div class="mb-6">
|
||||||
|
<h3 class="text-lg font-medium text-gray-900 mb-4">选择升级套餐</h3>
|
||||||
|
<div v-if="upgradePlansLoading" class="text-center py-8">
|
||||||
|
<div class="h-8 w-8 mx-auto animate-spin text-blue-600 mb-4 flex items-center justify-center">
|
||||||
|
<i class="fe fe-loader text-2xl"></i>
|
||||||
|
</div>
|
||||||
|
<p class="text-gray-700">正在加载可升级套餐...</p>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="upgradePlans.length === 0" class="text-center py-8">
|
||||||
|
<p class="text-gray-500">暂无可升级套餐</p>
|
||||||
|
</div>
|
||||||
|
<div v-else class="space-y-3">
|
||||||
|
<div
|
||||||
|
v-for="plan in upgradePlans"
|
||||||
|
:key="plan.plan_id"
|
||||||
|
@click="selectedPlan = plan"
|
||||||
|
:class="[
|
||||||
|
'p-4 border rounded-lg cursor-pointer transition-all',
|
||||||
|
selectedPlan?.plan_id === plan.plan_id
|
||||||
|
? 'border-blue-500 bg-blue-50'
|
||||||
|
: 'border-gray-200 hover:border-gray-300'
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<div class="flex justify-between items-start">
|
||||||
|
<div class="flex-1">
|
||||||
|
<div class="font-medium text-gray-900 mb-2">
|
||||||
|
{{ plan.core }}核 {{ plan.memory }}GB {{ plan.disk_size }}GB
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-2 gap-2 text-sm text-gray-600">
|
||||||
|
<div>CPU: {{ plan.core }}核</div>
|
||||||
|
<div>内存: {{ plan.memory }}GB</div>
|
||||||
|
<div>系统盘: {{ plan.disk_size }}GB</div>
|
||||||
|
<div>带宽: {{ plan.bandwidth }}Mbps</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-right">
|
||||||
|
<div class="text-lg font-bold text-blue-600">
|
||||||
|
¥{{ plan.origin_price }}/月
|
||||||
|
</div>
|
||||||
|
<div v-if="getPriceDiff(plan) > 0" class="text-sm text-green-600">
|
||||||
|
+¥{{ getPriceDiff(plan) }}/月
|
||||||
|
</div>
|
||||||
|
<div v-else-if="getPriceDiff(plan) < 0" class="text-sm text-orange-600">
|
||||||
|
-¥{{ Math.abs(getPriceDiff(plan)) }}/月
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 升级费用计算 -->
|
||||||
|
<div v-if="selectedPlan && upgradePriceInfo" class="mb-6 border-t border-gray-200 pt-4">
|
||||||
|
<h3 class="text-lg font-medium text-gray-900 mb-4">费用明细</h3>
|
||||||
|
<div class="space-y-2">
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<span class="text-gray-600">当前套餐月费</span>
|
||||||
|
<span>¥{{ upgradePriceInfo.current_plan_price }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<span class="text-gray-600">新套餐月费</span>
|
||||||
|
<span>¥{{ upgradePriceInfo.new_plan_price }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<span class="text-gray-600">剩余天数</span>
|
||||||
|
<span>{{ upgradePriceInfo.remaining_days }}天</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<span class="text-gray-600">每日差价</span>
|
||||||
|
<span>¥{{ upgradePriceInfo.daily_price_diff.toFixed(2) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="border-t pt-2">
|
||||||
|
<div class="flex justify-between text-lg font-bold">
|
||||||
|
<span>升级费用</span>
|
||||||
|
<span class="text-blue-600">¥{{ upgradePriceInfo.upgrade_amount.toFixed(2) }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 支付方式选择 -->
|
||||||
|
<div class="mb-6 border-t border-gray-200 pt-4">
|
||||||
|
<label class="block text-sm font-medium text-gray-700 mb-3">
|
||||||
|
选择支付方式
|
||||||
|
</label>
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mt-2">
|
||||||
|
<button
|
||||||
|
class="p-4 border rounded-lg flex flex-col items-center justify-center hover:bg-gray-50 transition-all"
|
||||||
|
:class="{'border-blue-500 border-2 shadow-sm': selectedPaymentMethod === 'balance', 'border-gray-200': selectedPaymentMethod !== 'balance'}"
|
||||||
|
@click="selectedPaymentMethod = 'balance'"
|
||||||
|
>
|
||||||
|
<span class="text-gray-800 font-medium">余额支付</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="p-4 border rounded-lg hover:bg-gray-50 transition-all"
|
||||||
|
:class="{'border-blue-500 border-2 shadow-sm': selectedPaymentMethod === 'alipay', 'border-gray-200': selectedPaymentMethod !== 'alipay'}"
|
||||||
|
@click="selectedPaymentMethod = 'alipay'"
|
||||||
|
>
|
||||||
|
<div class="flex flex-col items-center">
|
||||||
|
<div class="mb-2">
|
||||||
|
<AlipayLogo class="h-8" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="p-4 border rounded-lg hover:bg-gray-50 transition-all"
|
||||||
|
:class="{'border-blue-500 border-2 shadow-sm': selectedPaymentMethod === 'wechatpay', 'border-gray-200': selectedPaymentMethod !== 'wechatpay'}"
|
||||||
|
@click="selectedPaymentMethod = 'wechatpay'"
|
||||||
|
>
|
||||||
|
<div class="flex flex-col items-center">
|
||||||
|
<div class="mb-2">
|
||||||
|
<WeChatPayLogo class="h-8" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="error" class="mt-4 p-3 bg-red-50 text-red-700 rounded-md text-sm">
|
||||||
|
{{ error }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 第二步:处理支付 -->
|
||||||
|
<template v-else #body-content>
|
||||||
|
<div class="p-4 sm:p-6">
|
||||||
|
<!-- 加载中状态 -->
|
||||||
|
<div v-if="isProcessingPayment" class="text-center py-8">
|
||||||
|
<div class="h-12 w-12 mx-auto animate-spin text-blue-600 mb-4 flex items-center justify-center">
|
||||||
|
<i class="fe fe-loader text-3xl"></i>
|
||||||
|
</div>
|
||||||
|
<p class="text-gray-700 text-lg">正在处理支付,请稍候...</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 支付成功状态 -->
|
||||||
|
<div v-else-if="paymentSuccess" class="text-center py-8">
|
||||||
|
<div class="flex justify-center mb-6">
|
||||||
|
<div class="rounded-full bg-green-100 p-3">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-12 w-12 text-green-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h3 class="text-xl font-medium text-gray-900 mb-2">支付成功!</h3>
|
||||||
|
<p class="text-gray-600 mb-2">
|
||||||
|
您的服务器升级已提交,请3至5分钟后刷新页面查看结果...
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 微信支付状态 -->
|
||||||
|
<div v-else-if="selectedPaymentMethod === 'wechatpay' && paymentQrCode" class="text-center">
|
||||||
|
<div class="relative mb-6">
|
||||||
|
<div class="flex items-center justify-center">
|
||||||
|
<WeChatPayLogo class="h-10" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-center mb-6">
|
||||||
|
<div class="text-sm text-gray-600 mb-2">扫一扫付款(元)</div>
|
||||||
|
<div class="text-3xl font-bold text-[#FF0036]">{{ order?.total_amount }} 元</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex justify-center">
|
||||||
|
<div class="qrcode-container bg-white p-4 border border-gray-100 shadow-sm rounded-lg">
|
||||||
|
<img
|
||||||
|
:src="paymentQrCodeImage"
|
||||||
|
alt="微信支付二维码"
|
||||||
|
class="qrcode-image"
|
||||||
|
@load="qrcodeLoaded = true"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-6 text-center">
|
||||||
|
<p class="text-gray-600">
|
||||||
|
请使用微信扫描二维码完成支付
|
||||||
|
</p>
|
||||||
|
<p class="text-gray-500 text-sm mt-2">二维码有效期 15 分钟</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 支付宝支付状态 -->
|
||||||
|
<div v-else-if="selectedPaymentMethod === 'alipay' && paymentUrl" class="text-center py-6">
|
||||||
|
<div class="mb-6">
|
||||||
|
<AlipayLogo class="h-10 mx-auto" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 class="text-lg font-medium text-gray-900 mb-4">请在新页面完成支付宝支付</h3>
|
||||||
|
<p class="text-gray-600 mb-6">
|
||||||
|
如果没有自动跳转,请点击下方按钮打开支付页面
|
||||||
|
</p>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<button
|
||||||
|
class="w-full px-6 py-3 bg-[#1677FF] text-white rounded-lg hover:bg-[#0E5FD8] transition-colors shadow-sm"
|
||||||
|
@click="window.open(paymentUrl, '_blank')"
|
||||||
|
>
|
||||||
|
打开支付页面
|
||||||
|
</button>
|
||||||
|
<p class="text-gray-600 text-sm mt-4">
|
||||||
|
支付完成后,请稍等片刻,系统会自动刷新页面
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="error" class="mt-4 p-3 bg-red-50 text-red-700 rounded-md text-sm">
|
||||||
|
{{ error }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #actions>
|
||||||
|
<div class="w-full">
|
||||||
|
<button v-if="!showPaymentProcessing && !paymentSuccess"
|
||||||
|
type="button"
|
||||||
|
class="w-full px-4 py-2 bg-[#1fc76f] border border-transparent rounded-md text-sm font-medium text-white hover:bg-[#19b862] focus:outline-none"
|
||||||
|
@click="createUpgradeOrder"
|
||||||
|
:disabled="isLoading || !selectedPlan || !selectedPaymentMethod"
|
||||||
|
>
|
||||||
|
{{ isLoading ? '处理中...' : '确认升级' }}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="flex justify-between w-full" v-else>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="px-4 py-2 bg-[#1fc76f] border border-transparent rounded-md text-sm font-medium text-white hover:bg-[#19b862] focus:outline-none ml-auto"
|
||||||
|
@click="cancel"
|
||||||
|
v-if="paymentSuccess"
|
||||||
|
>
|
||||||
|
关闭
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { toast } from 'vue-sonner';
|
||||||
|
import { Dialog } from 'jingrow-ui';
|
||||||
|
import { DashboardError } from '../utils/error';
|
||||||
|
import AlipayLogo from '../logo/AlipayLogo.vue';
|
||||||
|
import WeChatPayLogo from '../logo/WeChatPayLogo.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'JsiteServerUpgradeDialog',
|
||||||
|
components: {
|
||||||
|
Dialog,
|
||||||
|
AlipayLogo,
|
||||||
|
WeChatPayLogo
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
server: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
serverDoc: {
|
||||||
|
type: Object,
|
||||||
|
default: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
emits: ['success'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
show: true,
|
||||||
|
selectedPlan: null,
|
||||||
|
selectedPaymentMethod: null,
|
||||||
|
order: null,
|
||||||
|
showPaymentProcessing: false,
|
||||||
|
paymentSuccess: false,
|
||||||
|
error: null,
|
||||||
|
isProcessingPayment: false,
|
||||||
|
paymentUrl: null,
|
||||||
|
paymentQrCode: null,
|
||||||
|
paymentQrCodeImage: null,
|
||||||
|
checkInterval: null,
|
||||||
|
upgradePlans: [],
|
||||||
|
upgradePlansLoading: false,
|
||||||
|
upgradeInfo: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
serverInfo() {
|
||||||
|
if (!this.serverDoc) return null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
cpu: this.serverDoc.cpu,
|
||||||
|
memory: this.serverDoc.memory,
|
||||||
|
disk_size: this.serverDoc.disk_size,
|
||||||
|
bandwidth: this.serverDoc.bandwidth,
|
||||||
|
plan_price: this.serverDoc.plan_price || 0,
|
||||||
|
instance_id: this.serverDoc.instance_id,
|
||||||
|
region: this.serverDoc.region
|
||||||
|
};
|
||||||
|
},
|
||||||
|
upgradePriceInfo() {
|
||||||
|
if (!this.selectedPlan || !this.serverInfo) return null;
|
||||||
|
|
||||||
|
const currentPrice = this.serverInfo.plan_price;
|
||||||
|
const newPrice = parseFloat(this.selectedPlan.origin_price);
|
||||||
|
const priceDiff = newPrice - currentPrice;
|
||||||
|
|
||||||
|
// 假设剩余30天(这里应该从服务器信息获取实际剩余天数)
|
||||||
|
const remainingDays = 30;
|
||||||
|
const dailyPriceDiff = priceDiff / 30;
|
||||||
|
const upgradeAmount = dailyPriceDiff * remainingDays;
|
||||||
|
|
||||||
|
return {
|
||||||
|
current_plan_price: currentPrice,
|
||||||
|
new_plan_price: newPrice,
|
||||||
|
remaining_days: remainingDays,
|
||||||
|
daily_price_diff: dailyPriceDiff,
|
||||||
|
upgrade_amount: upgradeAmount
|
||||||
|
};
|
||||||
|
},
|
||||||
|
isLoading() {
|
||||||
|
return this.$resources.createUpgradeOrder.loading;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async mounted() {
|
||||||
|
await this.loadUpgradePlans();
|
||||||
|
},
|
||||||
|
resources: {
|
||||||
|
loadUpgradePlans() {
|
||||||
|
return {
|
||||||
|
url: 'jcloud.api.aliyun_server_light.get_aliyun_instance_upgrade_plans',
|
||||||
|
params: {
|
||||||
|
instance_id: this.serverInfo?.instance_id,
|
||||||
|
region_id: this.serverInfo?.region
|
||||||
|
},
|
||||||
|
onSuccess(data) {
|
||||||
|
if (data.success && data.data && data.data.plans) {
|
||||||
|
this.upgradePlans = data.data.plans;
|
||||||
|
} else {
|
||||||
|
this.error = data.message || '获取可升级套餐失败';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onError(error) {
|
||||||
|
this.error = error.message || '获取可升级套餐失败';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
createUpgradeOrder() {
|
||||||
|
return {
|
||||||
|
url: 'jcloud.api.aliyun_server_light.create_server_upgrade_order',
|
||||||
|
validate() {
|
||||||
|
if (!this.server) {
|
||||||
|
throw new DashboardError('缺少服务器信息');
|
||||||
|
}
|
||||||
|
if (!this.selectedPlan) {
|
||||||
|
throw new DashboardError('请选择升级套餐');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSuccess(data) {
|
||||||
|
if (!data.success) {
|
||||||
|
this.error = data.message || '创建升级订单失败';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存升级信息
|
||||||
|
this.upgradeInfo = data.upgrade_info;
|
||||||
|
|
||||||
|
// 显示订单支付界面
|
||||||
|
this.order = data.order;
|
||||||
|
this.showPaymentProcessing = true;
|
||||||
|
|
||||||
|
// 立即处理支付
|
||||||
|
this.processPayment();
|
||||||
|
},
|
||||||
|
onError(error) {
|
||||||
|
this.error = error.message || '创建升级订单失败';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
processBalancePayment() {
|
||||||
|
return {
|
||||||
|
url: 'jcloud.api.billing.process_balance_payment_for_server_order',
|
||||||
|
params: {},
|
||||||
|
validate() {
|
||||||
|
if (!this.order || !this.order.order_id) {
|
||||||
|
throw new DashboardError('缺少订单信息');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSuccess(response) {
|
||||||
|
if (response.status === "Error" || response.success === false) {
|
||||||
|
toast.error(response.message || '支付失败,请确保余额充足');
|
||||||
|
this.error = response.message || '余额不足';
|
||||||
|
this.isProcessingPayment = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 支付成功,等待支付完成回调处理升级
|
||||||
|
this.$emit('success', {
|
||||||
|
order: this.order,
|
||||||
|
selected_plan: this.selectedPlan,
|
||||||
|
message: response.message || '支付成功'
|
||||||
|
});
|
||||||
|
|
||||||
|
this.isProcessingPayment = false;
|
||||||
|
this.paymentSuccess = true;
|
||||||
|
},
|
||||||
|
onError(error) {
|
||||||
|
this.error = error.message || '余额支付处理失败';
|
||||||
|
this.isProcessingPayment = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
processAlipayPayment() {
|
||||||
|
return {
|
||||||
|
url: 'jcloud.api.billing.process_alipay_order',
|
||||||
|
params: {},
|
||||||
|
validate() {
|
||||||
|
if (!this.order || !this.order.order_id) {
|
||||||
|
throw new DashboardError('缺少订单信息');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSuccess(response) {
|
||||||
|
this.paymentUrl = response.payment_url;
|
||||||
|
window.open(response.payment_url, '_blank');
|
||||||
|
|
||||||
|
toast.success('支付页面已在新窗口打开');
|
||||||
|
|
||||||
|
// 开始轮询支付状态
|
||||||
|
this.startPaymentCheck();
|
||||||
|
},
|
||||||
|
onError(error) {
|
||||||
|
this.error = error.message || '支付宝支付处理失败';
|
||||||
|
this.isProcessingPayment = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
processWechatPayment() {
|
||||||
|
return {
|
||||||
|
url: 'jcloud.api.billing.process_wechatpay_order',
|
||||||
|
params: {},
|
||||||
|
validate() {
|
||||||
|
if (!this.order || !this.order.order_id) {
|
||||||
|
throw new DashboardError('缺少订单信息');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSuccess(response) {
|
||||||
|
this.paymentQrCode = response.payment_url;
|
||||||
|
this.paymentQrCodeImage = response.qr_code_image;
|
||||||
|
|
||||||
|
toast.success('请使用微信扫码支付');
|
||||||
|
|
||||||
|
// 开始轮询支付状态
|
||||||
|
this.startPaymentCheck();
|
||||||
|
},
|
||||||
|
onError(error) {
|
||||||
|
this.error = error.message || '微信支付处理失败';
|
||||||
|
this.isProcessingPayment = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
checkPaymentStatus() {
|
||||||
|
return {
|
||||||
|
url: 'jcloud.api.billing.check_order_payment_status',
|
||||||
|
params: {},
|
||||||
|
validate() {
|
||||||
|
if (!this.order || !this.order.order_id) {
|
||||||
|
throw new DashboardError('缺少订单信息');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSuccess(data) {
|
||||||
|
if (data && data.status === '已支付') {
|
||||||
|
// 支付成功,停止轮询
|
||||||
|
this.stopPaymentCheck();
|
||||||
|
|
||||||
|
// 支付成功,等待支付完成回调处理升级
|
||||||
|
this.$emit('success', {
|
||||||
|
order: this.order,
|
||||||
|
selected_plan: this.selectedPlan,
|
||||||
|
message: '支付成功'
|
||||||
|
});
|
||||||
|
|
||||||
|
this.paymentSuccess = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onError(error) {
|
||||||
|
console.error('检查支付状态失败:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async loadUpgradePlans() {
|
||||||
|
this.upgradePlansLoading = true;
|
||||||
|
this.error = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.$resources.loadUpgradePlans.submit();
|
||||||
|
} catch (error) {
|
||||||
|
this.error = error.message || '获取可升级套餐失败';
|
||||||
|
} finally {
|
||||||
|
this.upgradePlansLoading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getPriceDiff(plan) {
|
||||||
|
if (!this.serverInfo) return 0;
|
||||||
|
const currentPrice = this.serverInfo.plan_price;
|
||||||
|
const newPrice = parseFloat(plan.origin_price);
|
||||||
|
return newPrice - currentPrice;
|
||||||
|
},
|
||||||
|
cancel() {
|
||||||
|
this.stopPaymentCheck();
|
||||||
|
this.show = false;
|
||||||
|
},
|
||||||
|
createUpgradeOrder() {
|
||||||
|
if (!this.selectedPaymentMethod) {
|
||||||
|
this.error = '请选择支付方式';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.error = null;
|
||||||
|
this.$resources.createUpgradeOrder.submit({
|
||||||
|
server: this.server,
|
||||||
|
new_plan_id: this.selectedPlan.plan_id
|
||||||
|
});
|
||||||
|
},
|
||||||
|
processPayment() {
|
||||||
|
this.isProcessingPayment = true;
|
||||||
|
this.error = null;
|
||||||
|
|
||||||
|
if (!this.order || !this.order.order_id) {
|
||||||
|
this.error = '订单信息不完整,请重试';
|
||||||
|
this.isProcessingPayment = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.selectedPaymentMethod === 'balance') {
|
||||||
|
this.$resources.processBalancePayment.submit({
|
||||||
|
order_id: this.order.order_id
|
||||||
|
});
|
||||||
|
} else if (this.selectedPaymentMethod === 'alipay') {
|
||||||
|
this.$resources.processAlipayPayment.submit({
|
||||||
|
order_id: this.order.order_id
|
||||||
|
});
|
||||||
|
} else if (this.selectedPaymentMethod === 'wechatpay') {
|
||||||
|
this.$resources.processWechatPayment.submit({
|
||||||
|
order_id: this.order.order_id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
startPaymentCheck() {
|
||||||
|
this.isProcessingPayment = false;
|
||||||
|
|
||||||
|
this.checkInterval = setInterval(() => {
|
||||||
|
this.$resources.checkPaymentStatus.submit({
|
||||||
|
order_id: this.order.order_id
|
||||||
|
});
|
||||||
|
}, 3000);
|
||||||
|
|
||||||
|
// 15分钟后停止检查
|
||||||
|
setTimeout(() => {
|
||||||
|
this.stopPaymentCheck();
|
||||||
|
}, 900000);
|
||||||
|
},
|
||||||
|
stopPaymentCheck() {
|
||||||
|
if (this.checkInterval) {
|
||||||
|
clearInterval(this.checkInterval);
|
||||||
|
this.checkInterval = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
this.stopPaymentCheck();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.qrcode-container {
|
||||||
|
height: 250px;
|
||||||
|
width: 250px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 5px;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
border-radius: 12px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qrcode-image {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 添加渐入动画 */
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(10px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.qrcode-container {
|
||||||
|
animation: fadeIn 0.3s ease-out;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -495,7 +495,7 @@ def monitor_server_status(instance_id, region_id):
|
|||||||
manager = _get_manager()
|
manager = _get_manager()
|
||||||
max_retries = 60 # 最多查询60次(10分钟)
|
max_retries = 60 # 最多查询60次(10分钟)
|
||||||
# 阿里云轻量应用服务器可能的状态值
|
# 阿里云轻量应用服务器可能的状态值
|
||||||
waiting_states = ['Starting', 'Stopping', 'Resetting', 'Stopped', 'Pending']
|
waiting_states = ['Starting', 'Stopping', 'Resetting', 'Stopped', 'Pending', 'Upgrading']
|
||||||
|
|
||||||
for retry in range(max_retries):
|
for retry in range(max_retries):
|
||||||
# 查询实例状态
|
# 查询实例状态
|
||||||
@ -811,6 +811,10 @@ def get_aliyun_instance_upgrade_plans(instance_id, region_id='cn-shanghai'):
|
|||||||
try:
|
try:
|
||||||
support_platform_list = json.loads(support_platform_str) if support_platform_str else []
|
support_platform_list = json.loads(support_platform_str) if support_platform_str else []
|
||||||
if server.os_type in support_platform_list:
|
if server.os_type in support_platform_list:
|
||||||
|
# 在origin_price上统一增加20%作为利润
|
||||||
|
if 'origin_price' in plan and plan['origin_price'] is not None:
|
||||||
|
original_price = float(plan['origin_price'])
|
||||||
|
plan['origin_price'] = int(original_price / (1 - 0.2)) # 确保20%利润率且价格为整数
|
||||||
filtered_plans.append(plan)
|
filtered_plans.append(plan)
|
||||||
except:
|
except:
|
||||||
continue
|
continue
|
||||||
@ -993,11 +997,19 @@ def create_server_upgrade_order(**kwargs):
|
|||||||
|
|
||||||
# 计算剩余天数
|
# 计算剩余天数
|
||||||
if server_pg.end_date:
|
if server_pg.end_date:
|
||||||
end_date = jingrow.utils.getdate(server_pg.end_date)
|
try:
|
||||||
current_date = jingrow.utils.nowdate()
|
# 确保end_date是datetime.date对象
|
||||||
remaining_days = (end_date - current_date).days
|
if isinstance(server_pg.end_date, str):
|
||||||
if remaining_days <= 0:
|
end_date = jingrow.utils.getdate(server_pg.end_date)
|
||||||
remaining_days = 1 # 至少1天
|
else:
|
||||||
|
end_date = server_pg.end_date
|
||||||
|
current_date = jingrow.utils.nowdate()
|
||||||
|
remaining_days = (end_date - current_date).days
|
||||||
|
if remaining_days <= 0:
|
||||||
|
remaining_days = 1 # 至少1天
|
||||||
|
except Exception as e:
|
||||||
|
jingrow.log_error("计算剩余天数失败", f"计算实例 {server_pg.instance_id} 剩余天数时发生错误: {str(e)}")
|
||||||
|
remaining_days = 30 # 出错时使用默认值
|
||||||
else:
|
else:
|
||||||
remaining_days = 30 # 默认30天
|
remaining_days = 30 # 默认30天
|
||||||
|
|
||||||
|
|||||||
@ -1092,6 +1092,9 @@ def handle_order_payment_complete(order_id):
|
|||||||
elif order.order_type == "服务器续费":
|
elif order.order_type == "服务器续费":
|
||||||
# 异步续费服务器
|
# 异步续费服务器
|
||||||
jingrow.enqueue('jcloud.api.aliyun_server_light.renew_server', order_name=order.name)
|
jingrow.enqueue('jcloud.api.aliyun_server_light.renew_server', order_name=order.name)
|
||||||
|
elif order.order_type == "服务器升级":
|
||||||
|
# 异步升级服务器
|
||||||
|
jingrow.enqueue('jcloud.api.aliyun_server_light.upgrade_server', order_name=order.name)
|
||||||
elif order.order_type == "新建服务器":
|
elif order.order_type == "新建服务器":
|
||||||
# 异步创建服务器
|
# 异步创建服务器
|
||||||
jingrow.enqueue('jcloud.api.aliyun_server_light.create_aliyun_server', order_name=order.name)
|
jingrow.enqueue('jcloud.api.aliyun_server_light.create_aliyun_server', order_name=order.name)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user