From 88fafe076b74166ee0fdc4efef329bcf6de44123 Mon Sep 17 00:00:00 2001 From: A1300399510 <1300399510@qq.com> Date: Fri, 12 Dec 2025 00:45:06 +0800 Subject: [PATCH] no message --- js/publish_admin.js | 285 +++++++------------------------------------- publish_admin.html | 79 ++---------- 2 files changed, 53 insertions(+), 311 deletions(-) diff --git a/js/publish_admin.js b/js/publish_admin.js index 71192b2..68dfe60 100644 --- a/js/publish_admin.js +++ b/js/publish_admin.js @@ -3,7 +3,7 @@ const { createApp, ref, computed, onMounted, nextTick, onUnmounted } = Vue; const editApp = createApp({ setup() { - const LANG = location.href.indexOf("lang=en") > 0 ? "en" : "zh-CN"; + const { Editor, FileUploader } = window.textbus; const title = ref(""); const saveStatus = ref(""); @@ -75,20 +75,16 @@ const editApp = createApp({ // 提交 const submit = (status) => { const infoTarget = { ...info.value } || {}; - - // 获取 TextBus 内容 - // TextBus 1.0: editor.getContents().content - // Fallback to generic HTML retrieval if needed + // 获取 HTML 内容 let content = ""; - if (editor && typeof editor.getContents === 'function') { - const contents = editor.getContents(); - content = (typeof contents === 'string') ? contents : (contents.content || ""); - } else if (editor && typeof editor.getHTML === 'function') { + if (editor && typeof editor.getHTML === 'function') { content = editor.getHTML(); + } else if (editor && editor.output) { + content = editor.output.content; // Fallback if getHTML isn't direct } - // 临时创建一个 div 来解析 content 中的图片和视频 - const tempDiv = document.createElement('div'); + // 创建临时 DOM 用于提取图片和视频 + const tempDiv = document.createElement("div"); tempDiv.innerHTML = content; const images = extractImages(tempDiv); @@ -152,7 +148,6 @@ const editApp = createApp({ } const infoTarget = data.info || {}; - // if (infoTarget.content) infoTarget.content = `
2026年度研究生课程火热招生中!
\n2026年度研究生课程火热招生中!
` info.value = infoTarget; token.value = data.token; @@ -230,250 +225,54 @@ const editApp = createApp({ }); }; - const initEditor = () => { - if (!window.textbus) { - console.error("TextBus is not loaded"); - return; + // 自定义上传适配器 + class CustomUploader extends FileUploader { + uploadFile(type, file) { + // type 可能是 'image' 或 'video' 等,取决于调用方 + // uploading 函数接受 (file, name, type) + return uploading(file, file.name, type).then(res => { + // 构造带 aid 的 url + return `${res.url}?aid=${res.aid}`; + }); } + } + const initEditor = () => { const editorConfig = { content: info.value?.content || "", - // 配置工具栏:去除音频、源码、组件库、插入段落 - // 根据 TextBus 文档,配置 providers.provide 可以在一定程度上控制工具栏, - // 但对于标准版 @textbus/editor,最直接的方式是使用 toolbar 配置项(如果支持) - // 或者通过 CSS 隐藏不需要的按钮(作为兜底方案,因为 CDN 版本配置灵活性有限) - - // 尝试配置工具栏项,仅保留需要的 - // 注意:TextBus 的工具栏配置键名可能需要根据具体版本调整 - // 下面是一个常见的 TextBus 工具栏配置示例 - toolbar: [ - ['undo', 'redo'], - ['bold', 'italic', 'underline', 'strikeThrough'], - ['heading'], - ['ol', 'ul'], - ['fontSize', 'fontFamily', 'color', 'backgroundColor'], - ['image'], // 先放图片 - ['video'], // 再放视频 - ['link', 'unlink'], - ['textAlign', 'textIndent'], - ['table'], - // ['clean'], // 去掉清除格式 - // ['source'], // 去掉源码 - // ['audio'], // 去掉音频 (TextBus 默认可能有也可能没有,这里显式不加) - // ['block'], // 去掉插入段落/组件库 - ], - - uploader: function (type) { - return new Promise((resolve, reject) => { - // 1. 数量限制检查 - let content = ""; - if (editor && typeof editor.getContents === 'function') { - const contents = editor.getContents(); - content = (typeof contents === 'string') ? contents : (contents.content || ""); - } else if (editor && typeof editor.getHTML === 'function') { - content = editor.getHTML(); - } - - const tempDiv = document.createElement('div'); - tempDiv.innerHTML = content; - - if (type === 'image') { - const currentImages = tempDiv.querySelectorAll('img').length; - if (currentImages >= imageLength) { - creationAlertBox("error", `最多只能上传 ${imageLength} 张图片`); - return; - } - } else if (type === 'video') { - const currentVideos = tempDiv.querySelectorAll('video').length; - if (currentVideos >= videoLength) { - creationAlertBox("error", `最多只能上传 ${videoLength} 个视频`); - return; - } - } - - // 视频上传逻辑 - if (type === 'video') { - const fileInput = document.createElement("input"); - fileInput.setAttribute("type", "file"); - fileInput.setAttribute("accept", "video/*"); - fileInput.style.cssText = "position: absolute; left: -9999px; top: -9999px; opacity: 0"; - - document.body.appendChild(fileInput); - - fileInput.onchange = async (e) => { - const file = e.target.files[0]; - if (!file) return; - - // 大小限制检查 (视频通常允许更大,这里暂时统一限制,如需单独限制请调整) - const maxSize = 1 * 1024 * 1024; // 1M - if (file.size > maxSize) { - creationAlertBox("error", "文件大小不能超过 1MB"); - document.body.removeChild(fileInput); - return; - } - - try { - // 1. 上传视频文件 - const res = await uploading(file, file.name, "video"); - const videoUrl = res.url + (res.aid ? `?aid=${res.aid}` : ''); - - // 2. 尝试获取封面 (可选) - // TextBus 插入视频通常只需要 URL,或者 { src, poster } - // 由于 uploading 返回的是 URL,我们直接返回 - resolve(videoUrl); - - } catch (err) { - console.error(err); - } finally { - document.body.removeChild(fileInput); - } - }; - - fileInput.click(); - return; - } - - const fileInput = document.createElement("input"); - fileInput.setAttribute("type", "file"); - fileInput.setAttribute("accept", "image/png, image/jpeg, image/jpg, image/gif"); - fileInput.style.cssText = "position: absolute; left: -9999px; top: -9999px; opacity: 0"; - - document.body.appendChild(fileInput); - - fileInput.onchange = async (e) => { - const file = e.target.files[0]; - if (!file) return; - - // 2. 大小限制检查 - const maxSize = 1 * 1024 * 1024; // 1M - if (file.size > maxSize) { - creationAlertBox("error", "文件大小不能超过 1MB"); - document.body.removeChild(fileInput); - return; - } - - try { - const res = await uploading(file, file.name, "image"); - // TextBus 期望返回 URL - resolve(res.url + (res.aid ? `?aid=${res.aid}` : '')); - } catch (err) { - console.error(err); - // reject(err); // TextBus 可能会捕获错误并提示 - } finally { - document.body.removeChild(fileInput); - } - }; - - fileInput.click(); - }); - } + providers: [{ + provide: FileUploader, + useFactory: () => new CustomUploader() + }], + // 默认情况下,xnote 使用悬浮/气泡菜单 + // 我们不配置 toolbar 容器,让其使用默认行为 }; try { - // 查找 createEditor 函数 - let createEditor = null; - if (window.textbus && typeof window.textbus.createEditor === 'function') { - createEditor = window.textbus.createEditor; - } else if (window.textbus && window.textbus.editor && typeof window.textbus.editor.createEditor === 'function') { - createEditor = window.textbus.editor.createEditor; - } else if (window.textbus && window.textbus.default && typeof window.textbus.default.createEditor === 'function') { - createEditor = window.textbus.default.createEditor; - } - - if (!createEditor) { - console.error("TextBus createEditor not found", window.textbus); - creationAlertBox("error", "TextBus 初始化失败: createEditor 未找到"); - return; - } - - // 初始化编辑器 - // 根据源码推测:createEditor(config) 返回 editor 实例,然后调用 mount(selector) - editor = createEditor(editorConfig); + editor = new Editor(editorConfig); + editor.mount(document.getElementById("editor-text-area")); - if (editor && typeof editor.mount === 'function') { - editor.mount("#editor-text-area"); - - // 手动隐藏不需要的工具栏按钮 (JS 兜底方案) - setTimeout(() => { - const hideButtons = () => { - const buttons = document.querySelectorAll('.textbus-toolbar-btn, .textbus-btn, button'); - const targets = ['音频', '插入音频', 'Audio', '源代码', '查看源码', 'Source', '组件库', 'Components', '段落', '插入段落', 'Paragraph', '清除格式', 'Clean', '格式化', 'Format', '格式刷', 'Brush']; - - let imageBtn = null; - let videoBtn = null; - - buttons.forEach(btn => { - const title = btn.getAttribute('title') || btn.getAttribute('aria-label') || ''; - if (targets.some(t => title.includes(t))) { - btn.style.display = 'none'; - } - // 备用:检查图标 class - if (btn.querySelector('.textbus-icon-music') || - btn.querySelector('.textbus-icon-code') || - btn.querySelector('.textbus-icon-components') || - btn.querySelector('.textbus-icon-paragraph') || - btn.querySelector('.textbus-icon-clean') // 清除格式 - ) { - btn.style.display = 'none'; - } - - // 单独处理格式刷图标(通常是刷子形状) - if (btn.querySelector('.textbus-icon-brush')) { - btn.style.display = 'none'; - } - - // 查找图片和视频按钮 - if (title.includes('图片') || title.includes('Image') || btn.querySelector('.textbus-icon-image')) { - imageBtn = btn; - } - if (title.includes('视频') || title.includes('Video') || btn.querySelector('.textbus-icon-video')) { - videoBtn = btn; - } - }); - - // 强制调整顺序:视频放在图片后面 - if (imageBtn && videoBtn && imageBtn.parentNode === videoBtn.parentNode) { - // 如果视频按钮不在图片按钮的紧邻后面,则移动 - if (imageBtn.nextElementSibling !== videoBtn) { - imageBtn.parentNode.insertBefore(videoBtn, imageBtn.nextElementSibling); - } - } - }; - - hideButtons(); - // 监听 DOM 变化以防重新渲染 - const observer = new MutationObserver(hideButtons); - const toolbar = document.querySelector('.textbus-toolbar') || document.querySelector('.textbus-ui-top'); - if (toolbar) { - observer.observe(toolbar, { childList: true, subtree: true }); - } - }, 100); - - } else { - // 兼容旧版本或直接传入 selector 的情况 - // 如果 createEditor 返回的不是带有 mount 的对象,可能是旧版本 - console.warn("Editor instance does not have mount method, assuming auto-mount or different API"); - } - - if (editor && editor.onChange) { + // 监听内容变化 + if (editor.onChange) { editor.onChange.subscribe(() => { saveStatus.value = "有未保存的更改"; }); } } catch (error) { - console.log("TextBus init error", error); - creationAlertBox("error", "TextBus 初始化异常: " + error.message); + console.log("error", error); } - // 点击空白处 focus 编辑器 (TextBus 可能不需要这个,但保留逻辑以防万一) - // document.getElementById("editor-text-area").addEventListener("click", (e) => { - // if (e.target.id === "editor-text-area") { - // // editor.focus(); - // } - // }); + // 点击空白处 focus 编辑器 + document.getElementById("editor-text-area").addEventListener("click", (e) => { + // 如果点击的是容器本身(空白处),则聚焦 + if (e.target.id === "editor-text-area") { + // editor.focus() 如果存在 + // Textbus editor 实例通常不需要手动 focus,除非是 command + } + }); }; - // 提取视频第一帧作为封面 (保留辅助函数) + // 提取视频第一帧作为封面 const getVideoFirstFrame = (file) => { return new Promise((resolve) => { const video = document.createElement("video"); @@ -510,10 +309,9 @@ const editApp = createApp({ }); onUnmounted(() => { - if (editor && typeof editor.destroy === 'function') { - editor.destroy(); - editor = null; - } + if (editor == null) return; + if (editor.destroy) editor.destroy(); + editor = null; }); return { @@ -527,4 +325,3 @@ const editApp = createApp({ }, }); editApp.mount("#edit"); - diff --git a/publish_admin.html b/publish_admin.html index bdaa504..1c2edb4 100644 --- a/publish_admin.html +++ b/publish_admin.html @@ -1,3 +1,4 @@ + @@ -8,55 +9,13 @@ 发布主题 - - - - + + + +