512 lines
19 KiB
Vue
512 lines
19 KiB
Vue
<script>
|
|
/**
|
|
* 云服务器订购:先筛出云服务分类,再按分页商品选择对应模块配置器;分类和商品详情各用独立代次处理快速切换。
|
|
*/
|
|
import "~/assets/styles/MfCloudConfig.DbgUu8qr.css";
|
|
import "~/assets/styles/KkyEcsConfig.BFaAwQPZ.css";
|
|
import "~/assets/styles/ConfigResourceOption.Dj4VopnA.css";
|
|
import "~/assets/styles/ConfigDiskAdd.C92e9ZuF.css";
|
|
import "~/assets/styles/ConfigBandwidth.CWSH1WJZ.css";
|
|
import "~/assets/styles/JyCloudConfig.kGfttaHi.css";
|
|
import "~/assets/styles/FinanceConfig.CLwX1CC8.css";
|
|
import "~/assets/styles/ConfigDiskCard.Dx9IqqQ_.css";
|
|
import "~/assets/styles/PromoCodeField.qFEkB6Hm.css";
|
|
import "~/assets/styles/ProductCustomFields.B2QMnPil.css";
|
|
import "~/assets/styles/OsIcon.CAi4Iz2S.css";
|
|
import "~/assets/styles/CommonConfig.Cfik-oMe.css";
|
|
import "~/assets/styles/CloudProductPicker.CAm9rmUl.css";
|
|
import "~/assets/styles/ProductGroupLabel.B1kWpFA-.css";
|
|
import "~/assets/styles/PaymentDialog.BNFKQF1U.css";
|
|
import "~/assets/styles/cloud-server.CcwlOXJ0.css";
|
|
definePageMeta({
|
|
key: (e) => e.fullPath,
|
|
alias: [`/products/cvm`],
|
|
});
|
|
import { toDisplayString } from "vue";
|
|
import { onMounted } from "vue";
|
|
import { resolveDynamicComponent } from "vue";
|
|
import { onScopeDispose } from "vue";
|
|
import { createBlock } from "vue";
|
|
import { ref } from "vue";
|
|
import { withCtx } from "vue";
|
|
import { createCommentVNode } from "vue";
|
|
import { mergeProps } from "vue";
|
|
import { computed } from "vue";
|
|
import { isRef } from "vue";
|
|
import { createElementBlock } from "vue";
|
|
import { toHandlers } 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 { useRoute } from "#app";
|
|
import { useRouter } from "#app";
|
|
import { useHead } from "#app";
|
|
import { useApi } from "~/lib/auth.js";
|
|
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 PaymentDialog from "~/components/PaymentDialog.vue";
|
|
import { t as product_order_module_t } from "~/lib/product-order-module.js";
|
|
import { n as ProductGroupLabel_n } from "~/components/ProductGroupLabel.vue";
|
|
import CloudProductPicker from "~/components/config/CloudProductPicker.vue";
|
|
import ConfigLoadingShell from "~/components/config/ConfigLoadingShell.vue";
|
|
import CommonConfig from "~/components/config/CommonConfig.vue";
|
|
import FinanceConfig from "~/components/config/FinanceConfig.vue";
|
|
import JyCloudConfig from "~/components/config/JyCloudConfig.vue";
|
|
import KkyEcsConfig from "~/components/config/KkyEcsConfig.vue";
|
|
import MfCloudConfig from "~/components/config/MfCloudConfig.vue";
|
|
import { t as composables_t } from "~/lib/composables-2.js";
|
|
import { t as product_description_data_t } from "~/lib/product-description-data.js";
|
|
import { productId } from "~/lib/product-id.js";
|
|
var ge = {
|
|
class: `cloud-order-page`,
|
|
},
|
|
_e = {
|
|
key: 0,
|
|
class: `product-switch-status`,
|
|
role: `status`,
|
|
},
|
|
S = _plugin_vue_export_helper_t(
|
|
defineComponent({
|
|
__name: `cloud-server`,
|
|
setup(m) {
|
|
let y = new Set([
|
|
`云服务器`,
|
|
`轻量云服务器`,
|
|
`国内云服务器`,
|
|
`香港云服务器`,
|
|
`弹性云服务器`,
|
|
]);
|
|
(composables_t({
|
|
alias: `/products/cvm`,
|
|
key: (e) => e.fullPath,
|
|
}),
|
|
useHead({
|
|
title: `创建云服务器实例 - 破碎工坊网络`,
|
|
}));
|
|
let S = useApi(),
|
|
C = useRoute(),
|
|
ve = useRouter(),
|
|
w = ref([]),
|
|
T = ref(null),
|
|
E = ref([]),
|
|
D = ref(null),
|
|
O = ref(null),
|
|
k = ref(!0),
|
|
A = ref(!1),
|
|
j = ref(!1),
|
|
M = ref(``),
|
|
N = ref(`groups`),
|
|
P = ref(``),
|
|
F = ref(0),
|
|
ye = computed(() => j.value || !!P.value || O.value !== D.value?.id),
|
|
I = ref(0),
|
|
L = ref(1),
|
|
R = 0,
|
|
z = 0;
|
|
onScopeDispose(() => {
|
|
(R++, z++);
|
|
});
|
|
let be = {
|
|
mf_cloud: MfCloudConfig,
|
|
remf_cloud: MfCloudConfig,
|
|
jy_cloud: JyCloudConfig,
|
|
jy_dcim: JyCloudConfig,
|
|
kky_ecs: KkyEcsConfig,
|
|
idcsmart_common: CommonConfig,
|
|
remf_finance: FinanceConfig,
|
|
},
|
|
B = computed(() => be[D.value?.module ?? ``]),
|
|
V = computed(() =>
|
|
D.value?.module === `mf_cloud` || D.value?.module === `remf_cloud`
|
|
? D.value.module
|
|
: void 0,
|
|
),
|
|
H = computed(() =>
|
|
D.value?.module === `jy_cloud` || D.value?.module === `jy_dcim`
|
|
? D.value.module
|
|
: void 0,
|
|
),
|
|
U = (e) => Number(e.stock_control) === 1 && !(Number(e.qty) > 0),
|
|
W = (e) =>
|
|
e.qty == null || !Number.isFinite(Number(e.qty))
|
|
? `库存待确认`
|
|
: `暂时缺货`,
|
|
G = (e) =>
|
|
productId(e.id) !== null &&
|
|
Number(e.hidden ?? 0) !== 1 &&
|
|
(e.product_group_id_second == null ||
|
|
Number(e.product_group_id_second) === T.value);
|
|
// 只选择当前分组内且有库存的商品;读取详情后再次核验分组/库存并解析可用模块,再挂载配置器。
|
|
async function K(e) {
|
|
let t = E.value.find((t) => t.id === e);
|
|
if (
|
|
!t ||
|
|
!G(t) ||
|
|
U(t) ||
|
|
(O.value === e && (j.value || (D.value?.id === e && !P.value)))
|
|
)
|
|
return;
|
|
let n = ++z;
|
|
((O.value = e), (P.value = ``), (j.value = !0));
|
|
try {
|
|
let r = await S.get(`/product/${e}`);
|
|
if (n !== z) return;
|
|
let i = r?.product ?? r;
|
|
if (!i?.id || Number(i.id) !== e) throw Error(`商品不存在或已下架`);
|
|
let a = {
|
|
...t,
|
|
...i,
|
|
};
|
|
if (
|
|
(typeof i.description != `string` &&
|
|
typeof t.description == `string` &&
|
|
(a.description = t.description),
|
|
!G(a))
|
|
)
|
|
throw Error(`商品不存在或已下架`);
|
|
if (U(a)) throw Error(`当前商品${W(a)},请稍后重试`);
|
|
let o = await product_order_module_t(S, a);
|
|
if (n !== z) return;
|
|
(F.value++,
|
|
(D.value = {
|
|
...a,
|
|
module: o,
|
|
}),
|
|
product_description_data_t(S, a).then((e) => {
|
|
n === z && D.value && (D.value.description = e);
|
|
}));
|
|
} catch (e) {
|
|
n === z &&
|
|
(P.value = e instanceof Error ? e.message : `商品配置加载失败`);
|
|
} finally {
|
|
n === z && (j.value = !1);
|
|
}
|
|
}
|
|
// 切换分组或分页时清空旧配置,加载商品后优先选择查询指定或首个可售商品。
|
|
async function q() {
|
|
let e = ++R;
|
|
(z++,
|
|
(E.value = []),
|
|
(D.value = null),
|
|
(O.value = null),
|
|
(j.value = !1),
|
|
(P.value = ``),
|
|
(I.value = 0),
|
|
(M.value = ``),
|
|
(N.value = `products`),
|
|
(A.value = !0));
|
|
try {
|
|
if (!T.value || !w.value.some((e) => e.id === T.value)) return;
|
|
let t = await S.get(`/product`, {
|
|
id: T.value,
|
|
page: L.value,
|
|
limit: 24,
|
|
});
|
|
if (e !== R) return;
|
|
((E.value = (t.list ?? []).filter(G)),
|
|
(I.value = Number(t.count) || 0),
|
|
(A.value = !1));
|
|
let n = productId(C.query.id ?? C.query.product_id),
|
|
r =
|
|
E.value.find((e) => e.id === n && !U(e)) ??
|
|
E.value.find((e) => !U(e));
|
|
r && (await K(r.id));
|
|
} catch (t) {
|
|
e === R &&
|
|
(M.value = t instanceof Error ? t.message : `产品加载失败`);
|
|
} finally {
|
|
e === R && (A.value = !1);
|
|
}
|
|
}
|
|
// 重新读取两级分类,并按标准化的云产品分类名筛选可用线路。
|
|
async function J() {
|
|
let e = ++R;
|
|
(z++,
|
|
(k.value = !0),
|
|
(M.value = ``),
|
|
(N.value = `groups`),
|
|
(P.value = ``),
|
|
(w.value = []),
|
|
(T.value = null),
|
|
(E.value = []),
|
|
(D.value = null),
|
|
(O.value = null),
|
|
(I.value = 0),
|
|
(L.value = 1),
|
|
(j.value = !1),
|
|
(A.value = !1));
|
|
try {
|
|
// 显式指定的商品编号无效时就地提示,不改选首件商品,更不把超限值交给后端。
|
|
const requestedId = C.query.id ?? C.query.product_id;
|
|
if (requestedId !== undefined && productId(requestedId) === null)
|
|
throw Error(`商品编号无效`);
|
|
let [t, n] = await Promise.all([
|
|
S.get(`/product/group/first`),
|
|
S.get(`/product/group/second`),
|
|
]);
|
|
if (e !== R) return;
|
|
let r = new Set(
|
|
(t.list ?? [])
|
|
.filter((e) => y.has(ProductGroupLabel_n(e.name).name))
|
|
.map((e) => Number(e.id)),
|
|
);
|
|
((w.value = (n.list ?? []).filter((e) =>
|
|
r.has(Number(e.parent_id)),
|
|
)),
|
|
(T.value = w.value[0]?.id ?? null),
|
|
(k.value = !1),
|
|
await q());
|
|
} catch (t) {
|
|
e === R &&
|
|
(M.value = t instanceof Error ? t.message : `线路加载失败`);
|
|
} finally {
|
|
e === R && (k.value = !1);
|
|
}
|
|
}
|
|
// 仅允许选择当前分类列表中存在的分组,并从第一页重新加载。
|
|
function xe(e) {
|
|
T.value !== e &&
|
|
w.value.some((t) => t.id === e) &&
|
|
((T.value = e), (L.value = 1), q());
|
|
}
|
|
// 商品分页变化时重载列表,旧配置器随之失效。
|
|
function Se(e) {
|
|
((L.value = e.page + 1), q());
|
|
}
|
|
let Y = ref(!1),
|
|
X = ref(null),
|
|
Z = ref(``);
|
|
// 配置器下单后记录返回订单并打开支付。
|
|
function Ce(e) {
|
|
((X.value = e.order_id), (Z.value = e.amount), (Y.value = !0));
|
|
}
|
|
// 支付完成进入该订单的结果页,真实付款状态仍由服务端返回。
|
|
function we() {
|
|
((Y.value = !1),
|
|
X.value && ve.push(`/orders/order/paid?id=${X.value}`));
|
|
}
|
|
let Q = computed(() => ({
|
|
groups: w.value,
|
|
activeGroup: T.value,
|
|
products: E.value,
|
|
selectedId: O.value,
|
|
loading: k.value,
|
|
productLoading: A.value,
|
|
error: M.value,
|
|
errorSource: N.value,
|
|
total: I.value,
|
|
page: L.value,
|
|
soldOut: U,
|
|
stockLabel: W,
|
|
})),
|
|
$ = {
|
|
"choose-group": xe,
|
|
"choose-product": K,
|
|
"retry-groups": J,
|
|
"retry-products": q,
|
|
paginate: Se,
|
|
};
|
|
return (
|
|
onMounted(J),
|
|
// 选择器与模块配置器使用同一商品快照;快速切换时由版本和组件 key 阻止旧配置继续提交。
|
|
(t, r) => {
|
|
let a = CloudProductPicker,
|
|
l = button,
|
|
m = message,
|
|
_ = ConfigLoadingShell,
|
|
v = PaymentDialog;
|
|
return (
|
|
openBlock(),
|
|
createElementBlock(`div`, ge, [
|
|
(r[4] ||= createElementVNode(
|
|
`h2`,
|
|
null,
|
|
`创建云服务器实例`,
|
|
-1,
|
|
)),
|
|
unref(D) && unref(B)
|
|
? (openBlock(),
|
|
createBlock(
|
|
resolveDynamicComponent(unref(B)),
|
|
mergeProps(
|
|
{
|
|
key: `${unref(D).id}-${unref(F)}`,
|
|
"product-id": unref(D).id,
|
|
"identity-locked": unref(ye),
|
|
},
|
|
unref(V)
|
|
? {
|
|
module: unref(V),
|
|
initialProduct: unref(D),
|
|
}
|
|
: unref(H)
|
|
? {
|
|
module: unref(H),
|
|
}
|
|
: {},
|
|
{
|
|
onSettled: Ce,
|
|
},
|
|
),
|
|
{
|
|
catalog: withCtx(({ disabled: t }) => [
|
|
createVNode(
|
|
a,
|
|
mergeProps(
|
|
unref(Q),
|
|
{
|
|
disabled: t,
|
|
},
|
|
toHandlers($),
|
|
),
|
|
null,
|
|
16,
|
|
[`disabled`],
|
|
),
|
|
unref(j)
|
|
? (openBlock(),
|
|
createElementBlock(
|
|
`p`,
|
|
_e,
|
|
`正在加载所选商品,原配置暂不可操作。`,
|
|
))
|
|
: unref(P)
|
|
? (openBlock(),
|
|
createBlock(
|
|
m,
|
|
{
|
|
key: 1,
|
|
severity: `error`,
|
|
closable: !1,
|
|
},
|
|
{
|
|
default: withCtx(() => [
|
|
createTextVNode(
|
|
toDisplayString(unref(P)),
|
|
1,
|
|
),
|
|
unref(O)
|
|
? (openBlock(),
|
|
createBlock(l, {
|
|
key: 0,
|
|
label: `重新加载配置`,
|
|
severity: `secondary`,
|
|
text: ``,
|
|
onClick: (r[0] ||= (e) =>
|
|
K(unref(O))),
|
|
}))
|
|
: createCommentVNode(``, !0),
|
|
]),
|
|
_: 1,
|
|
},
|
|
))
|
|
: createCommentVNode(``, !0),
|
|
]),
|
|
_: 1,
|
|
},
|
|
16,
|
|
[`product-id`, `identity-locked`],
|
|
))
|
|
: (openBlock(),
|
|
createBlock(
|
|
_,
|
|
{
|
|
key: 1,
|
|
busy: unref(k) || unref(A) || unref(j),
|
|
},
|
|
{
|
|
catalog: withCtx(() => [
|
|
createVNode(
|
|
a,
|
|
mergeProps(unref(Q), toHandlers($)),
|
|
null,
|
|
16,
|
|
),
|
|
unref(P)
|
|
? (openBlock(),
|
|
createBlock(
|
|
m,
|
|
{
|
|
key: 0,
|
|
severity: `error`,
|
|
closable: !1,
|
|
},
|
|
{
|
|
default: withCtx(() => [
|
|
createTextVNode(
|
|
toDisplayString(unref(P)),
|
|
1,
|
|
),
|
|
unref(O)
|
|
? (openBlock(),
|
|
createBlock(l, {
|
|
key: 0,
|
|
label: `重新加载配置`,
|
|
severity: `secondary`,
|
|
text: ``,
|
|
onClick: (r[1] ||= (e) =>
|
|
K(unref(O))),
|
|
}))
|
|
: createCommentVNode(``, !0),
|
|
]),
|
|
_: 1,
|
|
},
|
|
))
|
|
: unref(D)
|
|
? (openBlock(),
|
|
createBlock(
|
|
m,
|
|
{
|
|
key: 1,
|
|
severity: `warn`,
|
|
closable: !1,
|
|
},
|
|
{
|
|
default: withCtx(() => [
|
|
...(r[3] ||= [
|
|
createTextVNode(
|
|
`当前商品暂不支持在线配置,请通过工单咨询。`,
|
|
-1,
|
|
),
|
|
]),
|
|
]),
|
|
_: 1,
|
|
},
|
|
))
|
|
: createCommentVNode(``, !0),
|
|
]),
|
|
_: 1,
|
|
},
|
|
8,
|
|
[`busy`],
|
|
)),
|
|
createVNode(
|
|
v,
|
|
{
|
|
modelValue: unref(Y),
|
|
"onUpdate:modelValue": (r[2] ||= (e) =>
|
|
isRef(Y) ? (Y.value = e) : null),
|
|
"order-id": unref(X),
|
|
amount: unref(Z),
|
|
onSuccess: we,
|
|
},
|
|
null,
|
|
8,
|
|
[`modelValue`, `order-id`, `amount`],
|
|
),
|
|
])
|
|
);
|
|
}
|
|
);
|
|
},
|
|
}),
|
|
[[`__scopeId`, `data-v-ef3931f0`]],
|
|
);
|
|
export {};
|
|
export default S;
|
|
</script>
|