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 @@
发布主题
-
-
-
-
+
+
+
+