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

741 lines
27 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script>
/**
* 云实例 IPv4/IPv6 列表及附加地址购买;addresses 事件供父级更新可选 IP,pay 事件交接订单。
* 仅支持的云模块开放附加地址操作,upgradeAllowed 来自父级业务状态而非本地授权。
*/
import "~/assets/styles/MfCloudIps.jhFnuvcs.css";
import { toDisplayString } from "vue";
import { onMounted } 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 { reactive } from "vue";
import { isRef } from "vue";
import { createElementBlock } from "vue";
import { Fragment } 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 datatable from "primevue/datatable";
import inputnumber from "primevue/inputnumber";
import column from "primevue/column";
import OperationNotice from "~/components/OperationNotice.vue";
import { i as cloud_instance_management_i } 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";
var ce = {
class: `ips`,
},
C = {
class: `ips-head network-head`,
},
w = {
class: `ips-title`,
},
T = {
key: 0,
class: `count`,
},
E = {
class: `network-actions`,
},
le = {
class: `col-mono`,
},
ue = {
class: `col-mono`,
},
de = {
class: `col-mono`,
},
fe = {
class: `ipv6-group`,
},
pe = {
class: `ips-title ipv6-title`,
},
me = {
key: 0,
class: `count`,
},
he = {
class: `col-mono`,
},
ge = {
class: `dlg`,
},
_e = {
class: `form-row`,
},
ve = {
class: `form-row`,
},
ye = {
class: `quote`,
},
be = {
class: `quote-value`,
},
D = {
key: 0,
class: `pi pi-spin pi-spinner`,
},
O = Object.assign(
_plugin_vue_export_helper_t(
defineComponent({
__name: `MfCloudIps`,
props: {
hostId: {},
module: {
default: `mf_cloud`,
},
upgradeAllowed: {
type: Boolean,
default: !0,
},
},
emits: [`pay`, `addresses`],
setup(h, { emit: b }) {
let O = h,
k = b,
A = useApi(),
j = ref(``),
M = ref([]),
N = ref([]),
P = ref(!1),
F = ref(``),
I = ref(``),
L = 0,
R = 0,
z = 0,
B = !1;
/** 两类地址并行加载、分别显示错误;每类以 100 条分页完整拉取,实例变化使旧结果失效。 */
async function V() {
let e = ++L,
t = `/${O.module}/${O.hostId}`;
((P.value = !0), (F.value = ``), (I.value = ``));
// count 缺失时不足一页结束;有 count 却提前返回空页时不能当作完整列表。
async function n(n) {
let r = [],
i = 1;
for (;;) {
let a = await A.get(`${t}/${n}`, {
page: i,
limit: 100,
});
if (B || e !== L) return null;
if (!Array.isArray(a.list)) throw Error(`地址列表无效,请重试`);
if (
(r.push(...a.list),
a.count === void 0
? a.list.length < 100
: r.length >= Number(a.count))
)
return r;
if (!a.list.length) throw Error(`地址列表不完整,请重试`);
i++;
}
}
(await Promise.allSettled([
n(`ip`)
.then((e) => {
e &&
((M.value = e),
k(`addresses`, e.map((e) => e.ip).filter(Boolean)));
})
.catch((t) => {
!B &&
e === L &&
(F.value =
t instanceof Error ? t.message : `IPv4 加载失败`);
}),
...(cloud_instance_management_i(O.module)
? [
n(`ipv6`)
.then((e) => {
e && (N.value = e);
})
.catch((t) => {
!B &&
e === L &&
(I.value =
t instanceof Error ? t.message : `IPv6 加载失败`);
}),
]
: []),
]),
!B && e === L && (P.value = !1));
}
let H = ref(!1),
U = ref(!1),
W = ref(``),
G = reactive({
ip_num: 1,
ipv6_num: 0,
}),
K = ref(``),
q = ref(!1),
J = ref(``),
// 数量、权限、实例和弹窗共同构成取价快照,任何一项变化都必须重新报价。
Y = computed(() =>
JSON.stringify([
O.hostId,
O.module,
O.upgradeAllowed,
H.value,
G,
]),
),
// IPv4/IPv6 可分别为 0,但总数必须大于 0;不能提交负数或非整数数量。
X = computed(
() =>
Number.isInteger(G.ip_num) &&
G.ip_num >= 0 &&
Number.isInteger(G.ipv6_num) &&
G.ipv6_num >= 0 &&
G.ip_num + G.ipv6_num > 0,
),
Z = computed(
() =>
O.upgradeAllowed !== !1 &&
H.value &&
X.value &&
!!J.value &&
J.value === Y.value &&
!q.value &&
!U.value,
);
// 清除报价并递增序号,使较早的取价响应无法恢复过期的可提交状态。
function Q() {
(R++, (K.value = ``), (J.value = ``), (q.value = !1));
}
watch(Y, Q, {
flush: `sync`,
});
// 每次打开恢复为一枚 IPv4,并立即获取对应价格。
async function xe() {
O.upgradeAllowed !== !1 &&
!U.value &&
cloud_instance_management_i(O.module) &&
((G.ip_num = 1),
(G.ipv6_num = 0),
(W.value = ``),
(H.value = !0),
await $());
}
/** 询价只更新与当前数量快照一致的响应,合法的零价格也能通过金额适配器。 */
async function $() {
if ((Q(), O.upgradeAllowed === !1 || !H.value || !X.value)) return;
let e = ++R,
t = Y.value;
((q.value = !0), (W.value = ``));
try {
let n = await A.get(`/${O.module}/${O.hostId}/ip_num`, {
ip_num: G.ip_num,
ipv6_num: G.ipv6_num,
});
if (B || e !== R || t !== Y.value) return;
((K.value = cloud_instance_management_t(n.price)), (J.value = t));
} catch (t) {
!B &&
e === R &&
(W.value = t instanceof Error ? t.message : `询价失败`);
} finally {
!B && e === R && (q.value = !1);
}
}
/** 有有效报价才创建 ip_num/order,校验订单号后关闭弹窗并发 pay;不自动重复下单。 */
async function Se() {
if (!Z.value || B) return;
let e = Y.value,
t = K.value,
n = ++z;
((U.value = !0), (W.value = ``), (j.value = ``));
try {
let n = await A.post(`/${O.module}/${O.hostId}/ip_num/order`, {
ip_num: G.ip_num,
ipv6_num: G.ipv6_num,
});
if (B || e !== Y.value) return;
let r = cloud_instance_management_s(n.id);
((H.value = !1),
k(`pay`, {
order_id: r,
amount: t,
}));
} catch (t) {
!B &&
e === Y.value &&
(W.value = t instanceof Error ? t.message : `下单失败`);
} finally {
!B && n === z && (U.value = !1);
}
}
return (
onMounted(V),
watch(
() => [O.hostId, O.module],
() => {
(z++,
(H.value = !1),
(U.value = !1),
(M.value = []),
(N.value = []),
V());
},
),
// 离开面板后作废地址请求和报价请求,防止回填已销毁界面。
onBeforeUnmount(() => {
((B = !0), L++, Q());
}),
// 两张地址表共享加载态但分别呈现错误;购买弹窗展示当前数量、报价和重报价入口。
(t, n) => {
let r = OperationNotice,
a = button,
c = message,
l = column,
v = datatable,
b = inputnumber,
x = dialog;
return (
openBlock(),
createElementBlock(`div`, ce, [
createVNode(
r,
{
message: unref(j),
severity: `error`,
},
null,
8,
[`message`],
),
createElementVNode(`div`, C, [
createElementVNode(`div`, w, [
(n[4] ||= createTextVNode(`IP 列表`, -1)),
unref(M).length
? (openBlock(),
createElementBlock(
`span`,
T,
`(` + toDisplayString(unref(M).length) + `)`,
1,
))
: createCommentVNode(``, !0),
]),
createElementVNode(`div`, E, [
h.upgradeAllowed === !1
? createCommentVNode(``, !0)
: (openBlock(),
createBlock(a, {
key: 0,
label: `购买附加 IP`,
icon: `pi pi-plus`,
size: `small`,
onClick: xe,
})),
createVNode(
a,
{
label: `刷新地址`,
icon: `pi pi-refresh`,
size: `small`,
text: ``,
loading: unref(P),
onClick: V,
},
null,
8,
[`loading`],
),
]),
]),
unref(F)
? (openBlock(),
createBlock(
c,
{
key: 0,
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(toDisplayString(unref(F)), 1),
]),
_: 1,
},
))
: createCommentVNode(``, !0),
createVNode(
v,
{
class: `network-table ipv4-table`,
value: unref(M),
loading: unref(P),
size: `small`,
scrollable: ``,
},
{
empty: withCtx(() => [
...(n[5] ||= [
createElementVNode(
`div`,
{
class: `empty`,
},
`暂无 IP 信息`,
-1,
),
]),
]),
default: withCtx(() => [
createVNode(
l,
{
field: `ip`,
header: `IP 地址`,
style: {
width: `40%`,
},
},
{
body: withCtx(({ data: t }) => [
createElementVNode(
`span`,
le,
toDisplayString(t.ip),
1,
),
]),
_: 1,
},
),
createVNode(
l,
{
field: `subnet_mask`,
header: `子网掩码`,
style: {
width: `30%`,
},
},
{
body: withCtx(({ data: t }) => [
createElementVNode(
`span`,
ue,
toDisplayString(t.subnet_mask || `—`),
1,
),
]),
_: 1,
},
),
createVNode(
l,
{
field: `gateway`,
header: `网关`,
style: {
width: `30%`,
},
},
{
body: withCtx(({ data: t }) => [
createElementVNode(
`span`,
de,
toDisplayString(t.gateway || `—`),
1,
),
]),
_: 1,
},
),
]),
_: 1,
},
8,
[`value`, `loading`],
),
createElementVNode(`div`, fe, [
createElementVNode(`div`, pe, [
(n[6] ||= createTextVNode(`IPv6 地址`, -1)),
unref(N).length
? (openBlock(),
createElementBlock(
`span`,
me,
`(` + toDisplayString(unref(N).length) + `)`,
1,
))
: createCommentVNode(``, !0),
]),
unref(I)
? (openBlock(),
createBlock(
c,
{
key: 0,
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(toDisplayString(unref(I)), 1),
createVNode(a, {
label: `重试`,
text: ``,
size: `small`,
onClick: V,
}),
]),
_: 1,
},
))
: createCommentVNode(``, !0),
createVNode(
v,
{
class: `network-table ipv6-table`,
"show-headers": !1,
value: unref(N),
loading: unref(P),
size: `small`,
scrollable: ``,
},
{
empty: withCtx(() => [
...(n[7] ||= [
createElementVNode(
`div`,
{
class: `empty`,
},
`暂无 IPv6 地址`,
-1,
),
]),
]),
default: withCtx(() => [
createVNode(
l,
{
field: `ipv6`,
header: `IPv6 地址`,
},
{
body: withCtx(({ data: t }) => [
createElementVNode(
`span`,
he,
toDisplayString(t.ipv6),
1,
),
]),
_: 1,
},
),
]),
_: 1,
},
8,
[`value`, `loading`],
),
]),
h.upgradeAllowed === !1
? createCommentVNode(``, !0)
: (openBlock(),
createBlock(
x,
{
key: 1,
visible: unref(H),
"onUpdate:visible": (n[3] ||= (e) =>
isRef(H) ? (H.value = e) : null),
header: `购买附加 IP`,
modal: ``,
style: {
width: `420px`,
},
draggable: !1,
closable: !unref(U),
"close-on-escape": !unref(U),
},
{
footer: withCtx(() => [
createVNode(
a,
{
label: `取消`,
severity: `secondary`,
text: ``,
disabled: unref(U),
onClick: (n[2] ||= (e) => (H.value = !1)),
},
null,
8,
[`disabled`],
),
createVNode(
a,
{
label: `提交订单`,
loading: unref(U),
disabled: !unref(Z),
onClick: Se,
},
null,
8,
[`loading`, `disabled`],
),
]),
default: withCtx(() => [
createElementVNode(`div`, ge, [
createElementVNode(`div`, _e, [
(n[8] ||= createElementVNode(
`label`,
null,
`IPv4 数量`,
-1,
)),
createVNode(
b,
{
modelValue: unref(G).ip_num,
"onUpdate:modelValue": [
(n[0] ||= (e) => (unref(G).ip_num = e)),
$,
],
min: 0,
disabled: unref(U),
showButtons: ``,
buttonLayout: `horizontal`,
},
null,
8,
[`modelValue`, `disabled`],
),
]),
createElementVNode(`div`, ve, [
(n[9] ||= createElementVNode(
`label`,
null,
`IPv6 数量`,
-1,
)),
createVNode(
b,
{
modelValue: unref(G).ipv6_num,
"onUpdate:modelValue": [
(n[1] ||= (e) => (unref(G).ipv6_num = e)),
$,
],
min: 0,
disabled: unref(U),
showButtons: ``,
buttonLayout: `horizontal`,
},
null,
8,
[`modelValue`, `disabled`],
),
]),
createElementVNode(`div`, ye, [
(n[10] ||= createElementVNode(
`span`,
null,
`应付金额`,
-1,
)),
createElementVNode(`span`, be, [
unref(q)
? (openBlock(), createElementBlock(`i`, D))
: (openBlock(),
createElementBlock(
Fragment,
{
key: 1,
},
[
createTextVNode(
toDisplayString(
unref(K) ? `¥${unref(K)}` : `—`,
),
1,
),
],
64,
)),
]),
]),
unref(W)
? (openBlock(),
createBlock(
c,
{
key: 0,
severity: `error`,
closable: !1,
},
{
default: withCtx(() => [
createTextVNode(
toDisplayString(unref(W)),
1,
),
createVNode(
a,
{
label: `重新报价`,
text: ``,
size: `small`,
disabled: unref(U),
onClick: $,
},
null,
8,
[`disabled`],
),
]),
_: 1,
},
))
: createCommentVNode(``, !0),
]),
]),
_: 1,
},
8,
[`visible`, `closable`, `close-on-escape`],
)),
])
);
}
);
},
}),
[[`__scopeId`, `data-v-9ba8a1b3`]],
),
{
__name: `MfCloudIps`,
},
);
export {};
export default O;
</script>