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.
 
 
 
 
 
 

140 lines
15 KiB

import { defineComponent, ref, reactive, useSSRContext } from 'vue';
import { ssrRenderAttrs, ssrRenderStyle, ssrRenderList, ssrInterpolate, ssrRenderAttr, ssrIncludeBooleanAttr, ssrLooseContain, ssrLooseEqual, ssrRenderClass } from 'vue/server-renderer';
import { u as useApi } from './useApi-B7CB0Cnn.mjs';
import { u as useToast } from './useToast-BwtTR9dH.mjs';
import { u as useAuthStore } from './auth-QFkCoU1C.mjs';
import { a as useRouter } from './server.mjs';
import 'pinia';
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 'perfect-debounce';
const _sfc_main = /* @__PURE__ */ defineComponent({
__name: "apps",
__ssrInlineRender: true,
setup(__props) {
useApi();
useToast();
useAuthStore();
useRouter();
const view = ref("list");
const rows = ref([]);
const loading = ref(false);
const isEditing = ref(false);
const testPassed = ref(false);
const testing = ref(false);
const testResult = reactive({
show: false,
apiOk: false,
apiMsg: "",
dbOk: false,
dbMsg: ""
});
const form = reactive({
id: 0,
name: "",
region: "",
base_url: "",
service_dsn: "",
remark: "",
enabled: true
});
function dbName(dsn) {
const m = (dsn || "").match(/\/([^/?]+)\?/);
return m ? m[1] : "-";
}
ref(null);
const az = reactive({
appId: 0,
appLabel: "",
start: "",
end: "",
resultShow: false,
resultClass: "",
mode: "",
// 进度
steps: [],
curIndex: 0,
curTotal: 0,
curName: "",
pct: 0,
// 结果
errMsg: "",
report: null,
running: false,
btnText: "开始分析"
});
return (_ctx, _push, _parent, _attrs) => {
_push(`<div${ssrRenderAttrs(_attrs)}><div style="${ssrRenderStyle(view.value === "list" ? null : { display: "none" })}"><div class="flex items-center justify-between mb-5"><div><h2 class="text-lg font-semibold text-slate-800">应用注册</h2><p class="text-sm text-slate-400 mt-1">注册各应用的地址与业务库 DSN(仅「重置设备码」清用户绑定时用);厂家/产品/设备码统一在 console 主库,无需逐应用配。</p></div><button class="btn btn-primary btn-sm text-white">+ 注册应用</button></div><div class="bg-white rounded-xl shadow-sm border border-slate-100"><div class="overflow-x-auto"><table class="table table-zebra text-sm"><thead><tr><th>ID</th><th>名称</th><th>区域</th><th>应用地址</th><th>业务库</th><th>启用</th><th>操作</th></tr></thead><tbody>`);
if (loading.value) {
_push(`<tr><td colspan="7" class="text-center py-8"><span class="loading loading-spinner loading-sm"></span></td></tr>`);
} else if (!rows.value.length) {
_push(`<tr><td colspan="7" class="text-center text-slate-400 py-8">暂无注册应用</td></tr>`);
} else {
_push(`<!---->`);
}
_push(`<!--[-->`);
ssrRenderList(rows.value, (a) => {
_push(`<tr><td class="font-mono">${ssrInterpolate(a.id)}</td><td class="font-medium">${ssrInterpolate(a.name || "")}</td><td>${ssrInterpolate(a.region || "")}</td><td class="text-xs text-slate-500">${ssrInterpolate(a.base_url || "")}</td><td class="font-mono text-xs">${ssrInterpolate(dbName(a.service_dsn))}</td><td>`);
if (a.enabled) {
_push(`<span class="text-green-600">是</span>`);
} else {
_push(`<span class="text-slate-400">否</span>`);
}
_push(`</td><td><div class="flex gap-1"><button class="btn btn-ghost btn-xs">编辑</button><button class="btn btn-ghost btn-xs text-emerald-600">分析历史</button><button class="btn btn-ghost btn-xs text-indigo-500">发事件</button><button class="btn btn-ghost btn-xs text-red-500">删除</button></div></td></tr>`);
});
_push(`<!--]--></tbody></table></div></div></div><div style="${ssrRenderStyle(view.value === "form" ? null : { display: "none" })}"><div class="flex items-center gap-3 mb-5"><button class="btn btn-ghost btn-sm">← 返回</button><h2 class="text-lg font-semibold text-slate-800">${ssrInterpolate(isEditing.value ? "编辑应用" : "注册应用")}</h2></div><div class="bg-white rounded-xl shadow-sm border border-slate-100 p-6 max-w-3xl"><form><div class="grid grid-cols-2 gap-4"><div class="form-control"><label class="label"><span class="label-text font-medium">名称 *</span></label><input${ssrRenderAttr("value", form.name)} type="text" class="input input-bordered w-full" required placeholder="如 Voitrans(国内)"></div><div class="form-control"><label class="label"><span class="label-text font-medium">区域</span></label><select class="select select-bordered w-full"><option value=""${ssrIncludeBooleanAttr(Array.isArray(form.region) ? ssrLooseContain(form.region, "") : ssrLooseEqual(form.region, "")) ? " selected" : ""}>未指定</option><option value="中国"${ssrIncludeBooleanAttr(Array.isArray(form.region) ? ssrLooseContain(form.region, "中国") : ssrLooseEqual(form.region, "中国")) ? " selected" : ""}>中国</option><option value="美国"${ssrIncludeBooleanAttr(Array.isArray(form.region) ? ssrLooseContain(form.region, "美国") : ssrLooseEqual(form.region, "美国")) ? " selected" : ""}>美国</option><option value="东亚"${ssrIncludeBooleanAttr(Array.isArray(form.region) ? ssrLooseContain(form.region, "东亚") : ssrLooseEqual(form.region, "东亚")) ? " selected" : ""}>东亚</option><option value="东南亚"${ssrIncludeBooleanAttr(Array.isArray(form.region) ? ssrLooseContain(form.region, "东南亚") : ssrLooseEqual(form.region, "东南亚")) ? " selected" : ""}>东南亚</option><option value="南亚"${ssrIncludeBooleanAttr(Array.isArray(form.region) ? ssrLooseContain(form.region, "南亚") : ssrLooseEqual(form.region, "南亚")) ? " selected" : ""}>南亚</option><option value="中东"${ssrIncludeBooleanAttr(Array.isArray(form.region) ? ssrLooseContain(form.region, "中东") : ssrLooseEqual(form.region, "中东")) ? " selected" : ""}>中东</option><option value="欧洲"${ssrIncludeBooleanAttr(Array.isArray(form.region) ? ssrLooseContain(form.region, "欧洲") : ssrLooseEqual(form.region, "欧洲")) ? " selected" : ""}>欧洲</option><option value="俄罗斯"${ssrIncludeBooleanAttr(Array.isArray(form.region) ? ssrLooseContain(form.region, "俄罗斯") : ssrLooseEqual(form.region, "俄罗斯")) ? " selected" : ""}>俄罗斯</option><option value="非洲"${ssrIncludeBooleanAttr(Array.isArray(form.region) ? ssrLooseContain(form.region, "非洲") : ssrLooseEqual(form.region, "非洲")) ? " selected" : ""}>非洲</option><option value="南美"${ssrIncludeBooleanAttr(Array.isArray(form.region) ? ssrLooseContain(form.region, "南美") : ssrLooseEqual(form.region, "南美")) ? " selected" : ""}>南美</option><option value="北美"${ssrIncludeBooleanAttr(Array.isArray(form.region) ? ssrLooseContain(form.region, "北美") : ssrLooseEqual(form.region, "北美")) ? " selected" : ""}>北美</option><option value="大洋洲"${ssrIncludeBooleanAttr(Array.isArray(form.region) ? ssrLooseContain(form.region, "大洋洲") : ssrLooseEqual(form.region, "大洋洲")) ? " selected" : ""}>大洋洲</option><option value="巴西"${ssrIncludeBooleanAttr(Array.isArray(form.region) ? ssrLooseContain(form.region, "巴西") : ssrLooseEqual(form.region, "巴西")) ? " selected" : ""}>巴西</option><option value="印度"${ssrIncludeBooleanAttr(Array.isArray(form.region) ? ssrLooseContain(form.region, "印度") : ssrLooseEqual(form.region, "印度")) ? " selected" : ""}>印度</option></select></div></div><div class="form-control mt-4"><label class="label"><span class="label-text font-medium">应用地址 *</span></label><input${ssrRenderAttr("value", form.base_url)} type="text" class="input input-bordered w-full" required placeholder="https://api.xxx.com/"><span class="text-xs text-slate-400 mt-1">用于通知应用业务服务事件(如配置更新)</span></div><div class="form-control mt-4"><label class="label"><span class="label-text font-medium">业务库 DSN *</span></label><input${ssrRenderAttr("value", form.service_dsn)} type="text" class="input input-bordered w-full font-mono text-xs" required placeholder="root:pwd@tcp(host:3306)/earphone?charset=utf8mb4&amp;parseTime=True&amp;loc=Local"><span class="text-xs text-slate-400 mt-1">用于读取业务数据(重置设备码时清 userdevice/product_stat)</span></div><div class="form-control mt-4"><label class="label"><span class="label-text font-medium">备注</span></label><input${ssrRenderAttr("value", form.remark)} type="text" class="input input-bordered w-full"></div><div class="flex items-center gap-2 mt-4"><label class="flex items-center gap-2 cursor-pointer"><input${ssrIncludeBooleanAttr(Array.isArray(form.enabled) ? ssrLooseContain(form.enabled, null) : form.enabled) ? " checked" : ""} type="checkbox" class="toggle toggle-primary toggle-sm"><span class="text-sm">启用</span></label></div><div class="mt-4 text-sm" style="${ssrRenderStyle(testResult.show ? null : { display: "none" })}"><div class="${ssrRenderClass(testResult.apiOk ? "text-green-700" : "text-red-600")}"><span class="${ssrRenderClass(testResult.apiOk ? "text-green-600" : "text-red-500")}">${ssrInterpolate(testResult.apiOk ? "✓" : "✗")}</span> 应用地址:${ssrInterpolate(testResult.apiMsg)}</div><div class="${ssrRenderClass(testResult.dbOk ? "text-green-700" : "text-red-600")}"><span class="${ssrRenderClass(testResult.dbOk ? "text-green-600" : "text-red-500")}">${ssrInterpolate(testResult.dbOk ? "✓" : "✗")}</span> 业务库:${ssrInterpolate(testResult.dbMsg)}</div></div><div class="flex gap-3 mt-6"><button type="button" class="btn btn-ghost">取消</button><button type="button" class="btn btn-outline"${ssrIncludeBooleanAttr(testing.value) ? " disabled" : ""}>${ssrInterpolate(testing.value ? "测试中..." : "测试连接")}</button><button type="submit" class="btn btn-primary text-white"${ssrIncludeBooleanAttr(!testPassed.value) ? " disabled" : ""}>保存</button></div><p class="text-xs text-slate-400 mt-2">需先「测试连接」通过(应用地址可达 + 业务库可连)才能保存。</p></form></div></div><dialog class="modal"><div class="modal-box"><h3 class="font-bold text-lg">分析历史数据</h3><p class="text-sm text-slate-500 mt-1">${ssrInterpolate(az.appLabel)}</p><div class="text-xs text-amber-700 mt-3 bg-amber-50 border border-amber-100 rounded-lg p-3 leading-relaxed"> 扫描该应用业务库,按 <b>天 + 产品 + 区域</b> 生成历史统计(新增用户 / 订单 / 资源发放·使用 / 激活绑定设备),汇入 console 主库。<br> · <b>分析前会先清理该应用+区域在所选区间内的旧统计行</b>,避免数据重叠。<br> · 登录、活跃用户、AI对话等无每日原始记录,历史无法还原(由实时管道往后采集)。<br> · 仅对实时管道上线前的区间运行。留空 = 全部历史。大库可能耗时。 </div><div class="grid grid-cols-2 gap-3 mt-4"><div><label class="text-xs text-slate-500">起始日(含)</label><input${ssrRenderAttr("value", az.start)} type="date" class="input input-bordered input-sm w-full"></div><div><label class="text-xs text-slate-500">结束日(含)</label><input${ssrRenderAttr("value", az.end)} type="date" class="input input-bordered input-sm w-full"></div></div><div class="${ssrRenderClass([az.resultClass, "text-sm mt-3"])}" style="${ssrRenderStyle(az.resultShow ? null : { display: "none" })}">`);
if (az.mode === "progress") {
_push(`<div class="bg-slate-50 border border-slate-200 rounded-lg p-3"><div class="flex justify-between items-center text-xs mb-1.5"><span class="text-slate-600">`);
if (az.curName) {
_push(`<!--[--><span class="loading loading-spinner loading-xs"></span> 正在:${ssrInterpolate(az.curName)} (${ssrInterpolate(az.curIndex)}/${ssrInterpolate(az.curTotal)}) <!--]-->`);
} else {
_push(`<!--[-->准备中...<!--]-->`);
}
_push(`</span><span class="font-mono text-slate-400">${ssrInterpolate(az.pct)}%</span></div><progress class="progress progress-primary w-full h-2"${ssrRenderAttr("value", az.pct)} max="100"></progress><div class="mt-2 text-xs space-y-0.5"><!--[-->`);
ssrRenderList(az.steps, (nm, i) => {
_push(`<div class="${ssrRenderClass(i === az.curIndex - 1 ? "text-slate-600" : "text-green-600")}">`);
if (i === az.curIndex - 1) {
_push(`<!--[--><span class="loading loading-spinner loading-xs"></span> ${ssrInterpolate(nm)}<!--]-->`);
} else {
_push(`<!--[-->✓ ${ssrInterpolate(nm)}<!--]-->`);
}
_push(`</div>`);
});
_push(`<!--]--></div></div>`);
} else if (az.mode === "error") {
_push(`<div class="text-red-600">✗ ${ssrInterpolate(az.errMsg)}</div>`);
} else if (az.mode === "done" && az.report) {
_push(`<div class="bg-green-50 border border-green-100 rounded-lg p-3"><div class="font-semibold text-green-700 mb-2">✓ 分析完成</div><div class="grid grid-cols-2 gap-x-4 gap-y-1 text-xs text-slate-600 mb-2"><div>应用:<b>${ssrInterpolate(az.report.app_name || "")}</b></div><div>区域:${ssrInterpolate(az.report.region || "未指定")}</div><div>区间:${ssrInterpolate((az.report.start_day || "不限") + " ~ " + (az.report.end_day || "不限"))}</div><div>清理旧行:${ssrInterpolate(az.report.cleared_rows || 0)}</div><div>写入天数:<b>${ssrInterpolate(az.report.days_written || 0)}</b></div><div>写入行数:${ssrInterpolate(az.report.rows_written || 0)}</div></div><div class="border-t border-green-100 pt-2 text-xs"><!--[-->`);
ssrRenderList(az.report.steps || [], (s, i) => {
_push(`<div class="flex justify-between py-0.5"><span class="text-slate-500">${ssrInterpolate(s.name)}</span><span class="font-mono text-slate-700">${ssrInterpolate((s.groups || 0) + " 组")}</span></div>`);
});
_push(`<!--]--></div></div>`);
} else {
_push(`<!---->`);
}
_push(`</div><div class="modal-action"><button class="btn btn-ghost btn-sm">关闭</button><button class="btn btn-primary btn-sm text-white"${ssrIncludeBooleanAttr(az.running) ? " disabled" : ""}>${ssrInterpolate(az.btnText)}</button></div></div><form method="dialog" class="modal-backdrop"><button>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.vue");
return _sfc_setup ? _sfc_setup(props, ctx) : void 0;
};
export { _sfc_main as default };
//# sourceMappingURL=apps-DL1E6wot.mjs.map