You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

128 lines
10 KiB

import { defineComponent, ref, reactive, computed, unref, useSSRContext } from 'vue';
import { ssrRenderAttrs, ssrRenderList, ssrRenderClass, ssrInterpolate, ssrIncludeBooleanAttr, ssrLooseContain, ssrLooseEqual, ssrRenderAttr } from 'vue/server-renderer';
import { u as useApi } from './useApi-BNSkSrat.mjs';
import { u as useToast } from './useToast-MBvLaNjH.mjs';
import { r as regionLabel } from './regions-D1dSykUW.mjs';
import { _ as _export_sfc, b as useRouter } from './server.mjs';
import '../nitro/nitro.mjs';
import 'node:http';
import 'node:https';
import 'node:events';
import 'node:buffer';
import 'node:fs';
import 'node:path';
import 'node:crypto';
import 'node:url';
import '../routes/renderer.mjs';
import 'vue-bundle-renderer/runtime';
import 'unhead/server';
import 'devalue';
import 'unhead/utils';
import 'pinia';
import 'perfect-debounce';
const _sfc_main = /* @__PURE__ */ defineComponent({
__name: "index",
__ssrInlineRender: true,
setup(__props) {
useApi();
useToast();
useRouter();
const products = ref([]);
const deployments = ref([]);
const loading = ref(false);
const assignTo = reactive({});
const byApp = computed(() => {
const m = /* @__PURE__ */ new Map();
for (const d of deployments.value) {
const k = d.app_name || "";
if (!m.has(k)) m.set(k, []);
m.get(k).push(d);
}
return m;
});
const productGroups = computed(
() => products.value.map((p) => {
const deps = byApp.value.get(p.name) || [];
return {
product: p,
deployments: deps,
okCount: deps.filter((d) => d.status !== "pending").length,
pendingCount: deps.filter((d) => d.status === "pending").length
};
})
);
const orphans = computed(() => {
const known = new Set(products.value.map((p) => p.name));
return deployments.value.filter((d) => !d.app_name || !known.has(d.app_name));
});
const groups = computed(() => productGroups.value.length + (orphans.value.length ? 1 : 0));
function initial(name) {
return (name || "?").charAt(0).toUpperCase();
}
ref(null);
const pEditing = ref(false);
const pForm = reactive({ id: 0, name: "", image_name: "", remark: "", enabled: true });
return (_ctx, _push, _parent, _attrs) => {
_push(`<div${ssrRenderAttrs(_attrs)} data-v-80f99751><div class="flex items-start justify-between mb-5 flex-wrap gap-3" data-v-80f99751><div data-v-80f99751><h2 class="text-lg font-semibold text-slate-800" data-v-80f99751>应用中心</h2><p class="text-sm text-slate-400 mt-1 max-w-3xl" data-v-80f99751> 一张卡片 = 一个 <span class="chip chip-app" data-v-80f99751>应用</span>,卡片里是它在各区域的 <span class="chip chip-dep" data-v-80f99751>部署</span> 概况。 点卡片进入应用详情:编辑应用、部署新环境、升级服务、改各项配置。 </p></div><button class="btn btn-primary btn-sm text-white" data-v-80f99751>+ 添加应用</button></div>`);
if (loading.value) {
_push(`<div class="bg-white rounded-xl border border-slate-100 py-16 text-center" data-v-80f99751><span class="loading loading-spinner loading-sm" data-v-80f99751></span></div>`);
} else if (!groups.value) {
_push(`<div class="bg-white rounded-xl border border-slate-100 py-16 text-center text-slate-400" data-v-80f99751> 暂无应用,先「添加应用」 </div>`);
} else {
_push(`<div class="app-grid" data-v-80f99751><!--[-->`);
ssrRenderList(productGroups.value, (g) => {
_push(`<article class="${ssrRenderClass([{ off: !g.product.enabled }, "app-card"])}" tabindex="0" role="button" data-v-80f99751><div class="ac-head" data-v-80f99751><div class="ac-logo" data-v-80f99751>${ssrInterpolate(initial(g.product.name))}</div><div class="min-w-0 flex-1" data-v-80f99751><div class="ac-title" data-v-80f99751>${ssrInterpolate(g.product.name)}</div><div class="ac-meta" data-v-80f99751>${ssrInterpolate(g.product.image_name ? "🐳 " + g.product.image_name : "未设置默认镜像")}</div></div>`);
if (!g.product.enabled) {
_push(`<span class="chip" data-v-80f99751>停用</span>`);
} else {
_push(`<!---->`);
}
_push(`</div><p class="ac-remark" data-v-80f99751>${ssrInterpolate(g.product.remark || "—")}</p><div class="ac-stats" data-v-80f99751><div class="ac-stat" data-v-80f99751><div class="${ssrRenderClass([{ zero: !g.deployments.length }, "ac-stat-n"])}" data-v-80f99751>${ssrInterpolate(g.deployments.length)}</div><div class="ac-stat-l" data-v-80f99751>部署</div></div><div class="ac-stat" data-v-80f99751><div class="${ssrRenderClass([{ zero: !g.okCount }, "ac-stat-n"])}" data-v-80f99751>${ssrInterpolate(g.okCount)}</div><div class="ac-stat-l" data-v-80f99751>已接入</div></div><div class="ac-stat" data-v-80f99751><div class="${ssrRenderClass([g.pendingCount ? "warn" : "zero", "ac-stat-n"])}" data-v-80f99751>${ssrInterpolate(g.pendingCount)}</div><div class="ac-stat-l" data-v-80f99751>待部署</div></div></div><div class="ac-regions" data-v-80f99751>`);
if (g.deployments.length) {
_push(`<!--[-->`);
ssrRenderList(g.deployments, (d) => {
_push(`<span class="chip chip-dep" data-v-80f99751>${ssrInterpolate(unref(regionLabel)(d.region))}`);
if (d.status === "pending") {
_push(`<!--[--> · 待部署<!--]-->`);
} else {
_push(`<!---->`);
}
_push(`</span>`);
});
_push(`<!--]-->`);
} else {
_push(`<span class="text-xs text-slate-300" data-v-80f99751>还没有任何部署</span>`);
}
_push(`</div><div class="ac-foot" data-v-80f99751><button class="btn btn-ghost btn-xs" data-v-80f99751>编辑</button><button class="btn btn-ghost btn-xs text-red-500" data-v-80f99751>删除</button><button class="btn btn-primary btn-xs text-white" data-v-80f99751>进入详情 →</button></div></article>`);
});
_push(`<!--]-->`);
if (orphans.value.length) {
_push(`<article class="app-card orphan" data-v-80f99751><div class="ac-head" data-v-80f99751><div class="ac-logo" data-v-80f99751>?</div><div class="min-w-0 flex-1" data-v-80f99751><div class="ac-title" data-v-80f99751>未分组部署</div><div class="ac-meta" data-v-80f99751>需要归位到某个应用</div></div><span class="chip chip-warn" data-v-80f99751>待归位</span></div><p class="ac-remark" data-v-80f99751>未填所属应用名、或应用已被删除的历史部署。选一个应用把它归位,之后就能在该应用详情里管理。</p><div class="orphan-list" data-v-80f99751><!--[-->`);
ssrRenderList(orphans.value, (d) => {
_push(`<div class="orphan-row" data-v-80f99751><div class="min-w-0" data-v-80f99751><div class="text-sm font-medium text-slate-700 truncate" data-v-80f99751>${ssrInterpolate(d.name || "部署#" + d.id)}</div><div class="text-[11px] text-slate-400 font-mono" data-v-80f99751>#${ssrInterpolate(d.id)} · ${ssrInterpolate(unref(regionLabel)(d.region))}</div></div><select class="select select-bordered select-xs w-32" data-v-80f99751><option value="" data-v-80f99751${ssrIncludeBooleanAttr(Array.isArray(assignTo[d.id]) ? ssrLooseContain(assignTo[d.id], "") : ssrLooseEqual(assignTo[d.id], "")) ? " selected" : ""}>选择应用…</option><!--[-->`);
ssrRenderList(products.value, (p) => {
_push(`<option${ssrRenderAttr("value", p.name)} data-v-80f99751${ssrIncludeBooleanAttr(Array.isArray(assignTo[d.id]) ? ssrLooseContain(assignTo[d.id], p.name) : ssrLooseEqual(assignTo[d.id], p.name)) ? " selected" : ""}>${ssrInterpolate(p.name)}</option>`);
});
_push(`<!--]--></select><button class="btn btn-xs"${ssrIncludeBooleanAttr(!assignTo[d.id]) ? " disabled" : ""} data-v-80f99751>归位</button></div>`);
});
_push(`<!--]--></div></article>`);
} else {
_push(`<!---->`);
}
_push(`</div>`);
}
_push(`<dialog class="modal" data-v-80f99751><div class="modal-box max-w-lg" data-v-80f99751><h3 class="font-bold text-lg" data-v-80f99751>${ssrInterpolate(pEditing.value ? "编辑应用" : "添加应用")}</h3><p class="text-xs text-slate-400 mt-1" data-v-80f99751> 应用名是关联键:部署、账号作用域、第三方服务配置都按它挂载,改名会断开已有关联。 </p><form class="mt-4 space-y-3" data-v-80f99751><div class="form-control" data-v-80f99751><label class="label py-1" data-v-80f99751><span class="label-text text-sm font-medium" data-v-80f99751>应用名 *</span></label><input${ssrRenderAttr("value", pForm.name)} type="text" class="input input-bordered input-sm w-full" required placeholder="如 Voitrans" data-v-80f99751></div><div class="form-control" data-v-80f99751><label class="label py-1" data-v-80f99751><span class="label-text text-sm font-medium" data-v-80f99751>默认镜像名</span></label><input${ssrRenderAttr("value", pForm.image_name)} type="text" class="input input-bordered input-sm w-full font-mono text-xs" placeholder="voitrans-a11(新建部署时预填)" data-v-80f99751></div><div class="form-control" data-v-80f99751><label class="label py-1" data-v-80f99751><span class="label-text text-sm font-medium" data-v-80f99751>备注</span></label><input${ssrRenderAttr("value", pForm.remark)} type="text" class="input input-bordered input-sm w-full" data-v-80f99751></div><label class="flex items-center gap-2 cursor-pointer pt-1" data-v-80f99751><input${ssrIncludeBooleanAttr(Array.isArray(pForm.enabled) ? ssrLooseContain(pForm.enabled, null) : pForm.enabled) ? " checked" : ""} type="checkbox" class="toggle toggle-primary toggle-sm" data-v-80f99751><span class="text-sm" data-v-80f99751>启用</span></label><div class="modal-action" data-v-80f99751><button type="button" class="btn btn-ghost btn-sm" data-v-80f99751>取消</button><button type="submit" class="btn btn-primary btn-sm text-white" data-v-80f99751>保存</button></div></form></div><form method="dialog" class="modal-backdrop" data-v-80f99751><button data-v-80f99751>close</button></form></dialog></div>`);
};
}
});
const _sfc_setup = _sfc_main.setup;
_sfc_main.setup = (props, ctx) => {
const ssrContext = useSSRContext();
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("pages/apps/index.vue");
return _sfc_setup ? _sfc_setup(props, ctx) : void 0;
};
const index = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-80f99751"]]);
export { index as default };
//# sourceMappingURL=index-Cw3MwhTM.mjs.map