Files
console-dev/app/components/mfcloud/MfCloudDefence.vue
T

526 lines
18 KiB
Vue

<script>
/**
* 每 IP 防御调整面板:父级提供实例、模块、IP 集合及操作权限,订单通过 pay 事件交回父级支付。
* 配置加载、取价、下单分别计数;IP/实例/目标档位变化时旧报价立即失效。
*/
import "~/assets/styles/MfCloudDefence.B2vl5Yip.css";
import { toDisplayString } from "vue";
import { onMounted } from "vue";
import { useId } from "vue";
import { onBeforeUnmount } from "vue";
import { watch } from "vue";
import { createBlock } from "vue";
import { ref } from "vue";
import { withCtx } from "vue";
import { createCommentVNode } from "vue";
import { computed } from "vue";
import { isRef } from "vue";
import { createElementBlock } from "vue";
import { createElementVNode } from "vue";
import { createTextVNode } from "vue";
import { unref } from "vue";
import { defineComponent } from "vue";
import { createVNode } from "vue";
import { openBlock } from "vue";
import { useApi } from "~/lib/auth.js";
import dialog from "primevue/dialog";
import button from "primevue/button";
import { t as _plugin_vue_export_helper_t } from "~/lib/_plugin-vue_export-helper.js";
import message from "primevue/message";
import select from "primevue/select";
import { n as cloud_instance_management_n } from "~/lib/cloud-instance-management.js";
import { s as cloud_instance_management_s } from "~/lib/cloud-instance-management.js";
import { t as cloud_instance_management_t } from "~/lib/cloud-instance-management.js";
// 恢复渲染函数的静态节点属性;保留 scopeId 和既有 class 与样式文件的对应关系。
var E = {
class: `defence network-section`,
},
D = {
class: `network-head`,
},
O = {
class: `network-actions`,
},
te = {
key: 0,
},
ne = {
key: 1,
class: `muted`,
},
re = {
key: 2,
class: `muted`,
},
ie = {
key: 3,
class: `muted`,
},
ae = {
key: 4,
class: `muted`,
},
oe = {
class: `form`,
},
se = {
key: 0,
},
k = Object.assign(
_plugin_vue_export_helper_t(
defineComponent({
__name: `MfCloudDefence`,
props: {
hostId: {},
module: {},
ips: {},
active: {
type: Boolean,
},
upgradeAllowed: {
type: Boolean,
},
},
emits: [`pay`],
setup(h, { emit: b }) {
let k = h,
ce = b,
A = useApi(),
le = useId(),
j = ref(k.ips[0] ?? ``),
M = ref(``),
N = ref(``),
P = ref([]),
F = ref(!1),
I = ref(!1),
L = ref(``),
R = ref(``),
z = ref(!1),
B = ref(!1),
V = ref(``),
H = ref(``),
// 报价快照同时包含权限和弹窗状态,不能把关闭前或另一实例的价格继续用于下单。
U = computed(() =>
JSON.stringify([
k.hostId,
k.module,
j.value,
M.value,
z.value,
k.upgradeAllowed,
]),
),
W = ref(``),
// 不把当前档位再次当作可购买目标;K 要求当前选择确实存在且报价快照一致。
G = computed(() =>
P.value.filter((e) => String(e.value) !== String(N.value)),
),
K = computed(
() =>
!!W.value &&
W.value === U.value &&
!B.value &&
!I.value &&
k.upgradeAllowed &&
k.active &&
G.value.some((e) => String(e.value) === String(M.value)),
),
q = 0,
J = 0,
Y = 0,
X = !1;
// 同步 watcher 调用此函数,使配置变化与按钮禁用发生在同一个更新周期。
function Z() {
(J++,
(W.value = ``),
(V.value = ``),
(H.value = ``),
(B.value = !1));
}
watch(U, Z, {
flush: `sync`,
});
/** 按选中 IP 取防御配置,通过统一适配器校验列表和当前值,空 IP 不发请求。 */
async function Q() {
let e = ++q,
t = j.value,
n = k.hostId,
r = k.module;
if (
((P.value = []),
(L.value = ``),
(F.value = !0),
(N.value = ``),
Z(),
!t)
) {
F.value = !1;
return;
}
try {
let i = await A.get(`/${r}/${n}/upgrade_defence_config`, {
ip: t,
});
if (X || e !== q) return;
let a = cloud_instance_management_n(i);
((P.value = a.options), (N.value = a.current));
} catch (t) {
!X &&
e === q &&
(L.value = t instanceof Error ? t.message : `加载防御配置失败`);
} finally {
!X && e === q && (F.value = !1);
}
}
// 只有实例活动、允许升级且存在其他档位时才打开空白调整表单。
function ue() {
k.upgradeAllowed &&
k.active &&
!I.value &&
!F.value &&
G.value.length &&
((M.value = ``), (R.value = ``), (z.value = !0));
}
/** 取得本次费用与续费差价;只接受序号和快照仍属于当前选择的结果。 */
async function $() {
if (
(Z(),
!z.value ||
!k.upgradeAllowed ||
!G.value.some((e) => String(e.value) === String(M.value)))
)
return;
let e = ++J,
t = U.value;
((B.value = !0), (R.value = ``));
try {
let n = await A.get(
`/${k.module}/${k.hostId}/upgrade_defence/price`,
{
ip: j.value,
peak_defence: M.value,
},
);
if (X || e !== J || t !== U.value) return;
((V.value = cloud_instance_management_t(n.price)),
(H.value = String(n.renew_price_difference ?? ``)),
(W.value = t));
} catch (t) {
!X &&
e === J &&
(R.value = t instanceof Error ? t.message : `报价失败`);
} finally {
!X && e === J && (B.value = !1);
}
}
/** 先核验有效报价再创建订单;只把合法订单号及金额发给父级,不在这里执行支付。 */
async function de() {
if (!K.value || X) return;
let e = U.value,
t = V.value,
n = ++Y;
((I.value = !0), (R.value = ``));
try {
let n = await A.post(
`/${k.module}/${k.hostId}/upgrade_defence/order`,
{
ip: j.value,
peak_defence: M.value,
},
);
if (X || e !== U.value) return;
let r = cloud_instance_management_s(n.id);
((z.value = !1),
ce(`pay`, {
order_id: r,
amount:
n.amount == null
? t
: cloud_instance_management_t(n.amount),
}));
} catch (t) {
!X &&
e === U.value &&
(R.value = t instanceof Error ? t.message : `创建订单失败`);
} finally {
!X && n === Y && (I.value = !1);
}
}
return (
watch(j, () => {
((z.value = !1), Q());
}),
watch(
() => [k.hostId, k.module, k.ips.join(`,`)],
() => {
(Y++,
(z.value = !1),
(I.value = !1),
(j.value = k.ips.includes(j.value)
? j.value
: (k.ips[0] ?? ``)),
Q());
},
),
onMounted(Q),
// HTTP 请求未必即时取消,因此卸载标记和递增序号共同阻止迟到响应回填。
onBeforeUnmount(() => {
((X = !0), q++, Z());
}),
// UI 分为 IP/档位状态、加载错误与调整弹窗;提交期间禁用选择和关闭操作。
(t, n) => {
let r = select,
i = button,
o = message,
l = dialog;
return (
openBlock(),
createElementBlock(`section`, E, [
createElementVNode(`div`, D, [
(n[4] ||= createElementVNode(`h4`, null, `每 IP 防御`, -1)),
createElementVNode(`div`, O, [
createVNode(
r,
{
class: `defence-ip`,
modelValue: unref(j),
"onUpdate:modelValue": (n[0] ||= (e) =>
isRef(j) ? (j.value = e) : null),
options: h.ips,
"input-id": `${unref(le)}-ip`,
"aria-label": `防御 IP`,
disabled: unref(I) || !h.ips.length,
},
null,
8,
[`modelValue`, `options`, `input-id`, `disabled`],
),
createVNode(
i,
{
label: `刷新`,
text: ``,
size: `small`,
loading: unref(F),
disabled: unref(I),
onClick: Q,
},
null,
8,
[`loading`, `disabled`],
),
createVNode(
i,
{
label: `调整防御`,
size: `small`,
disabled:
!h.upgradeAllowed ||
!h.active ||
unref(I) ||
unref(F) ||
!unref(G).length,
onClick: ue,
},
null,
8,
[`disabled`],
),
]),
]),
unref(N) === ``
? createCommentVNode(``, !0)
: (openBlock(),
createElementBlock(
`p`,
te,
`当前防御:` + toDisplayString(unref(N)) + ` Gbps`,
1,
)),
h.ips.length
? !unref(F) && !unref(L) && !unref(P).length
? (openBlock(),
createElementBlock(`p`, re, `当前 IP 未开放防御调整。`))
: !unref(F) && !unref(L) && !unref(G).length
? (openBlock(),
createElementBlock(
`p`,
ie,
`当前 IP 没有其他可选防御档位。`,
))
: createCommentVNode(``, !0)
: (openBlock(),
createElementBlock(
`p`,
ne,
`暂无可调整防御的 IP 地址。`,
)),
h.upgradeAllowed
? createCommentVNode(``, !0)
: (openBlock(),
createElementBlock(`p`, ae, `当前实例不允许升降级。`)),
unref(L)
? (openBlock(),
createBlock(
o,
{
key: 5,
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(toDisplayString(unref(L)), 1),
createVNode(i, {
label: `重试`,
text: ``,
size: `small`,
onClick: Q,
}),
]),
_: 1,
},
))
: createCommentVNode(``, !0),
createVNode(
l,
{
visible: unref(z),
"onUpdate:visible": (n[3] ||= (e) =>
isRef(z) ? (z.value = e) : null),
header: `调整 IP 防御`,
modal: ``,
closable: !unref(I),
"close-on-escape": !unref(I),
},
{
footer: withCtx(() => [
createVNode(
i,
{
label: `取消`,
text: ``,
severity: `secondary`,
disabled: unref(I),
onClick: (n[2] ||= (e) => (z.value = !1)),
},
null,
8,
[`disabled`],
),
createVNode(
i,
{
label: `创建订单`,
loading: unref(I),
disabled: !unref(K),
onClick: de,
},
null,
8,
[`loading`, `disabled`],
),
]),
default: withCtx(() => [
createElementVNode(`div`, oe, [
createElementVNode(
`p`,
null,
toDisplayString(unref(j)),
1,
),
createVNode(
r,
{
modelValue: unref(M),
"onUpdate:modelValue": (n[1] ||= (e) =>
isRef(M) ? (M.value = e) : null),
options: unref(G),
"option-label": `desc`,
"option-value": `value`,
"aria-label": `目标防御峰值`,
disabled: unref(I),
onChange: $,
},
null,
8,
[`modelValue`, `options`, `disabled`],
),
createElementVNode(
`p`,
null,
`本次费用:` +
toDisplayString(
unref(B)
? `正在计算…`
: unref(V) === ``
? `待确认`
: `¥${unref(V)}`,
),
1,
),
unref(H) === ``
? createCommentVNode(``, !0)
: (openBlock(),
createElementBlock(
`p`,
se,
`续费差价:¥` + toDisplayString(unref(H)),
1,
)),
unref(R)
? (openBlock(),
createBlock(
o,
{
key: 1,
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(
toDisplayString(unref(R)),
1,
),
createVNode(
i,
{
label: `重新报价`,
text: ``,
size: `small`,
disabled: unref(I),
onClick: $,
},
null,
8,
[`disabled`],
),
]),
_: 1,
},
))
: createCommentVNode(``, !0),
]),
]),
_: 1,
},
8,
[`visible`, `closable`, `close-on-escape`],
),
])
);
}
);
},
}),
[[`__scopeId`, `data-v-b0c2bd86`]],
),
{
__name: `MfCloudDefence`,
},
);
export {};
export default k;
</script>