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.
 
 
 
 
 
 

259 lines
21 KiB

import { defineComponent, ref, reactive, computed, unref, useSSRContext } from 'vue';
import { ssrRenderAttrs, ssrInterpolate, ssrIncludeBooleanAttr, ssrLooseContain, ssrLooseEqual, ssrRenderList, ssrRenderAttr, ssrRenderClass } from 'vue/server-renderer';
import { u as useApi } from './useApi-BNSkSrat.mjs';
import { u as useToast } from './useToast-MBvLaNjH.mjs';
import { A as ALL_LANG_OPTIONS, l as langName } from './useLangOptions-CXXbv5NM.mjs';
import { _ as _export_sfc } 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 PRIMARY_LANG = "zh-CN";
const _sfc_main = /* @__PURE__ */ defineComponent({
__name: "meettemplates",
__ssrInlineRender: true,
setup(__props) {
const SYNC_FIELDS = [
{ key: "ttype", label: "类型 ttype" },
{ key: "tags", label: "标签 tags" },
{ key: "icon", label: "图标 icon" },
{ key: "title", label: "标题 title", translated: true },
{ key: "description", label: "描述 description", translated: true },
{ key: "outline", label: "大纲 outline", translated: true },
{ key: "template", label: "模板内容 template", translated: true }
];
useApi();
useToast();
const view = ref("list");
const rows = ref([]);
const loading = ref(false);
const detailLoading = ref(false);
const submitting = ref(false);
const catFilter = ref("");
const typeFilter = ref("");
const kwFilter = ref("");
const current = ref(null);
const curLang = ref("");
const form = reactive({
id: 0,
title: "",
tid: "",
language: "",
ttype: "",
tags: "",
icon: "",
description: "",
outline: "",
template: ""
});
const syncOpen = ref(false);
const syncFields = ref([]);
const syncLangs = ref([]);
const addLangOpen = ref(false);
const newLang = ref("");
const newLangErr = ref("");
const createOpen = ref(false);
const createTid = ref("");
const createTitle = ref("");
const createLang = ref(PRIMARY_LANG);
const createErr = ref("");
function tagList(s) {
return (s || "").split(",").map((x) => x.trim()).filter(Boolean);
}
function isImg(s) {
return /^https?:\/\//i.test(s || "");
}
function isPrimary(lang) {
return lang === PRIMARY_LANG;
}
function catOf(tid) {
const i = (tid || "").indexOf("-");
return i > 0 ? tid.slice(0, i) : "其他";
}
function initials(c) {
const t = (c.primary.title || c.tid || "?").trim();
return t.slice(0, 1).toUpperCase();
}
const cards = computed(() => {
const byTid = /* @__PURE__ */ new Map();
rows.value.forEach((t) => {
const k = t.tid || "";
if (!byTid.has(k)) byTid.set(k, []);
byTid.get(k).push(t);
});
const out = [];
byTid.forEach((items, tid) => {
const langs = items.slice().sort((a, b) => {
if (isPrimary(a.language) !== isPrimary(b.language)) return isPrimary(a.language) ? -1 : 1;
return (a.language || "") < (b.language || "") ? -1 : 1;
});
const zh = langs.find((t) => isPrimary(t.language));
out.push({ tid, primary: zh || langs[0], hasPrimary: !!zh, langs });
});
return out.sort((a, b) => a.tid < b.tid ? -1 : 1);
});
const catOptions = computed(
() => Array.from(new Set(cards.value.map((c) => catOf(c.tid)))).sort()
);
const typeOptions = computed(
() => Array.from(new Set(rows.value.map((t) => t.ttype).filter(Boolean))).sort()
);
const filteredCards = computed(() => {
const kw = kwFilter.value.trim().toLowerCase();
return cards.value.filter((c) => {
if (catFilter.value && catOf(c.tid) !== catFilter.value) return false;
if (typeFilter.value && c.primary.ttype !== typeFilter.value) return false;
if (!kw) return true;
return `${c.tid} ${c.primary.title} ${c.primary.description}`.toLowerCase().includes(kw);
});
});
const otherLangs = computed(
() => (current.value?.langs ?? []).map((t) => t.language).filter((l) => l && l !== curLang.value)
);
const addableLangs = computed(() => {
const has = new Set((current.value?.langs ?? []).map((t) => t.language));
return ALL_LANG_OPTIONS.filter((o) => !has.has(o.code));
});
return (_ctx, _push, _parent, _attrs) => {
_push(`<div${ssrRenderAttrs(_attrs)} data-v-5eb39361>`);
if (view.value === "list") {
_push(`<div data-v-5eb39361><div class="flex flex-wrap items-start justify-between gap-3 mb-5" data-v-5eb39361><div data-v-5eb39361><h2 class="text-lg font-semibold text-slate-800" data-v-5eb39361>会议模板</h2><p class="text-xs text-slate-400 mt-1" data-v-5eb39361> 会议纪要公共模板(source=public),所有应用共享。同一 tid 下每种语言一份,卡面展示 ${ssrInterpolate(PRIMARY_LANG)} 版本。修改后各服务约 10 分钟内自动同步。 </p></div><button class="btn btn-primary btn-sm text-white" data-v-5eb39361>+ 新增模板</button></div><div class="panel p-3 mb-5" data-v-5eb39361><div class="flex flex-wrap items-center gap-2" data-v-5eb39361><select class="select select-bordered select-sm w-40" data-v-5eb39361><option value="" data-v-5eb39361${ssrIncludeBooleanAttr(Array.isArray(catFilter.value) ? ssrLooseContain(catFilter.value, "") : ssrLooseEqual(catFilter.value, "")) ? " selected" : ""}>全部分类</option><!--[-->`);
ssrRenderList(catOptions.value, (c) => {
_push(`<option${ssrRenderAttr("value", c)} data-v-5eb39361${ssrIncludeBooleanAttr(Array.isArray(catFilter.value) ? ssrLooseContain(catFilter.value, c) : ssrLooseEqual(catFilter.value, c)) ? " selected" : ""}>${ssrInterpolate(c)}</option>`);
});
_push(`<!--]--></select><select class="select select-bordered select-sm w-40" data-v-5eb39361><option value="" data-v-5eb39361${ssrIncludeBooleanAttr(Array.isArray(typeFilter.value) ? ssrLooseContain(typeFilter.value, "") : ssrLooseEqual(typeFilter.value, "")) ? " selected" : ""}>全部类型</option><!--[-->`);
ssrRenderList(typeOptions.value, (t) => {
_push(`<option${ssrRenderAttr("value", t)} data-v-5eb39361${ssrIncludeBooleanAttr(Array.isArray(typeFilter.value) ? ssrLooseContain(typeFilter.value, t) : ssrLooseEqual(typeFilter.value, t)) ? " selected" : ""}>${ssrInterpolate(t)}</option>`);
});
_push(`<!--]--></select><input${ssrRenderAttr("value", kwFilter.value)} class="input input-bordered input-sm w-56" placeholder="搜索标题 / tid / 描述" data-v-5eb39361><span class="ml-auto text-xs text-slate-400" data-v-5eb39361>${ssrInterpolate(cards.value.length)} 个模板 · ${ssrInterpolate(rows.value.length)} 个语言版本 · 命中 ${ssrInterpolate(filteredCards.value.length)}</span></div></div>`);
if (loading.value) {
_push(`<div class="py-16 text-center" data-v-5eb39361><span class="loading loading-spinner loading-md" data-v-5eb39361></span></div>`);
} else if (!filteredCards.value.length) {
_push(`<div class="panel py-16 text-center text-slate-400 text-sm" data-v-5eb39361>${ssrInterpolate(rows.value.length ? "当前筛选下没有匹配的模板" : "暂无模板,点击右上角「新增模板」创建")}</div>`);
} else {
_push(`<div class="tpl-grid" data-v-5eb39361><!--[-->`);
ssrRenderList(filteredCards.value, (c) => {
_push(`<div class="tpl-card" data-v-5eb39361><div class="tc-top" data-v-5eb39361><div class="tc-ico" data-v-5eb39361>`);
if (isImg(c.primary.icon)) {
_push(`<img${ssrRenderAttr("src", c.primary.icon)} alt="" data-v-5eb39361>`);
} else {
_push(`<span data-v-5eb39361>${ssrInterpolate(initials(c))}</span>`);
}
_push(`</div><div class="min-w-0 flex-1" data-v-5eb39361><div class="tc-title"${ssrRenderAttr("title", c.primary.title)} data-v-5eb39361>${ssrInterpolate(c.primary.title || "(未命名)")}</div><div class="tc-tid"${ssrRenderAttr("title", c.tid)} data-v-5eb39361>${ssrInterpolate(c.tid || "无 tid")}</div></div></div><div class="tc-desc" data-v-5eb39361>${ssrInterpolate(c.primary.description || "(无描述)")}</div><div class="tc-tags" data-v-5eb39361>`);
if (c.primary.ttype) {
_push(`<span class="chip chip-type" data-v-5eb39361>${ssrInterpolate(c.primary.ttype)}</span>`);
} else {
_push(`<!---->`);
}
_push(`<!--[-->`);
ssrRenderList(tagList(c.primary.tags), (tag, i) => {
_push(`<span class="chip" data-v-5eb39361>${ssrInterpolate(tag)}</span>`);
});
_push(`<!--]--></div><div class="tc-foot" data-v-5eb39361><span class="chip chip-lang" data-v-5eb39361>${ssrInterpolate(c.langs.length)} 种语言</span>`);
if (!c.hasPrimary) {
_push(`<span class="chip chip-warn" data-v-5eb39361>缺 ${ssrInterpolate(PRIMARY_LANG)},按 ${ssrInterpolate(c.primary.language)} 展示</span>`);
} else {
_push(`<!---->`);
}
_push(`<span class="ml-auto tc-go" data-v-5eb39361>查看 →</span></div></div>`);
});
_push(`<!--]--></div>`);
}
_push(`</div>`);
} else if (view.value === "detail" && current.value) {
_push(`<div data-v-5eb39361><div class="flex flex-wrap items-center gap-3 mb-4" data-v-5eb39361><button class="btn btn-ghost btn-sm" data-v-5eb39361>← 返回列表</button><div class="min-w-0" data-v-5eb39361><h2 class="text-lg font-semibold text-slate-800 truncate" data-v-5eb39361>${ssrInterpolate(form.title || current.value.tid)}</h2><div class="text-xs text-slate-400 font-mono" data-v-5eb39361>${ssrInterpolate(current.value.tid)} · ${ssrInterpolate(current.value.langs.length)} 种语言</div></div><div class="ml-auto flex items-center gap-2" data-v-5eb39361><button class="btn btn-sm" data-v-5eb39361>同步到其他语言</button><button class="btn btn-sm" data-v-5eb39361>+ 添加语言</button><button class="btn btn-ghost btn-sm text-red-500" data-v-5eb39361>删除整组</button></div></div><div class="panel p-3 mb-4" data-v-5eb39361><div class="flex items-center gap-2 flex-wrap" data-v-5eb39361><span class="text-xs text-slate-400 shrink-0" data-v-5eb39361>语言版本</span><!--[-->`);
ssrRenderList(current.value.langs, (l) => {
_push(`<button class="${ssrRenderClass([{ on: l.language === curLang.value, primary: isPrimary(l.language) }, "lang-pill"])}"${ssrRenderAttr("title", unref(langName)(l.language))} data-v-5eb39361>${ssrInterpolate(l.language)} <span class="lp-name" data-v-5eb39361>${ssrInterpolate(unref(langName)(l.language))}</span></button>`);
});
_push(`<!--]--></div></div><div class="panel p-6" data-v-5eb39361>`);
if (detailLoading.value) {
_push(`<div class="py-16 text-center" data-v-5eb39361><span class="loading loading-spinner loading-md" data-v-5eb39361></span></div>`);
} else {
_push(`<form data-v-5eb39361><div class="flex items-center gap-2 mb-4 pb-3 border-b border-slate-100" data-v-5eb39361><span class="badge badge-outline" data-v-5eb39361>${ssrInterpolate(curLang.value)}</span><span class="text-sm text-slate-500" data-v-5eb39361>${ssrInterpolate(unref(langName)(curLang.value))}</span>`);
if (isPrimary(curLang.value)) {
_push(`<span class="chip chip-primary" data-v-5eb39361>主语言</span>`);
} else {
_push(`<!---->`);
}
_push(`<span class="text-xs text-slate-300 font-mono ml-2" data-v-5eb39361>id ${ssrInterpolate(form.id)}</span><div class="ml-auto flex gap-2" data-v-5eb39361><button type="button" class="btn btn-ghost btn-xs text-red-500" data-v-5eb39361> 删除此语言版本 </button></div></div><div class="grid grid-cols-1 md:grid-cols-2 gap-4" data-v-5eb39361><div class="form-control" data-v-5eb39361><label class="label" data-v-5eb39361><span class="label-text font-medium" data-v-5eb39361>标题 *</span></label><input${ssrRenderAttr("value", form.title)} type="text" class="input input-bordered w-full" required data-v-5eb39361></div><div class="form-control" data-v-5eb39361><label class="label" data-v-5eb39361><span class="label-text font-medium" data-v-5eb39361>类型 (ttype)</span></label><input${ssrRenderAttr("value", form.ttype)} type="text" class="input input-bordered w-full" list="ttypelist" data-v-5eb39361><datalist id="ttypelist" data-v-5eb39361><!--[-->`);
ssrRenderList(typeOptions.value, (t) => {
_push(`<option${ssrRenderAttr("value", t)} data-v-5eb39361></option>`);
});
_push(`<!--]--></datalist></div><div class="form-control" data-v-5eb39361><label class="label" data-v-5eb39361><span class="label-text font-medium" data-v-5eb39361>标签 (tags)</span></label><input${ssrRenderAttr("value", form.tags)} type="text" class="input input-bordered w-full" placeholder="逗号分隔" data-v-5eb39361></div><div class="form-control" data-v-5eb39361><label class="label" data-v-5eb39361><span class="label-text font-medium" data-v-5eb39361>图标 (icon)</span></label><input${ssrRenderAttr("value", form.icon)} type="text" class="input input-bordered w-full" placeholder="图标 URL 或名称" data-v-5eb39361></div></div><div class="form-control mt-4" data-v-5eb39361><label class="label" data-v-5eb39361><span class="label-text font-medium" data-v-5eb39361>描述</span></label><input${ssrRenderAttr("value", form.description)} type="text" class="input input-bordered w-full" data-v-5eb39361></div><div class="form-control mt-4" data-v-5eb39361><label class="label" data-v-5eb39361><span class="label-text font-medium" data-v-5eb39361>大纲 (outline)</span></label><textarea class="textarea textarea-bordered w-full font-mono text-sm" rows="5" data-v-5eb39361>${ssrInterpolate(form.outline)}</textarea></div><div class="form-control mt-4" data-v-5eb39361><label class="label" data-v-5eb39361><span class="label-text font-medium" data-v-5eb39361>模板内容 (template)</span></label><textarea class="textarea textarea-bordered w-full font-mono text-sm" rows="16" data-v-5eb39361>${ssrInterpolate(form.template)}</textarea></div><div class="flex gap-3 mt-6" data-v-5eb39361><button type="submit" class="btn btn-primary text-white"${ssrIncludeBooleanAttr(submitting.value) ? " disabled" : ""} data-v-5eb39361>保存当前语言</button><button type="button" class="btn btn-ghost"${ssrIncludeBooleanAttr(submitting.value) ? " disabled" : ""} data-v-5eb39361>放弃修改</button></div></form>`);
}
_push(`</div></div>`);
} else {
_push(`<!---->`);
}
if (syncOpen.value) {
_push(`<div class="modal modal-open" data-v-5eb39361><div class="modal-box max-w-2xl" data-v-5eb39361><h3 class="font-bold text-base mb-1" data-v-5eb39361>同步到其他语言</h3><p class="text-xs text-slate-400 mb-4" data-v-5eb39361> 以 <span class="font-mono" data-v-5eb39361>${ssrInterpolate(curLang.value)}</span> 版本为源,把勾选字段覆盖写入所选语言版本。 标题/描述/大纲/模板属译文,覆盖后目标语言的译文会丢失,请谨慎勾选。 </p><div class="mb-4" data-v-5eb39361><div class="text-sm font-medium text-slate-600 mb-2" data-v-5eb39361>同步哪些字段</div><div class="flex flex-wrap gap-x-5 gap-y-2" data-v-5eb39361><!--[-->`);
ssrRenderList(SYNC_FIELDS, (f) => {
_push(`<label class="flex items-center gap-2 text-sm cursor-pointer" data-v-5eb39361><input${ssrIncludeBooleanAttr(Array.isArray(syncFields.value) ? ssrLooseContain(syncFields.value, f.key) : syncFields.value) ? " checked" : ""} type="checkbox"${ssrRenderAttr("value", f.key)} class="checkbox checkbox-sm checkbox-primary" data-v-5eb39361><span data-v-5eb39361>${ssrInterpolate(f.label)}</span>`);
if (f.translated) {
_push(`<span class="chip chip-warn" data-v-5eb39361>译文</span>`);
} else {
_push(`<!---->`);
}
_push(`</label>`);
});
_push(`<!--]--></div></div><div data-v-5eb39361><div class="flex items-center gap-2 mb-2" data-v-5eb39361><span class="text-sm font-medium text-slate-600" data-v-5eb39361>同步到哪些语言</span><span class="text-xs text-slate-400" data-v-5eb39361>已选 ${ssrInterpolate(syncLangs.value.length)} / ${ssrInterpolate(otherLangs.value.length)}</span><button type="button" class="btn btn-ghost btn-xs ml-auto" data-v-5eb39361>全选</button><button type="button" class="btn btn-ghost btn-xs" data-v-5eb39361>清空</button></div><div class="lang-picker" data-v-5eb39361><!--[-->`);
ssrRenderList(otherLangs.value, (l) => {
_push(`<label class="lang-opt" data-v-5eb39361><input${ssrIncludeBooleanAttr(Array.isArray(syncLangs.value) ? ssrLooseContain(syncLangs.value, l) : syncLangs.value) ? " checked" : ""} type="checkbox"${ssrRenderAttr("value", l)} class="checkbox checkbox-xs checkbox-primary" data-v-5eb39361><span class="lo-code" data-v-5eb39361>${ssrInterpolate(l)}</span><span class="lo-name" data-v-5eb39361>${ssrInterpolate(unref(langName)(l))}</span></label>`);
});
_push(`<!--]--></div></div><div class="modal-action" data-v-5eb39361><button class="btn btn-ghost" data-v-5eb39361>取消</button><button class="btn btn-primary text-white"${ssrIncludeBooleanAttr(submitting.value || !syncFields.value.length || !syncLangs.value.length) ? " disabled" : ""} data-v-5eb39361> 同步到 ${ssrInterpolate(syncLangs.value.length)} 个语言 </button></div></div></div>`);
} else {
_push(`<!---->`);
}
if (addLangOpen.value) {
_push(`<div class="modal modal-open" data-v-5eb39361><div class="modal-box max-w-lg" data-v-5eb39361><h3 class="font-bold text-base mb-1" data-v-5eb39361>为「${ssrInterpolate(current.value?.tid)}」添加语言版本</h3><p class="text-xs text-slate-400 mb-4" data-v-5eb39361> 新版本以当前 <span class="font-mono" data-v-5eb39361>${ssrInterpolate(curLang.value)}</span> 版本的内容为初稿,创建后再逐项译成目标语言。 </p><div class="form-control" data-v-5eb39361><label class="label" data-v-5eb39361><span class="label-text font-medium" data-v-5eb39361>语言码 *</span></label><input${ssrRenderAttr("value", newLang.value)} class="input input-bordered w-full" list="addlanglist" placeholder="BCP-47,如 en-US" data-v-5eb39361><datalist id="addlanglist" data-v-5eb39361><!--[-->`);
ssrRenderList(addableLangs.value, (o) => {
_push(`<option${ssrRenderAttr("value", o.code)} data-v-5eb39361>${ssrInterpolate(o.name)}</option>`);
});
_push(`<!--]--></datalist>`);
if (newLangErr.value) {
_push(`<span class="text-xs text-rose-600 mt-1" data-v-5eb39361>${ssrInterpolate(newLangErr.value)}</span>`);
} else {
_push(`<!---->`);
}
_push(`</div><div class="modal-action" data-v-5eb39361><button class="btn btn-ghost" data-v-5eb39361>取消</button><button class="btn btn-primary text-white"${ssrIncludeBooleanAttr(submitting.value) ? " disabled" : ""} data-v-5eb39361>创建</button></div></div></div>`);
} else {
_push(`<!---->`);
}
if (createOpen.value) {
_push(`<div class="modal modal-open" data-v-5eb39361><div class="modal-box max-w-lg" data-v-5eb39361><h3 class="font-bold text-base mb-1" data-v-5eb39361>新增模板</h3><p class="text-xs text-slate-400 mb-4" data-v-5eb39361> 先建出主语言版本,创建后在详情页补齐内容,再用「添加语言」扩展其他语言。 </p><div class="form-control" data-v-5eb39361><label class="label" data-v-5eb39361><span class="label-text font-medium" data-v-5eb39361>模板标识 tid *</span></label><input${ssrRenderAttr("value", createTid.value)} class="input input-bordered w-full" placeholder="如 Meeting-WeeklyMeeting" data-v-5eb39361></div><div class="form-control mt-3" data-v-5eb39361><label class="label" data-v-5eb39361><span class="label-text font-medium" data-v-5eb39361>标题 *</span></label><input${ssrRenderAttr("value", createTitle.value)} class="input input-bordered w-full" data-v-5eb39361></div><div class="form-control mt-3" data-v-5eb39361><label class="label" data-v-5eb39361><span class="label-text font-medium" data-v-5eb39361>语言</span></label><input${ssrRenderAttr("value", createLang.value)} class="input input-bordered w-full" list="addlanglist" data-v-5eb39361></div>`);
if (createErr.value) {
_push(`<span class="text-xs text-rose-600 mt-2 block" data-v-5eb39361>${ssrInterpolate(createErr.value)}</span>`);
} else {
_push(`<!---->`);
}
_push(`<div class="modal-action" data-v-5eb39361><button class="btn btn-ghost" data-v-5eb39361>取消</button><button class="btn btn-primary text-white"${ssrIncludeBooleanAttr(submitting.value) ? " disabled" : ""} data-v-5eb39361>创建</button></div></div></div>`);
} else {
_push(`<!---->`);
}
_push(`</div>`);
};
}
});
const _sfc_setup = _sfc_main.setup;
_sfc_main.setup = (props, ctx) => {
const ssrContext = useSSRContext();
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("pages/meettemplates.vue");
return _sfc_setup ? _sfc_setup(props, ctx) : void 0;
};
const meettemplates = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-5eb39361"]]);
export { meettemplates as default };
//# sourceMappingURL=meettemplates-BbY84yBS.mjs.map