diff --git a/assets/css/frontend/pages/submit-editor.css b/assets/css/frontend/pages/submit-editor.css new file mode 100644 index 00000000..2303712e --- /dev/null +++ b/assets/css/frontend/pages/submit-editor.css @@ -0,0 +1,44 @@ +/* Content shown inside the front-submit TinyMCE iframe. */ +.tinymce-hide { + position: relative; + margin: 18px 0; + padding: 38px 14px 14px; + border: 1px dashed rgba(240, 68, 148, 0.35); + border-radius: 6px; + background: rgba(240, 68, 148, 0.04); +} + +.tinymce-hide > .hide-before, +.tinymce-hide > .hide-after { + margin: 0; + color: #f04494; + font-family: Consolas, Monaco, monospace; + font-size: 12px; + line-height: 1.5; +} + +.tinymce-hide > .hide-before { + position: absolute; + top: 10px; + left: 14px; +} + +.tinymce-hide > .hide-after { + margin-top: 8px; +} + +.tinymce-hide > [contenteditable="true"] { + min-height: 38px; + padding: 10px; + border-radius: 4px; + background: rgba(255, 255, 255, 0.75); + outline: 0; +} + +.tinymce-hide > [contenteditable="true"] > :first-child { + margin-top: 0; +} + +.tinymce-hide > [contenteditable="true"] > :last-child { + margin-bottom: 0; +} diff --git a/assets/css/frontend/pages/submit.css b/assets/css/frontend/pages/submit.css index bf4f5e2b..2c7ea2d4 100644 --- a/assets/css/frontend/pages/submit.css +++ b/assets/css/frontend/pages/submit.css @@ -117,16 +117,170 @@ opacity: 1; } -.newposts-editor-tools { +.aksrt-editor-media { + position: fixed; + z-index: 100080; + inset: 0; display: flex; - flex-wrap: wrap; - gap: 6px; - padding: 8px 0 0; + align-items: center; + justify-content: center; + padding: 10px; } -.newposts-editor-tools .but { - padding: 4px 9px; +.aksrt-editor-media__backdrop { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + padding: 0; + border: 0; + background: rgba(0, 0, 0, 0.45); + cursor: default; +} + +.aksrt-editor-media__dialog { + position: relative; + z-index: 1; + width: min(620px, calc(100vw - 20px)); + max-height: calc(100vh - 20px); + overflow: hidden auto; + border-radius: var(--main-radius, 8px); + background: var(--main-bg-color, #fff); + box-shadow: 0 18px 60px rgba(0, 0, 0, 0.2); +} + +.aksrt-editor-media__close { + position: absolute; + z-index: 2; + top: 8px; + right: 10px; + width: 34px; + height: 34px; + padding: 0; + border: 0; + background: transparent; + color: var(--muted-color, #777); + font-size: 26px; + line-height: 34px; + cursor: pointer; +} + +.aksrt-editor-media__title { + margin: 0; + padding: 18px 54px 14px 20px; + font-size: 17px; + line-height: 1.4; +} + +.aksrt-editor-media__tabs { + display: flex; + gap: 24px; + padding: 0 20px; + border-bottom: 1px solid var(--main-border-color, rgba(50, 50, 50, 0.08)); +} + +.aksrt-editor-media__tabs button { + position: relative; + padding: 10px 2px 12px; + border: 0; + background: transparent; + color: var(--muted-color, #777); + font-size: 14px; + cursor: pointer; +} + +.aksrt-editor-media__tabs button.is-active { + color: var(--focus-color, #f04494); +} + +.aksrt-editor-media__tabs button.is-active::after { + position: absolute; + right: 0; + bottom: -1px; + left: 0; + height: 2px; + border-radius: 2px; + background: currentColor; + content: ''; +} + +.aksrt-editor-media__panel { + display: none; + padding: 20px; +} + +.aksrt-editor-media__panel.is-active { + display: block; +} + +.aksrt-editor-media__panel > p { + margin: 0 0 10px; + color: var(--muted-color, #777); + font-size: 13px; +} + +.aksrt-editor-media__panel textarea { + width: 100%; + min-height: 120px; + resize: vertical; +} + +.aksrt-editor-media__drop { + display: flex; + min-height: 180px; + align-items: center; + justify-content: center; + flex-direction: column; + gap: 8px; + padding: 20px; + border: 2px dashed var(--main-border-color, rgba(50, 50, 50, 0.12)); + border-radius: var(--main-radius, 8px); + background: var(--muted-border-color, rgba(50, 50, 50, 0.03)); + color: var(--muted-color, #777); + text-align: center; + cursor: pointer; + transition: 0.2s; +} + +.aksrt-editor-media__drop:hover, +.aksrt-editor-media__drop.is-dragging { + border-color: var(--focus-color, #f04494); + background: rgba(240, 68, 148, 0.06); + color: var(--focus-color, #f04494); +} + +.aksrt-editor-media__drop input { + position: absolute; + width: 1px; + height: 1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); +} + +.aksrt-editor-media__drop i { + font-size: 34px; +} + +.aksrt-editor-media__drop span { font-size: 12px; + opacity: 0.75; +} + +.aksrt-editor-media__selection { + min-height: 20px; + margin-top: 10px; + color: var(--muted-color, #777); + font-size: 13px; +} + +.aksrt-editor-media__submit { + display: block; + min-width: 110px; + margin: 14px 0 0 auto; +} + +body.aksrt-editor-media-open { + overflow: hidden; } .featured-edit { @@ -402,6 +556,21 @@ margin: 2px; } + .aksrt-editor-media { + align-items: flex-end; + padding: 0; + } + + .aksrt-editor-media__dialog { + width: 100%; + max-height: 88vh; + border-radius: 12px 12px 0 0; + } + + .aksrt-editor-media__drop { + min-height: 150px; + } + .pay-dawnload-edit .lists-item { align-items: stretch; flex-direction: column; diff --git a/assets/js/front-submit-editor.js b/assets/js/front-submit-editor.js new file mode 100644 index 00000000..4e97232e --- /dev/null +++ b/assets/js/front-submit-editor.js @@ -0,0 +1,362 @@ +/** + * Front-submit TinyMCE tools matching the reference article editor. + * + * @package AKSRT + */ +(function (window, document) { + 'use strict'; + + if (!window.tinymce || !window.tinymce.PluginManager) { + return; + } + + var cfg = window.aksrtSubmitData || {}; + var editorCfg = cfg.editor || {}; + var i18n = cfg.i18n || {}; + var activeModal = null; + + function notify(message, type, duration, id) { + if (typeof window.notyf === 'function') { + window.notyf( + message, + type, + duration === undefined ? 5 : duration, + id || 'aksrt-front-submit-editor-image-toast' + ); + } + } + + function escapeAttr(value) { + return String(value) + .replace(/&/g, '&') + .replace(/"/g, '"') + .replace(//g, '>'); + } + + function validImageUrl(value) { + var raw = String(value).trim(); + if (!/^https?:\/\//i.test(raw)) { + return ''; + } + try { + var url = new URL(raw); + return url.protocol === 'http:' || url.protocol === 'https:' ? url.href : ''; + } catch (error) { + return ''; + } + } + + function imageMarkup(url, attachmentId, alt) { + var safeUrl = validImageUrl(url); + if (!safeUrl) { + return ''; + } + var idAttr = attachmentId ? ' data-edit-file-id="' + Number(attachmentId) + '"' : ''; + var altAttr = alt ? ' alt="' + escapeAttr(alt) + '"' : ' alt=""'; + return '

'; + } + + function closeImageModal() { + if (!activeModal) { + return; + } + document.removeEventListener('keydown', activeModal.onKeydown); + activeModal.root.remove(); + activeModal = null; + document.body.classList.remove('aksrt-editor-media-open'); + } + + function uploadImage(file) { + var upload = (cfg.uploads || {}).image || null; + if (!upload || !upload.url) { + return Promise.reject(new Error(i18n.uploadUnavailable || '当前账号不能上传图片')); + } + if (!file || !/^image\//.test(file.type || '')) { + return Promise.reject(new Error(i18n.invalidImage || '请选择有效的图片文件')); + } + if (upload.maxSize && file.size > upload.maxSize) { + return Promise.reject(new Error(i18n.fileTooLarge || '文件大小超过限制')); + } + + var body = new FormData(); + body.append('file', file); + return fetch(upload.url, { + method: 'POST', + credentials: 'same-origin', + headers: { 'X-WP-Nonce': cfg.restNonce || '' }, + body: body, + }).then(function (response) { + return response.json().catch(function () { + return {}; + }).then(function (payload) { + if (!response.ok || !payload || !payload.success || !payload.url) { + throw new Error((payload && payload.message) || i18n.error || '图片上传失败'); + } + return payload; + }); + }); + } + + function createImageModal(editor) { + closeImageModal(); + + var canUpload = !!editorCfg.canUploadImage && !!((cfg.uploads || {}).image); + var root = document.createElement('div'); + root.className = 'aksrt-editor-media'; + root.setAttribute('role', 'dialog'); + root.setAttribute('aria-modal', 'true'); + root.setAttribute('aria-labelledby', 'aksrt-editor-media-title'); + root.innerHTML = + '' + + '
' + + '' + + '

' + + escapeAttr(i18n.insertImage || '插入图片') + '

' + + '
' + + (canUpload + ? '' + : '') + + '' + + '
' + + '
' + + (canUpload + ? '
' + + '' + + '
' + + '' + + '
' + : '') + + '
' + + '

' + escapeAttr(i18n.externalImageHint || '每行填写一个图片地址:') + '

' + + '' + + '' + + '
' + + '
' + + '
'; + + document.body.appendChild(root); + document.body.classList.add('aksrt-editor-media-open'); + + var fileInput = root.querySelector('[data-editor-media-files]'); + var uploadButton = root.querySelector('[data-editor-media-upload]'); + var selection = root.querySelector('[data-editor-media-selection]'); + var drop = root.querySelector('.aksrt-editor-media__drop'); + var files = []; + + function showSelection() { + if (!selection || !uploadButton) { + return; + } + selection.textContent = files.length + ? (i18n.selectedImages || '已选择 %d 张图片').replace('%d', String(files.length)) + : ''; + uploadButton.disabled = files.length === 0; + } + + function setFiles(nextFiles) { + files = Array.prototype.filter.call(nextFiles || [], function (file) { + return /^image\//.test(file.type || ''); + }); + showSelection(); + } + + root.addEventListener('click', function (event) { + var close = event.target.closest('[data-editor-media-close]'); + if (close) { + event.preventDefault(); + closeImageModal(); + return; + } + + var tab = event.target.closest('[data-editor-media-tab]'); + if (tab) { + var name = tab.getAttribute('data-editor-media-tab'); + root.querySelectorAll('[data-editor-media-tab]').forEach(function (item) { + var active = item === tab; + item.classList.toggle('is-active', active); + item.setAttribute('aria-selected', active ? 'true' : 'false'); + }); + root.querySelectorAll('[data-editor-media-panel]').forEach(function (panel) { + panel.classList.toggle('is-active', panel.getAttribute('data-editor-media-panel') === name); + }); + return; + } + + if (event.target.closest('[data-editor-media-external]')) { + var textarea = root.querySelector('[data-editor-media-urls]'); + var urls = String(textarea ? textarea.value : '').split(/\r?\n/); + var html = urls.map(function (url) { + return imageMarkup(url, 0, ''); + }).join(''); + if (!html) { + notify(i18n.invalidImageUrl || '请填写有效的图片地址', 'warning'); + return; + } + editor.insertContent(html + '

'); + editor.nodeChanged(); + closeImageModal(); + return; + } + + if (event.target.closest('[data-editor-media-upload]')) { + if (!files.length || !uploadButton) { + return; + } + uploadButton.disabled = true; + uploadButton.textContent = i18n.uploading || '正在上传…'; + notify(i18n.uploading || '正在上传正文图片…', 'load', ''); + Promise.all(files.map(uploadImage)).then(function (items) { + var html = items.map(function (item) { + return imageMarkup(item.url, item.attachment_id, item.name || ''); + }).join(''); + if (!html) { + throw new Error(i18n.error || '图片上传失败'); + } + editor.insertContent(html + '

'); + editor.nodeChanged(); + notify(i18n.uploaded || '正文图片上传成功', 'success'); + closeImageModal(); + }).catch(function (error) { + notify(error.message || i18n.error || '正文图片上传失败', 'danger'); + uploadButton.disabled = false; + uploadButton.textContent = i18n.confirmUpload || '确认上传'; + }); + } + }); + + if (fileInput) { + fileInput.addEventListener('change', function () { + setFiles(fileInput.files); + }); + } + + if (drop) { + ['dragenter', 'dragover'].forEach(function (name) { + drop.addEventListener(name, function (event) { + event.preventDefault(); + drop.classList.add('is-dragging'); + }); + }); + ['dragleave', 'drop'].forEach(function (name) { + drop.addEventListener(name, function (event) { + event.preventDefault(); + drop.classList.remove('is-dragging'); + }); + }); + drop.addEventListener('drop', function (event) { + setFiles(event.dataTransfer && event.dataTransfer.files); + }); + } + + var onKeydown = function (event) { + if (event.key === 'Escape') { + closeImageModal(); + } + }; + activeModal = { root: root, onKeydown: onKeydown }; + document.addEventListener('keydown', onKeydown); + var focusTarget = root.querySelector('[data-editor-media-tab].is-active'); + if (focusTarget) { + focusTarget.focus(); + } + } + + function selectedContent(editor) { + var content = String(editor.selection.getContent() || '').trim(); + return content ? '

' + content + '

' : '

' + escapeAttr(i18n.hiddenPlaceholder || '隐藏内容') + '

'; + } + + function insertHiddenContent(editor, item) { + var node = editor.selection.getNode(); + var content = selectedContent(editor); + var suffix = '

'; + if (node && editor.dom.hasClass(node, 'tinymce-hide')) { + var editable = node.querySelector('[contenteditable="true"]'); + content = editable ? editable.innerHTML : content; + editor.dom.remove(node); + suffix = ''; + } + + editor.insertContent( + '
' + + '

' + escapeAttr(item.open) + '

' + + '
' + content + '
' + + '

' + escapeAttr(item.close) + '

' + + '
' + suffix + ); + editor.nodeChanged(); + } + + window.tinymce.PluginManager.add('aksrt_editor_tools', function (editor) { + if (editorCfg.canUploadImage) { + editor.addButton('aksrt_image', { + text: '', + icon: 'image', + tooltip: i18n.insertImage || '插入图片', + onclick: function () { + createImageModal(editor); + }, + }); + } + + if (editorCfg.canHide) { + var menu = [ + { + text: i18n.replyVisible || '回复可见', + type: 'reply', + open: '[reply]', + close: '[/reply]', + }, + { + text: i18n.loginVisible || '登录可见', + type: 'login', + open: '[aksrt_hide mode="login"]', + close: '[/aksrt_hide]', + }, + { + text: i18n.vipVisible || 'VIP 可见', + type: 'vip', + open: '[vip]', + close: '[/vip]', + }, + ]; + if (editorCfg.canPay) { + menu.push({ + text: i18n.payVisible || '付费可见', + type: 'payshow', + open: '[payshow]', + close: '[/payshow]', + }); + } + menu = menu.map(function (item) { + return { + text: item.text, + onclick: function () { + insertHiddenContent(editor, item); + }, + }; + }); + editor.addButton('aksrt_hide', { + text: '', + icon: 'preview', + tooltip: i18n.hiddenContent || '隐藏内容', + type: 'menubutton', + stateSelector: '.tinymce-hide', + menu: menu, + }); + } + }); +})(window, document); diff --git a/assets/js/front-submit.js b/assets/js/front-submit.js index 175832b7..ccee267c 100644 --- a/assets/js/front-submit.js +++ b/assets/js/front-submit.js @@ -32,21 +32,6 @@ return ta ? ta.value : ''; } - function insertEditorContent(html) { - if (window.tinymce) { - var ed = window.tinymce.get('post_content'); - if (ed) { - ed.focus(); - ed.insertContent(html); - return; - } - } - var ta = qs('#post_content') || qs('[name="post_content"]'); - if (ta) { - ta.value += html; - } - } - function serializeForm(form) { var data = {}; var fd = new FormData(form); @@ -106,7 +91,6 @@ this.bindDraftDelete(); this.bindFeatured(); this.bindPay(); - this.bindShortcodes(); this.bindLeaveWarning(); this.bindAi(); this.bindTagActive(); @@ -659,41 +643,6 @@ } }, - bindShortcodes: function () { - document.addEventListener('click', function (e) { - var btn = e.target.closest('[data-insert-shortcode]'); - if (!btn) { - return; - } - e.preventDefault(); - if (btn.hasAttribute('data-insert-upload-image')) { - var toastId = 'aksrt-front-submit-editor-image-toast'; - App.pickUpload('image', 'image/*').then(function (file) { - if (!file) return null; - notyf(i18n.uploading || '正在上传正文图片…', 'load', '', toastId); - return App.uploadFile('image', file); - }).then(function (file) { - if (!file || !file.url) return; - insertEditorContent(''); - notyf(i18n.uploaded || '正文图片上传成功', 'success', 5, toastId); - }).catch(function (error) { - notyf(error.message || i18n.error || '正文图片上传失败', 'danger', 5, toastId); - }); - return; - } - var type = btn.getAttribute('data-insert-shortcode'); - if (type === 'hide') { - insertEditorContent('[hidecontent type="login"]' + (i18n.hidePlaceholder || '隐藏内容') + '[/hidecontent]'); - } else if (type === 'payshow') { - insertEditorContent('[hidecontent type="payshow"]' + (i18n.payPlaceholder || '付费可见内容') + '[/hidecontent]'); - } else if (type === 'reply') { - insertEditorContent('[hidecontent type="reply"]' + (i18n.replyPlaceholder || '回复后可见') + '[/hidecontent]'); - } else if (type === 'vip') { - insertEditorContent('[vip]' + (i18n.vipPlaceholder || 'VIP 可见内容') + '[/vip]'); - } - }); - }, - bindAi: function () { var ai = cfg.ai || {}; if (!ai.available) { diff --git a/templates/pages/page-submit.php b/templates/pages/page-submit.php index 1643272b..d0ce51b6 100644 --- a/templates/pages/page-submit.php +++ b/templates/pages/page-submit.php @@ -86,6 +86,11 @@ if (file_exists($js)) { 'summary' => !empty($ctx['ai_available']), 'tags' => !empty($ctx['ai_available']), ], + 'editor' => [ + 'canUploadImage' => !empty($ctx['can_upload_img']), + 'canHide' => !empty($ctx['can_hide']), + 'canPay' => !empty($ctx['can_pay']), + ], 'uploads' => [ 'image' => !empty($ctx['can_upload_img']) ? [ 'url' => esc_url_raw(rest_url('aksrt/v1/front-submit/upload/image')), @@ -118,6 +123,25 @@ if (file_exists($js)) { 'pickImage' => __('选择图片', 'aksrt'), 'pickVideo' => __('选择视频封面图', 'aksrt'), 'videoUrl' => __('请输入视频地址', 'aksrt'), + 'close' => __('关闭', 'aksrt'), + 'insertImage' => __('插入图片', 'aksrt'), + 'uploadImage' => __('上传图片', 'aksrt'), + 'externalImage' => __('外链图片', 'aksrt'), + 'selectImages' => __('选择或拖入图片', 'aksrt'), + 'imageUploadHint' => __('可批量选择,上传完成后自动插入正文', 'aksrt'), + 'selectedImages' => __('已选择 %d 张图片', 'aksrt'), + 'confirmUpload' => __('确认上传', 'aksrt'), + 'externalImageHint' => __('每行填写一个图片地址:', 'aksrt'), + 'confirmInsert' => __('确认插入', 'aksrt'), + 'invalidImage' => __('请选择有效的图片文件', 'aksrt'), + 'invalidImageUrl' => __('请填写有效的图片地址', 'aksrt'), + 'uploadUnavailable' => __('当前账号不能上传图片', 'aksrt'), + 'hiddenContent' => __('隐藏内容', 'aksrt'), + 'hiddenPlaceholder' => __('隐藏内容', 'aksrt'), + 'replyVisible' => __('回复可见', 'aksrt'), + 'loginVisible' => __('登录可见', 'aksrt'), + 'vipVisible' => __('VIP 可见', 'aksrt'), + 'payVisible' => __('付费可见', 'aksrt'), ], ]); } diff --git a/templates/parts/submit/editor-main.php b/templates/parts/submit/editor-main.php index ca35c148..1dc72d68 100644 --- a/templates/parts/submit/editor-main.php +++ b/templates/parts/submit/editor-main.php @@ -43,8 +43,36 @@ $is_can_new = !empty($ctx['is_can_new']);
'post_content', 'textarea_rows' => 20, @@ -61,34 +89,10 @@ $is_can_new = !empty($ctx['is_can_new']); ], ]; wp_editor((string) $ctx['current_content'], 'post_content', $editor_settings); + remove_filter('mce_external_plugins', $external_plugins, 10); + remove_filter('mce_css', $editor_content_css, 10); ?>
- - -
- - - - - - - - - - - -
-
diff --git a/tests/widget-system/post-editor-reference-contract.test.cjs b/tests/widget-system/post-editor-reference-contract.test.cjs index c78fca26..c9d935fd 100644 --- a/tests/widget-system/post-editor-reference-contract.test.cjs +++ b/tests/widget-system/post-editor-reference-contract.test.cjs @@ -49,6 +49,29 @@ test('front submit follows the reference newposts shell and cards', () => { assert.match(submit, /new-posts-submit/); }); +test('front submit image and visibility actions live in the TinyMCE toolbar', () => { + const main = read('templates/parts/submit/editor-main.php'); + const page = read('templates/pages/page-submit.php'); + const submit = read('assets/js/front-submit.js'); + const editor = read('assets/js/front-submit-editor.js'); + + assert.match(main, /\$toolbar1\[\]\s*=\s*'aksrt_image'/); + assert.match(main, /\$toolbar1\[\]\s*=\s*'aksrt_hide'/); + assert.match(main, /mce_external_plugins/); + assert.match(main, /aksrt_editor_tools/); + assert.match(editor, /PluginManager\.add\('aksrt_editor_tools'/); + assert.match(editor, /addButton\('aksrt_image'/); + assert.match(editor, /addButton\('aksrt_hide'/); + assert.match(editor, /type:\s*'menubutton'/); + assert.match(editor, /data-editor-media-tab="upload"/); + assert.match(editor, /data-editor-media-tab="external"/); + assert.match(editor, /\(cfg\.uploads \|\| \{\}\)\.image/); + assert.match(page, /'canUploadImage'\s*=>\s*!empty\(\$ctx\['can_upload_img'\]\)/); + assert.doesNotMatch(main, /newposts-editor-tools|data-insert-shortcode|data-insert-upload-image/); + assert.doesNotMatch(submit, /bindShortcodes|data-insert-shortcode|data-insert-upload-image/); + assert.doesNotMatch(editor, /hidecontent|shop|forum|bbs_/i); +}); + test('article paid-content metabox follows the reference field set without shop enhancements', () => { const meta = read('src/Core/ResourceMetaBox.php'); const fields = [ diff --git a/tests/widget-system/toast-contract.test.cjs b/tests/widget-system/toast-contract.test.cjs index 7bd901fb..04bb2edd 100644 --- a/tests/widget-system/toast-contract.test.cjs +++ b/tests/widget-system/toast-contract.test.cjs @@ -369,6 +369,8 @@ test('settings actions use rehydrated fixed-id notyf flows', () => { test('front submit actions use direct fixed-id notyf flows', () => { const submit = read('assets/js/front-submit.js'); + const editor = read('assets/js/front-submit-editor.js'); + const source = submit + '\n' + editor; const page = read('templates/pages/page-submit.php'); [ @@ -382,7 +384,7 @@ test('front submit actions use direct fixed-id notyf flows', () => { 'aksrt-front-submit-editor-image-toast', 'aksrt-front-submit-ai-tags-toast', 'aksrt-front-submit-ai-summary-toast', - ].forEach((id) => assert.match(submit, new RegExp("['\"]" + id + "['\"]"))); + ].forEach((id) => assert.match(source, new RegExp("['\"]" + id + "['\"]"))); assert.match(submit, /submit: function[\s\S]*?notyf\([^;]+?'load'[^;]+?toastId[\s\S]*?notyf\([^;]+?'success'[^;]+?toastId[\s\S]*?notyf\([^;]+?'danger'[^;]+?toastId/); assert.match(submit, /bindDraftDelete: function[\s\S]*?'aksrt-front-submit-delete-draft-toast'[\s\S]*?'load'[\s\S]*?'success'[\s\S]*?'danger'/); @@ -398,7 +400,7 @@ test('front submit actions use direct fixed-id notyf flows', () => { }); assert.match(submit, /aksrt-front-submit-resource-upload-toast[\s\S]*?'load'[\s\S]*?'success'[\s\S]*?'danger'/); - assert.match(submit, /aksrt-front-submit-editor-image-toast[\s\S]*?'load'[\s\S]*?'success'[\s\S]*?'danger'/); + assert.match(editor, /aksrt-front-submit-editor-image-toast[\s\S]*?'load'[\s\S]*?'success'[\s\S]*?'danger'/); assert.match(submit, /aksrt-front-submit-ai-tags-toast[\s\S]*?aksrt-front-submit-ai-summary-toast[\s\S]*?'load'[\s\S]*?'success'[\s\S]*?'danger'/); assert.match(submit, /needContent[^;]+?'warning'/); assert.doesNotMatch(submit, /\b(?:window\.)?alert\s*\(/);