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.
219 lines
19 KiB
219 lines
19 KiB
import { defineComponent, ref, reactive, computed, useSSRContext } from 'vue';
|
|
import { ssrRenderAttrs, ssrRenderStyle, ssrRenderList, ssrInterpolate, ssrRenderAttr, ssrIncludeBooleanAttr, ssrLooseContain, ssrLooseEqual, ssrRenderClass } from 'vue/server-renderer';
|
|
import { u as useApi } from './useApi-BNSkSrat.mjs';
|
|
import { u as useToast } from './useToast-MBvLaNjH.mjs';
|
|
import './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: "product",
|
|
__ssrInlineRender: true,
|
|
setup(__props) {
|
|
useApi();
|
|
useToast();
|
|
const deviceTypes = { 0: "未知", 1: "经典蓝牙", 2: "BLE双端", 3: "BLE单端", 4: "录音笔", 5: "智能眼镜", 6: "激活码卡片" };
|
|
const deviceTypeOptions = { 0: "未知", 1: "经典蓝牙耳机", 2: "BLE双端", 3: "BLE单端", 4: "智能录音笔", 5: "智能眼镜", 6: "激活码卡片" };
|
|
const toggleFields = [
|
|
{ key: "iscanbindagain", label: "可重复绑定奖励" },
|
|
{ key: "iscontinuouschat", label: "连续对话" },
|
|
{ key: "eqenable", label: "EQ 均衡器" },
|
|
{ key: "keyenable", label: "按键控制" },
|
|
{ key: "workmodeenable", label: "工作模式" },
|
|
{ key: "callmodeenable", label: "通话模式" },
|
|
{ key: "ledenable", label: "LED 灯" },
|
|
{ key: "finddeviceenable", label: "查找设备" },
|
|
{ key: "noisereductionenable", label: "降噪" },
|
|
{ key: "shutdownenable", label: "定时关机" },
|
|
{ key: "showcasebattery", label: "充电盒电量" },
|
|
{ key: "wakeupenable", label: "唤醒控制" },
|
|
{ key: "broadcastpeertranslate", label: "播报对方翻译" },
|
|
{ key: "hideaiassistant", label: "隐藏AI助手" }
|
|
];
|
|
const view = ref("list");
|
|
const rows = ref([]);
|
|
const brands = ref({});
|
|
const brandOptions = ref([]);
|
|
const appOptions = ref([]);
|
|
const loading = ref(false);
|
|
const submitting = ref(false);
|
|
const isEditing = ref(false);
|
|
function emptyToggles() {
|
|
const o = {};
|
|
toggleFields.forEach((t) => {
|
|
o[t.key] = false;
|
|
});
|
|
return o;
|
|
}
|
|
const form = reactive({
|
|
id: 0,
|
|
devicename: "",
|
|
brandid: 0,
|
|
devicetype: 0,
|
|
scanuuid: "",
|
|
devicecodecount: 1,
|
|
productimage: "",
|
|
channel: "official",
|
|
appnames: [],
|
|
bindrewardvpitime: 0,
|
|
bindrewardtranslate: 0,
|
|
bindrewardmeeting: 0,
|
|
toggles: emptyToggles()
|
|
});
|
|
const mobileExtra = reactive({ platform_id: "", platform_key: "", tui: "" });
|
|
function hexId(id) {
|
|
return id ? Number(id).toString(16).toUpperCase() : "";
|
|
}
|
|
function appNamesOf(p) {
|
|
return String(p.appnames || "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
}
|
|
function featureCount(p) {
|
|
return toggleFields.filter((t) => !!p[t.key]).length;
|
|
}
|
|
ref(null);
|
|
const imgUploading = ref(false);
|
|
const currentProduct = ref(null);
|
|
const versions = ref([]);
|
|
const versionSubmitting = ref(false);
|
|
const ver = reactive({ version: "", desc: "", isuse: false });
|
|
const otaFile = ref(null);
|
|
const canAddVersion = computed(() => !!ver.version.trim() && !!otaFile.value);
|
|
function isCurrentVer(v) {
|
|
return !!currentProduct.value?.versionid && v.id === currentProduct.value.versionid;
|
|
}
|
|
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"><h2 class="text-lg font-semibold text-slate-800">产品列表</h2><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><th>功能</th><th>操作</th></tr></thead><tbody>`);
|
|
if (loading.value) {
|
|
_push(`<tr><td colspan="9" class="text-center py-8"><span class="loading loading-spinner loading-sm"></span></td></tr>`);
|
|
} else if (!rows.value.length) {
|
|
_push(`<tr><td colspan="9" class="text-center text-slate-400 py-8">暂无数据</td></tr>`);
|
|
} else {
|
|
_push(`<!---->`);
|
|
}
|
|
_push(`<!--[-->`);
|
|
ssrRenderList(rows.value, (p) => {
|
|
_push(`<tr><td class="font-mono text-sm">${ssrInterpolate(hexId(p.id))}</td><td>`);
|
|
if (p.productimage) {
|
|
_push(`<img${ssrRenderAttr("src", p.productimage)} class="w-10 h-10 rounded object-cover">`);
|
|
} else {
|
|
_push(`<div class="w-10 h-10 rounded bg-slate-100 flex items-center justify-center text-slate-300"><svg class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"></path></svg></div>`);
|
|
}
|
|
_push(`</td><td class="font-medium">${ssrInterpolate(p.devicename)}</td><td>${ssrInterpolate(brands.value[p.brandid] || "")}</td><td>`);
|
|
if (appNamesOf(p).length) {
|
|
_push(`<div class="flex flex-wrap gap-1"><!--[-->`);
|
|
ssrRenderList(appNamesOf(p), (n) => {
|
|
_push(`<span class="badge badge-ghost badge-sm">${ssrInterpolate(n)}</span>`);
|
|
});
|
|
_push(`<!--]--></div>`);
|
|
} else {
|
|
_push(`<span class="text-slate-300 text-xs">未绑定</span>`);
|
|
}
|
|
_push(`</td><td><span class="badge badge-outline badge-sm">${ssrInterpolate(deviceTypes[p.devicetype] || p.devicetype)}</span></td><td class="text-sm">`);
|
|
if (p.version) {
|
|
_push(`<span>${ssrInterpolate(p.version)}</span>`);
|
|
} else {
|
|
_push(`<span class="text-slate-300">-</span>`);
|
|
}
|
|
_push(`</td><td><span class="text-xs text-slate-500">${ssrInterpolate(featureCount(p))}/14</span></td><td><div class="flex gap-1"><button class="btn btn-ghost btn-xs">编辑</button><button class="btn btn-ghost btn-xs text-indigo-600">版本</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><h3 class="text-sm font-semibold text-slate-500 mb-3">基本信息</h3><div class="form-control mb-4"><label class="label"><span class="label-text font-medium">产品名称 *</span></label><input${ssrRenderAttr("value", form.devicename)} type="text" class="input input-bordered w-full" required></div><div class="grid grid-cols-2 gap-4 mb-4"><div class="form-control"><label class="label"><span class="label-text font-medium">所属品牌商</span></label><select class="select select-bordered w-full"><option${ssrRenderAttr("value", 0)}${ssrIncludeBooleanAttr(Array.isArray(form.brandid) ? ssrLooseContain(form.brandid, 0) : ssrLooseEqual(form.brandid, 0)) ? " selected" : ""}>请选择</option><!--[-->`);
|
|
ssrRenderList(brandOptions.value, (b) => {
|
|
_push(`<option${ssrRenderAttr("value", b.id)}${ssrIncludeBooleanAttr(Array.isArray(form.brandid) ? ssrLooseContain(form.brandid, b.id) : ssrLooseEqual(form.brandid, b.id)) ? " selected" : ""}>${ssrInterpolate(b.brand)}</option>`);
|
|
});
|
|
_push(`<!--]--></select></div><div class="form-control"><label class="label"><span class="label-text font-medium">设备类型</span></label><select class="select select-bordered w-full"><!--[-->`);
|
|
ssrRenderList(deviceTypeOptions, (label, val) => {
|
|
_push(`<option${ssrRenderAttr("value", Number(val))}${ssrIncludeBooleanAttr(Array.isArray(form.devicetype) ? ssrLooseContain(form.devicetype, Number(val)) : ssrLooseEqual(form.devicetype, Number(val))) ? " selected" : ""}>${ssrInterpolate(label)}</option>`);
|
|
});
|
|
_push(`<!--]--></select></div></div><div class="form-control mb-4"><label class="label"><span class="label-text font-medium">绑定应用</span><span class="label-text-alt text-slate-400">可多选;品牌商/渠道商账号的「应用」下拉只列出其产品绑定过的应用</span></label>`);
|
|
if (!appOptions.value.length) {
|
|
_push(`<div class="text-xs text-slate-400 border border-dashed border-slate-200 rounded-lg px-3 py-2"> 暂无可选应用,请先到【SaaS 管理 → 应用中心】新建应用 </div>`);
|
|
} else {
|
|
_push(`<div class="flex flex-wrap gap-2 border border-slate-200 rounded-lg px-3 py-2"><!--[-->`);
|
|
ssrRenderList(appOptions.value, (n) => {
|
|
_push(`<label class="label cursor-pointer gap-2 py-1"><input${ssrIncludeBooleanAttr(Array.isArray(form.appnames) ? ssrLooseContain(form.appnames, n) : form.appnames) ? " checked" : ""} type="checkbox"${ssrRenderAttr("value", n)} class="checkbox checkbox-sm checkbox-primary"><span class="label-text">${ssrInterpolate(n)}</span></label>`);
|
|
});
|
|
_push(`<!--]--></div>`);
|
|
}
|
|
_push(`</div><div class="grid grid-cols-2 gap-4 mb-4"><div class="form-control"><label class="label"><span class="label-text font-medium">扫描UUID</span></label><input${ssrRenderAttr("value", form.scanuuid)} type="text" class="input input-bordered w-full"></div><div class="form-control"><label class="label"><span class="label-text font-medium">设备码数量(每副)</span></label><select class="select select-bordered w-full"><option${ssrRenderAttr("value", 1)}${ssrIncludeBooleanAttr(Array.isArray(form.devicecodecount) ? ssrLooseContain(form.devicecodecount, 1) : ssrLooseEqual(form.devicecodecount, 1)) ? " selected" : ""}>单份 (1个/副)</option><option${ssrRenderAttr("value", 2)}${ssrIncludeBooleanAttr(Array.isArray(form.devicecodecount) ? ssrLooseContain(form.devicecodecount, 2) : ssrLooseEqual(form.devicecodecount, 2)) ? " selected" : ""}>双份 (2个/副)</option></select></div></div><div class="grid grid-cols-2 gap-4 mb-4"><div class="form-control"><label class="label"><span class="label-text font-medium">渠道方</span></label><select class="select select-bordered w-full"><option value="official"${ssrIncludeBooleanAttr(Array.isArray(form.channel) ? ssrLooseContain(form.channel, "official") : ssrLooseEqual(form.channel, "official")) ? " selected" : ""}>官方</option><option value="mobile"${ssrIncludeBooleanAttr(Array.isArray(form.channel) ? ssrLooseContain(form.channel, "mobile") : ssrLooseEqual(form.channel, "mobile")) ? " selected" : ""}>移动</option></select></div></div><div class="mb-4" style="${ssrRenderStyle(form.channel === "mobile" ? null : { display: "none" })}"><div class="grid grid-cols-3 gap-4"><div class="form-control"><label class="label"><span class="label-text font-medium">移动平台ID</span></label><input${ssrRenderAttr("value", mobileExtra.platform_id)} type="text" class="input input-bordered w-full" placeholder="移动平台分配的 ID"></div><div class="form-control"><label class="label"><span class="label-text font-medium">移动平台KEY</span></label><input${ssrRenderAttr("value", mobileExtra.platform_key)} type="text" class="input input-bordered w-full" placeholder="移动平台分配的 KEY"></div><div class="form-control"><label class="label"><span class="label-text font-medium">TUI(8位)</span></label><input${ssrRenderAttr("value", mobileExtra.tui)} type="text" maxlength="8" class="input input-bordered w-full" placeholder="CMEI 前缀,如 11113396"></div></div></div><div class="form-control mb-4"><label class="label"><span class="label-text font-medium">产品图片</span></label><div class="flex items-start gap-4"><div class="flex-1"><input${ssrRenderAttr("value", form.productimage)} type="text" class="input input-bordered w-full input-sm mb-2" placeholder="图片URL(可手动输入或上传)"><div class="flex items-center gap-2"><input type="file" accept="image/*" class="file-input file-input-bordered file-input-sm flex-1"><button type="button" class="btn btn-outline btn-sm btn-primary"${ssrIncludeBooleanAttr(imgUploading.value) ? " disabled" : ""}>`);
|
|
if (imgUploading.value) {
|
|
_push(`<span class="loading loading-spinner loading-xs"></span>`);
|
|
} else {
|
|
_push(`<!---->`);
|
|
}
|
|
_push(`上传 </button></div></div>`);
|
|
if (form.productimage) {
|
|
_push(`<div><img${ssrRenderAttr("src", form.productimage)} class="w-20 h-20 object-cover rounded-lg border"></div>`);
|
|
} else {
|
|
_push(`<!---->`);
|
|
}
|
|
_push(`</div></div><div class="divider text-sm text-slate-400">绑定奖励设置</div><div class="grid grid-cols-3 gap-4 mb-4"><div class="form-control"><label class="label"><span class="label-text font-medium">VIP时长(天)</span></label><input${ssrRenderAttr("value", form.bindrewardvpitime)} type="number" min="0" class="input input-bordered w-full"></div><div class="form-control"><label class="label"><span class="label-text font-medium">翻译时长(分钟)</span></label><input${ssrRenderAttr("value", form.bindrewardtranslate)} type="number" min="0" class="input input-bordered w-full"></div><div class="form-control"><label class="label"><span class="label-text font-medium">会议时长(分钟)</span></label><input${ssrRenderAttr("value", form.bindrewardmeeting)} type="number" min="0" class="input input-bordered w-full"></div></div><div class="divider text-sm text-slate-400">功能开关</div><div class="grid grid-cols-3 gap-3 mb-6"><!--[-->`);
|
|
ssrRenderList(toggleFields, (t) => {
|
|
_push(`<label class="flex items-center gap-2 cursor-pointer"><input${ssrIncludeBooleanAttr(Array.isArray(form.toggles[t.key]) ? ssrLooseContain(form.toggles[t.key], null) : form.toggles[t.key]) ? " checked" : ""} type="checkbox" class="toggle toggle-primary toggle-sm"><span class="text-sm">${ssrInterpolate(t.label)}</span></label>`);
|
|
});
|
|
_push(`<!--]--></div><div class="flex gap-3"><button type="button" class="btn btn-ghost">取消</button><button type="submit" class="btn btn-primary text-white"${ssrIncludeBooleanAttr(submitting.value) ? " disabled" : ""}>`);
|
|
if (submitting.value) {
|
|
_push(`<span class="loading loading-spinner loading-xs"></span>`);
|
|
} else {
|
|
_push(`<!---->`);
|
|
}
|
|
_push(`保存 </button></div></form></div></div><div style="${ssrRenderStyle(view.value === "version" ? 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(currentProduct.value?.devicename || "")}</h2></div>`);
|
|
if (currentProduct.value) {
|
|
_push(`<div class="bg-white rounded-xl shadow-sm border border-slate-100 p-4 mb-5"><div class="flex items-center gap-6 text-sm flex-wrap"><span class="text-slate-400">产品ID:</span><span class="font-mono">${ssrInterpolate(hexId(currentProduct.value.id))}</span><span class="text-slate-400">当前版本:</span><span class="badge badge-primary badge-sm">${ssrInterpolate(currentProduct.value.version || "无")}</span><span class="text-slate-400">版本ID:</span><span class="text-xs">${ssrInterpolate(currentProduct.value.versionid || "无")}</span><span class="text-slate-400">更新包:</span><span class="text-xs max-w-md truncate">${ssrInterpolate(currentProduct.value.updatepackageaddress || "无")}</span></div></div>`);
|
|
} else {
|
|
_push(`<!---->`);
|
|
}
|
|
_push(`<div class="bg-white rounded-xl shadow-sm border border-slate-100 p-5 mb-5"><h3 class="text-sm font-semibold text-slate-700 mb-3">添加新版本</h3><form><div class="grid grid-cols-2 gap-4 mb-3"><div class="form-control"><label class="label"><span class="label-text text-xs">版本号 *</span></label><input${ssrRenderAttr("value", ver.version)} type="text" class="input input-bordered input-sm w-full" placeholder="如 1.2.0"></div><div class="form-control"><label class="label"><span class="label-text text-xs">描述</span></label><input${ssrRenderAttr("value", ver.desc)} type="text" class="input input-bordered input-sm w-full" placeholder="版本描述"></div></div><div class="form-control mb-3"><label class="label"><span class="label-text text-xs">OTA 更新包 *</span></label><input type="file" class="file-input file-input-bordered file-input-sm w-full" accept=".bin,.hex,.zip,.gz,.tar,.ota,.ufw,.fot"></div><div class="flex items-center gap-3"><label class="flex items-center gap-2 cursor-pointer"><input${ssrIncludeBooleanAttr(Array.isArray(ver.isuse) ? ssrLooseContain(ver.isuse, null) : ver.isuse) ? " checked" : ""} type="checkbox" class="checkbox checkbox-primary checkbox-sm"><span class="text-sm">设为当前使用版本</span></label><button type="submit" class="btn btn-primary btn-sm text-white ml-auto"${ssrIncludeBooleanAttr(!canAddVersion.value || versionSubmitting.value) ? " disabled" : ""}>`);
|
|
if (versionSubmitting.value) {
|
|
_push(`<span class="loading loading-spinner loading-xs"></span>`);
|
|
} else {
|
|
_push(`<!---->`);
|
|
}
|
|
_push(`添加版本 </button></div></form></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></tr></thead><tbody>`);
|
|
if (!versions.value.length) {
|
|
_push(`<tr><td colspan="5" class="text-center text-slate-400 py-8">暂无版本</td></tr>`);
|
|
} else {
|
|
_push(`<!---->`);
|
|
}
|
|
_push(`<!--[-->`);
|
|
ssrRenderList(versions.value, (v) => {
|
|
_push(`<tr><td>${ssrInterpolate(v.id)}</td><td><span class="${ssrRenderClass([isCurrentVer(v) ? "badge-primary" : "badge-outline", "badge badge-sm"])}">${ssrInterpolate(v.version)}</span>`);
|
|
if (isCurrentVer(v)) {
|
|
_push(`<span class="badge badge-success badge-xs ml-1">当前</span>`);
|
|
} else {
|
|
_push(`<!---->`);
|
|
}
|
|
_push(`</td><td class="text-sm">${ssrInterpolate(v.description || "")}</td><td class="text-xs text-slate-400 max-w-xs truncate">${ssrInterpolate(v.updatepackageaddress || "")}</td><td><div class="flex gap-1">`);
|
|
if (isCurrentVer(v)) {
|
|
_push(`<span class="text-xs text-slate-400">使用中</span>`);
|
|
} else {
|
|
_push(`<!--[--><button class="btn btn-ghost btn-xs text-indigo-600">设为当前</button><button class="btn btn-ghost btn-xs text-red-500">删除</button><!--]-->`);
|
|
}
|
|
_push(`</div></td></tr>`);
|
|
});
|
|
_push(`<!--]--></tbody></table></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/product.vue");
|
|
return _sfc_setup ? _sfc_setup(props, ctx) : void 0;
|
|
};
|
|
|
|
export { _sfc_main as default };
|
|
//# sourceMappingURL=product-3h7bsXRI.mjs.map
|
|
|