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.
 
 
 
 
 
 

274 lines
20 KiB

import { defineComponent, ref, reactive, computed, useSSRContext } from 'vue';
import { ssrRenderAttrs, ssrIncludeBooleanAttr, ssrLooseContain, ssrLooseEqual, ssrRenderList, ssrRenderAttr, ssrInterpolate, ssrRenderStyle, ssrRenderClass } from 'vue/server-renderer';
import { u as useApi } from './useApi-B-E5eLsI.mjs';
import { u as useToast } from './useToast-CS21XT61.mjs';
import { _ as _export_sfc, b as useRouter } from './server.mjs';
import './useLoading-D4zrSa2m.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: "channels",
__ssrInlineRender: true,
setup(__props) {
const CHANNELS = {
1: "华为",
2: "小米",
3: "OPPO",
4: "VIVO",
5: "荣耀",
100: "官方包",
101: "App Store",
102: "Google Play"
};
function channelName(v) {
return CHANNELS[v] ?? "渠道" + v;
}
const CHANNEL_GROUPS = [
{ label: "中国厂商", icon: "📱", color: "#0369a1", bg: "#dbeafe", channels: [1, 2, 3, 4, 5] },
{ label: "全球商店", icon: "🌍", color: "#c2410c", bg: "#fff7ed", channels: [101, 102] },
{ label: "官方渠道", icon: "🏠", color: "#059669", bg: "#dcfce7", channels: [100] }
];
const STATUS = [
{ v: 0, label: "草稿(不下发)", color: "#64748b", bg: "#f1f5f9" },
{ v: 1, label: "已上架", color: "#059669", bg: "#dcfce7" },
{ v: 2, label: "审核中", color: "#b45309", bg: "#fef9c3" },
{ v: 3, label: "已下架", color: "#be123c", bg: "#ffe4e6" }
];
function statusLabel(v) {
return STATUS.find((s) => s.v === v)?.label ?? "未知";
}
function statusStyle(v) {
const s = STATUS.find((x) => x.v === v);
return s ? { background: s.bg, color: s.color } : {};
}
const PAY_CHANNELS = [
{ key: "wxpay", label: "微信支付", module: "wechatpay" },
{ key: "alipay", label: "支付宝", module: "alipay" },
{ key: "paypal", label: "PayPal", module: "paypal" },
{ key: "apple", label: "Apple 内购", module: "appleiap" },
{ key: "google", label: "Google 内购", module: "googleiap" }
];
function payLabel(k) {
return PAY_CHANNELS.find((p) => p.key === k)?.label ?? k;
}
useApi();
useToast();
useRouter();
const scopeApp = ref("");
const scopeApps = ref([]);
const items = ref([]);
const payConfigured = ref([]);
const payChecked_ = ref(false);
const modalOpen = ref(false);
const isEditing = ref(false);
const submitting = ref(false);
function emptyForm() {
return {
row_id: 0,
app_name: "",
channel: 0,
version: "",
min_version: "",
force_update: false,
changelog: "",
address: "",
review_version: "",
status: 1,
description: "",
tourists: false,
allowskipdevicebinding: false,
paychannels: "",
enable: true
};
}
const form = reactive(emptyForm());
const groups = computed(
() => CHANNEL_GROUPS.map((g) => ({
...g,
items: items.value.filter((c) => g.channels.includes(c.channel)).sort((a, b) => a.channel - b.channel),
missing: g.channels.filter((ch) => !items.value.some((c) => c.channel === ch))
}))
);
function payList(c) {
return (c.paychannels || "").split(",").map((x) => x.trim()).filter(Boolean);
}
function payMissing(key) {
if (!payChecked_.value) return false;
const mod = PAY_CHANNELS.find((p) => p.key === key)?.module;
return !!mod && !payConfigured.value.includes(mod);
}
const payWarnings = computed(() => {
if (!payChecked_.value) return [];
const map = {};
for (const c of items.value) {
if (!c.enable) continue;
for (const p of payList(c)) {
if (!payMissing(p)) continue;
(map[p] = map[p] || []).push(channelName(c.channel));
}
}
return Object.keys(map).map((k) => ({ key: k, label: payLabel(k), channels: map[k] }));
});
const versionWarn = computed(() => {
if (form.force_update && !form.min_version.trim()) return "开启强制更新时必须填「最低支持版本」,否则挡不住任何人";
if (form.min_version.trim() && form.version.trim() && cmpVer(form.min_version, form.version) > 0) {
return "「最低支持版本」高于「当前线上版本」,会把所有线上用户判定为需强更却无更高版本可更";
}
return "";
});
function cmpVer(a, b) {
const as = a.trim().split("."), bs = b.trim().split(".");
const n = Math.max(as.length, bs.length);
for (let i = 0; i < n; i++) {
const x = parseInt(as[i] ?? "0") || 0;
const y = parseInt(bs[i] ?? "0") || 0;
if (x !== y) return x < y ? -1 : 1;
}
return 0;
}
function payChecked(key) {
return payList(form).includes(key);
}
return (_ctx, _push, _parent, _attrs) => {
_push(`<div${ssrRenderAttrs(_attrs)} data-v-6057a5c9><div class="flex items-center justify-between mb-5 flex-wrap gap-3" data-v-6057a5c9><div data-v-6057a5c9><h2 class="text-lg font-semibold text-slate-800" data-v-6057a5c9>渠道分发</h2><p class="text-xs text-slate-400 mt-1" data-v-6057a5c9> 管理各应用在各分发渠道上的版本、强制更新与能力开关。支付渠道在这里控制「开放哪几种支付入口」,商户凭据在应用的业务功能配置里填。 </p></div><div class="flex items-center gap-3" data-v-6057a5c9><span class="text-sm text-slate-500" data-v-6057a5c9>应用</span><select class="select select-bordered select-sm" data-v-6057a5c9><option value="" data-v-6057a5c9${ssrIncludeBooleanAttr(Array.isArray(scopeApp.value) ? ssrLooseContain(scopeApp.value, "") : ssrLooseEqual(scopeApp.value, "")) ? " selected" : ""}>全局默认(所有应用兜底)</option><!--[-->`);
ssrRenderList(scopeApps.value, (a) => {
_push(`<option${ssrRenderAttr("value", a)} data-v-6057a5c9${ssrIncludeBooleanAttr(Array.isArray(scopeApp.value) ? ssrLooseContain(scopeApp.value, a) : ssrLooseEqual(scopeApp.value, a)) ? " selected" : ""}>${ssrInterpolate(a)}</option>`);
});
_push(`<!--]--></select><button class="btn btn-primary btn-sm text-white" data-v-6057a5c9>+ 添加渠道</button></div></div>`);
if (payWarnings.value.length) {
_push(`<div class="pay-warn" data-v-6057a5c9><div class="flex items-start gap-2" data-v-6057a5c9><span data-v-6057a5c9>⚠</span><div class="flex-1" data-v-6057a5c9><div class="text-sm font-semibold" data-v-6057a5c9>以下支付方式已在渠道里开放,但商户凭据还没配:</div><div class="text-xs mt-1" data-v-6057a5c9><!--[-->`);
ssrRenderList(payWarnings.value, (w) => {
_push(`<span class="warn-pill" data-v-6057a5c9>${ssrInterpolate(w.label)}(${ssrInterpolate(w.channels.join("、"))})</span>`);
});
_push(`<!--]--></div></div><button class="btn btn-xs btn-ghost" data-v-6057a5c9>去配置 →</button></div></div>`);
} else {
_push(`<!---->`);
}
if (!scopeApp.value) {
_push(`<div class="scope-tip" data-v-6057a5c9> 当前是<b data-v-6057a5c9>全局默认</b>作用域:这里配的渠道对所有应用生效,应用自己配了同渠道则以应用的为准。 </div>`);
} else {
_push(`<!---->`);
}
_push(`<!--[-->`);
ssrRenderList(groups.value, (g) => {
_push(`<div class="mb-6" data-v-6057a5c9><div class="flex items-center gap-2 mb-3" data-v-6057a5c9><span class="group-tag" style="${ssrRenderStyle({ background: g.bg, color: g.color })}" data-v-6057a5c9>${ssrInterpolate(g.icon)} ${ssrInterpolate(g.label)}</span><span class="text-xs text-slate-400" data-v-6057a5c9>${ssrInterpolate(g.items.length)} / ${ssrInterpolate(g.channels.length)} 已配置</span></div><div class="ch-grid" data-v-6057a5c9><!--[-->`);
ssrRenderList(g.items, (c) => {
_push(`<div class="${ssrRenderClass([{ off: !c.enable }, "ch-card"])}" data-v-6057a5c9><div class="flex items-start justify-between gap-2" data-v-6057a5c9><div class="min-w-0" data-v-6057a5c9><div class="font-semibold text-sm text-slate-800" data-v-6057a5c9>${ssrInterpolate(channelName(c.channel))}</div><div class="text-[11px] text-slate-400 mt-0.5" data-v-6057a5c9>渠道号 ${ssrInterpolate(c.channel)}</div></div><span class="status-badge" style="${ssrRenderStyle(statusStyle(c.status))}" data-v-6057a5c9>${ssrInterpolate(statusLabel(c.status))}</span></div><div class="ver-row" data-v-6057a5c9><div class="ver-box" data-v-6057a5c9><span class="ver-label" data-v-6057a5c9>线上版本</span><span class="ver-val" data-v-6057a5c9>${ssrInterpolate(c.version || "—")}</span></div><div class="ver-box" data-v-6057a5c9><span class="ver-label" data-v-6057a5c9>最低支持</span><span class="${ssrRenderClass([{ warn: c.force_update }, "ver-val"])}" data-v-6057a5c9>${ssrInterpolate(c.min_version || "—")}</span></div>`);
if (c.review_version) {
_push(`<div class="ver-box" data-v-6057a5c9><span class="ver-label" data-v-6057a5c9>审核中</span><span class="ver-val review" data-v-6057a5c9>${ssrInterpolate(c.review_version)}</span></div>`);
} else {
_push(`<!---->`);
}
_push(`</div><div class="flex flex-wrap items-center gap-1.5 mt-2" data-v-6057a5c9>`);
if (c.force_update) {
_push(`<span class="flag flag-warn" data-v-6057a5c9>强制更新</span>`);
} else {
_push(`<!---->`);
}
if (c.tourists) {
_push(`<span class="flag" data-v-6057a5c9>游客登录</span>`);
} else {
_push(`<!---->`);
}
if (c.allowskipdevicebinding) {
_push(`<span class="flag" data-v-6057a5c9>可跳过绑定</span>`);
} else {
_push(`<!---->`);
}
if (!c.enable) {
_push(`<span class="flag flag-off" data-v-6057a5c9>已停用</span>`);
} else {
_push(`<!---->`);
}
_push(`</div>`);
if (payList(c).length) {
_push(`<div class="flex flex-wrap items-center gap-1.5 mt-2" data-v-6057a5c9><!--[-->`);
ssrRenderList(payList(c), (p) => {
_push(`<span class="${ssrRenderClass([{ missing: payMissing(p) }, "pay-pill"])}"${ssrRenderAttr("title", payMissing(p) ? "该支付方式的商户凭据尚未配置" : "")} data-v-6057a5c9>${ssrInterpolate(payLabel(p))}${ssrInterpolate(payMissing(p) ? " ⚠" : "")}</span>`);
});
_push(`<!--]--></div>`);
} else {
_push(`<!---->`);
}
if (c.address) {
_push(`<div class="text-[11px] text-slate-400 mt-2 truncate" data-v-6057a5c9>${ssrInterpolate(c.address)}</div>`);
} else {
_push(`<!---->`);
}
_push(`</div>`);
});
_push(`<!--]--><!--[-->`);
ssrRenderList(g.missing, (ch) => {
_push(`<div class="ch-card ch-empty" data-v-6057a5c9><div class="text-sm text-slate-400" data-v-6057a5c9>${ssrInterpolate(channelName(ch))}</div><div class="text-[11px] text-slate-300 mt-1" data-v-6057a5c9>未配置 · 点击添加</div></div>`);
});
_push(`<!--]--></div></div>`);
});
_push(`<!--]--><div class="ch-overlay" style="${ssrRenderStyle(modalOpen.value ? null : { display: "none" })}" data-v-6057a5c9><div class="ch-box" data-v-6057a5c9><div class="flex items-center justify-between mb-4" data-v-6057a5c9><div class="flex items-center gap-3" data-v-6057a5c9><h3 class="text-base font-semibold text-slate-800" data-v-6057a5c9>${ssrInterpolate(isEditing.value ? "编辑渠道" : "添加渠道")}</h3><span class="scope-chip" data-v-6057a5c9>${ssrInterpolate(scopeApp.value || "全局默认")}</span></div><button class="btn btn-ghost btn-xs" data-v-6057a5c9>✕ 关闭</button></div><section class="ed-section" data-v-6057a5c9><div class="ed-section-title" data-v-6057a5c9>渠道</div><div class="grid grid-cols-2 gap-3" data-v-6057a5c9><div data-v-6057a5c9><span class="ed-label" data-v-6057a5c9>分发渠道 <i class="req" data-v-6057a5c9>*</i></span><select${ssrIncludeBooleanAttr(isEditing.value) ? " disabled" : ""} class="select select-bordered select-sm w-full" data-v-6057a5c9><option${ssrRenderAttr("value", 0)} disabled data-v-6057a5c9${ssrIncludeBooleanAttr(Array.isArray(form.channel) ? ssrLooseContain(form.channel, 0) : ssrLooseEqual(form.channel, 0)) ? " selected" : ""}>请选择渠道</option><!--[-->`);
ssrRenderList(CHANNEL_GROUPS, (g) => {
_push(`<optgroup${ssrRenderAttr("label", g.label)} data-v-6057a5c9><!--[-->`);
ssrRenderList(g.channels, (ch) => {
_push(`<option${ssrRenderAttr("value", ch)} data-v-6057a5c9${ssrIncludeBooleanAttr(Array.isArray(form.channel) ? ssrLooseContain(form.channel, ch) : ssrLooseEqual(form.channel, ch)) ? " selected" : ""}>${ssrInterpolate(channelName(ch))}(${ssrInterpolate(ch)})</option>`);
});
_push(`<!--]--></optgroup>`);
});
_push(`<!--]--></select></div><div data-v-6057a5c9><span class="ed-label" data-v-6057a5c9>上架状态</span><select class="select select-bordered select-sm w-full" data-v-6057a5c9><!--[-->`);
ssrRenderList(STATUS, (s) => {
_push(`<option${ssrRenderAttr("value", s.v)} data-v-6057a5c9${ssrIncludeBooleanAttr(Array.isArray(form.status) ? ssrLooseContain(form.status, s.v) : ssrLooseEqual(form.status, s.v)) ? " selected" : ""}>${ssrInterpolate(s.label)}</option>`);
});
_push(`<!--]--></select></div></div><div class="mt-3" data-v-6057a5c9><span class="ed-label" data-v-6057a5c9>描述</span><input${ssrRenderAttr("value", form.description)} class="input input-bordered input-sm w-full" placeholder="这个渠道包的说明" data-v-6057a5c9></div></section><section class="ed-section" data-v-6057a5c9><div class="ed-section-title" data-v-6057a5c9>版本</div><div class="grid grid-cols-3 gap-3" data-v-6057a5c9><div data-v-6057a5c9><span class="ed-label" data-v-6057a5c9>当前线上版本</span><input${ssrRenderAttr("value", form.version)} class="input input-bordered input-sm w-full" placeholder="如 1.4.2" data-v-6057a5c9></div><div data-v-6057a5c9><span class="ed-label" data-v-6057a5c9>最低支持版本</span><input${ssrRenderAttr("value", form.min_version)} class="input input-bordered input-sm w-full" placeholder="低于它的客户端需更新" data-v-6057a5c9></div><div data-v-6057a5c9><span class="ed-label" data-v-6057a5c9>审核中版本</span><input${ssrRenderAttr("value", form.review_version)} class="input input-bordered input-sm w-full" placeholder="过审后清空" data-v-6057a5c9></div></div><label class="flex items-center gap-2 text-sm cursor-pointer mt-3" data-v-6057a5c9><input${ssrIncludeBooleanAttr(Array.isArray(form.force_update) ? ssrLooseContain(form.force_update, null) : form.force_update) ? " checked" : ""} type="checkbox" class="checkbox checkbox-sm checkbox-primary" data-v-6057a5c9><span data-v-6057a5c9>强制更新</span><span class="text-xs text-slate-400" data-v-6057a5c9>低于「最低支持版本」的客户端必须更新才能继续使用</span></label>`);
if (versionWarn.value) {
_push(`<p class="ed-warn" data-v-6057a5c9>⚠ ${ssrInterpolate(versionWarn.value)}</p>`);
} else {
_push(`<!---->`);
}
_push(`<div class="mt-3" data-v-6057a5c9><span class="ed-label" data-v-6057a5c9>下载地址</span><input${ssrRenderAttr("value", form.address)} class="input input-bordered input-sm w-full" placeholder="应用商店链接或 apk 直链" data-v-6057a5c9></div><div class="mt-3" data-v-6057a5c9><span class="ed-label" data-v-6057a5c9>更新日志</span><textarea class="textarea textarea-bordered w-full text-xs" rows="3" placeholder="本次更新内容,会下发给客户端展示" data-v-6057a5c9>${ssrInterpolate(form.changelog)}</textarea></div></section><section class="ed-section" data-v-6057a5c9><div class="ed-section-title" data-v-6057a5c9>能力开关</div><div class="flex flex-col gap-2" data-v-6057a5c9><label class="flex items-center gap-2 text-sm cursor-pointer" data-v-6057a5c9><input${ssrIncludeBooleanAttr(Array.isArray(form.tourists) ? ssrLooseContain(form.tourists, null) : form.tourists) ? " checked" : ""} type="checkbox" class="checkbox checkbox-sm checkbox-primary" data-v-6057a5c9><span data-v-6057a5c9>允许游客登录</span></label><label class="flex items-center gap-2 text-sm cursor-pointer" data-v-6057a5c9><input${ssrIncludeBooleanAttr(Array.isArray(form.allowskipdevicebinding) ? ssrLooseContain(form.allowskipdevicebinding, null) : form.allowskipdevicebinding) ? " checked" : ""} type="checkbox" class="checkbox checkbox-sm checkbox-primary" data-v-6057a5c9><span data-v-6057a5c9>允许跳过设备绑定</span></label></div></section><section class="ed-section" data-v-6057a5c9><div class="ed-section-title" data-v-6057a5c9>支付渠道</div><div class="pay-grid" data-v-6057a5c9><!--[-->`);
ssrRenderList(PAY_CHANNELS, (p) => {
_push(`<label class="${ssrRenderClass([{ checked: payChecked(p.key) }, "pay-item"])}" data-v-6057a5c9><input type="checkbox" class="checkbox checkbox-xs checkbox-primary"${ssrIncludeBooleanAttr(payChecked(p.key)) ? " checked" : ""} data-v-6057a5c9><span data-v-6057a5c9>${ssrInterpolate(p.label)}</span>`);
if (payChecked(p.key) && payMissing(p.key)) {
_push(`<span class="pay-miss" data-v-6057a5c9>未配凭据</span>`);
} else {
_push(`<!---->`);
}
_push(`</label>`);
});
_push(`<!--]--></div><p class="ed-tip" data-v-6057a5c9> 这里只决定「这个渠道开放哪几种支付入口」;商户号、密钥等凭据在 `);
if (scopeApp.value) {
_push(`<a class="link" data-v-6057a5c9>应用「${ssrInterpolate(scopeApp.value)}」→ 业务功能配置 → 支付</a>`);
} else {
_push(`<span data-v-6057a5c9>各应用的「业务功能配置 → 支付」</span>`);
}
_push(` 里填。 </p></section><div class="ed-footer" data-v-6057a5c9><label class="flex items-center gap-2 text-sm cursor-pointer" data-v-6057a5c9><input${ssrIncludeBooleanAttr(Array.isArray(form.enable) ? ssrLooseContain(form.enable, null) : form.enable) ? " checked" : ""} type="checkbox" class="checkbox checkbox-sm checkbox-primary" data-v-6057a5c9><span data-v-6057a5c9>启用</span><span class="text-xs text-slate-400" data-v-6057a5c9>停用后该渠道配置不下发客户端</span></label><div class="flex gap-2" data-v-6057a5c9>`);
if (isEditing.value) {
_push(`<button class="btn btn-ghost btn-sm text-error mr-auto" data-v-6057a5c9>删除</button>`);
} else {
_push(`<!---->`);
}
_push(`<button class="btn btn-ghost btn-sm" data-v-6057a5c9>取消</button><button class="btn btn-primary btn-sm text-white"${ssrIncludeBooleanAttr(submitting.value) ? " disabled" : ""} data-v-6057a5c9>保存</button></div></div></div></div></div>`);
};
}
});
const _sfc_setup = _sfc_main.setup;
_sfc_main.setup = (props, ctx) => {
const ssrContext = useSSRContext();
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("pages/channels.vue");
return _sfc_setup ? _sfc_setup(props, ctx) : void 0;
};
const channels = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-6057a5c9"]]);
export { channels as default };
//# sourceMappingURL=channels-CRuax8yi.mjs.map