lesson more edit
This commit is contained in:
115
Html/apps/static/js/modelinput.js
Normal file
115
Html/apps/static/js/modelinput.js
Normal file
@@ -0,0 +1,115 @@
|
||||
// ======= 工具:创建编辑按钮(含铅笔 SVG) =======
|
||||
function createEditButton(onClick) {
|
||||
const btn = document.createElement('button');
|
||||
btn.type = 'button';
|
||||
btn.className = 'edit-btn';
|
||||
btn.setAttribute('aria-label', '编辑标题');
|
||||
btn.innerHTML = `
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||
<i class="fas fa-pencil-alt"></i>
|
||||
</svg>
|
||||
`;
|
||||
btn.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
onClick && onClick();
|
||||
});
|
||||
return btn;
|
||||
}
|
||||
// ======= 模态框:打开标题编辑器 =======
|
||||
function openTitleEditor({ title = '修改标题', initialValue = '', onSubmit }) {
|
||||
const backdrop = document.createElement('div');
|
||||
backdrop.className = 'title_change_modal-backdrop';
|
||||
|
||||
const title_change_modal = document.createElement('div');
|
||||
title_change_modal.className = 'title_change_modal';
|
||||
title_change_modal.setAttribute('role', 'dialog');
|
||||
title_change_modal.setAttribute('aria-title_change_modal', 'true');
|
||||
|
||||
title_change_modal.innerHTML = `
|
||||
<h3 id="dialog-title">${title}</h3>
|
||||
<div class="field">
|
||||
<label for="title-input" class="sr-only">标题</label>
|
||||
<input id="title-input" type="text" placeholder="请输入标题" value="${escapeHtmlAttr(initialValue)}" />
|
||||
<div class="error" id="title-error"></div>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button type="button" class="btn" id="cancel-btn">取消</button>
|
||||
<button type="button" class="btn primary" id="ok-btn">确定</button>
|
||||
</div>
|
||||
`;
|
||||
backdrop.appendChild(title_change_modal);
|
||||
document.body.appendChild(backdrop);
|
||||
|
||||
const input = title_change_modal.querySelector('#title-input');
|
||||
const okBtn = title_change_modal.querySelector('#ok-btn');
|
||||
const cancelBtn = title_change_modal.querySelector('#cancel-btn');
|
||||
const errBox = title_change_modal.querySelector('#title-error');
|
||||
const prevFocus = document.activeElement;
|
||||
|
||||
const close = () => {
|
||||
document.body.removeChild(backdrop);
|
||||
if (prevFocus && prevFocus.focus) prevFocus.focus();
|
||||
};
|
||||
|
||||
const submit = () => {
|
||||
const raw = input.value;
|
||||
const result = onSubmit ? onSubmit(raw) : { ok: true };
|
||||
if (result && result.ok) {
|
||||
close();
|
||||
} else if (result && result.message) {
|
||||
errBox.textContent = result.message;
|
||||
input.setAttribute('aria-invalid', 'true');
|
||||
input.focus();
|
||||
}
|
||||
};
|
||||
|
||||
okBtn.addEventListener('click', submit);
|
||||
cancelBtn.addEventListener('click', close);
|
||||
backdrop.addEventListener('click', (e) => {
|
||||
if (e.target === backdrop) close(); // 点击遮罩关闭
|
||||
});
|
||||
title_change_modal.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape') close();
|
||||
if (e.key === 'Enter') submit();
|
||||
});
|
||||
|
||||
// 初始聚焦
|
||||
setTimeout(() => input.focus(), 0);
|
||||
}
|
||||
|
||||
// ======= 验证与规范化 =======
|
||||
function validateTitle(raw) {
|
||||
if (typeof raw !== 'string') return { ok: false, message: '标题格式不正确' };
|
||||
|
||||
// 1) 去掉 Markdown 井号前缀(若用户手动输入了 ### )
|
||||
let t = raw.replace(/^#+\s*/, '');
|
||||
|
||||
// 2) 替换换行为单空格(标题单行)
|
||||
t = t.replace(/\r?\n+/g, ' ');
|
||||
|
||||
// 3) 去首尾空白 & 折叠多空格
|
||||
t = t.trim().replace(/\s{2,}/g, ' ');
|
||||
|
||||
// 4) 基础规则
|
||||
if (t.length === 0) return { ok: false, message: '标题不能为空' };
|
||||
if (t.length > 80) return { ok: false, message: '标题不能超过 80 个字符' };
|
||||
|
||||
// 5) 可选:禁止只包含标点
|
||||
if (!/[0-9A-Za-z\u4e00-\u9fa5]/.test(t)) {
|
||||
return { ok: false, message: '标题需包含字母/数字/中文' };
|
||||
}
|
||||
|
||||
return { ok: true, value: t };
|
||||
}
|
||||
|
||||
// 显示时的规范化(比如初始渲染)
|
||||
function normalizeTitle(s) {
|
||||
if (!s) return '';
|
||||
return s.replace(/^#+\s*/, '').replace(/\r?\n+/g, ' ').trim().replace(/\s{2,}/g, ' ');
|
||||
}
|
||||
|
||||
// 简单转义,避免把初始值插进 input 时破坏属性
|
||||
function escapeHtmlAttr(s) {
|
||||
return String(s).replace(/&/g,'&').replace(/"/g,'"').replace(/</g,'<').replace(/>/g,'>');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user