- Fix empty status/department selects in the detail panel (printf leak) - Replace dead 代客提交 button with a working collapsible create form - Error-aware notices; redirect keeps filters and the open ticket - Status views with counts, incl. 未关闭 default and 已关闭 view - Two-column detail: conversation + attachments + prereplies | info, status/department/assignee select, internal notes; closed = read-only - Split detail panel into UsersMenusTicketsDetail trait; page-scoped tickets.css / admin-tickets.js replace inline styles - AdminListPagination accepts parent file so users.php keeps menu highlight - Drop native loading="lazy" per lazyload contract Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
64 lines
2.4 KiB
JavaScript
64 lines
2.4 KiB
JavaScript
/**
|
|
* 工单管理后台页(users.php?page=aksrt_tickets)的渐进增强:
|
|
* - 预设回复下拉:插入到回复框光标处;
|
|
* - 回复框字数计数;
|
|
* - 表单提交时禁用提交按钮,防止重复提交。
|
|
* 无 JS 时页面全部功能仍可用。
|
|
*/
|
|
(function () {
|
|
'use strict';
|
|
|
|
function byId(id) {
|
|
return id ? document.getElementById(id) : null;
|
|
}
|
|
|
|
function updateCounter(textarea) {
|
|
var counter = document.querySelector('[data-aksrt-ticket-counter="' + textarea.id + '"]');
|
|
if (!counter) return;
|
|
var max = parseInt(textarea.getAttribute('maxlength'), 10) || 0;
|
|
var len = textarea.value.length;
|
|
counter.textContent = len + ' / ' + max;
|
|
counter.classList.toggle('is-over', max > 0 && len >= max);
|
|
}
|
|
|
|
function insertAtCursor(textarea, text) {
|
|
var start = textarea.selectionStart || 0;
|
|
var end = textarea.selectionEnd || 0;
|
|
var before = textarea.value.slice(0, start);
|
|
var sep = before && !/\n$/.test(before) ? '\n' : '';
|
|
textarea.value = before + sep + text + textarea.value.slice(end);
|
|
var pos = (before + sep + text).length;
|
|
textarea.focus();
|
|
textarea.setSelectionRange(pos, pos);
|
|
updateCounter(textarea);
|
|
}
|
|
|
|
document.addEventListener('change', function (e) {
|
|
var select = e.target.closest('[data-aksrt-ticket-prereply]');
|
|
if (!select || !select.value) return;
|
|
var textarea = byId(select.getAttribute('data-aksrt-ticket-prereply'));
|
|
if (textarea) insertAtCursor(textarea, select.value);
|
|
select.value = '';
|
|
});
|
|
|
|
document.addEventListener('input', function (e) {
|
|
if (e.target.matches('[data-aksrt-ticket-reply-input]')) updateCounter(e.target);
|
|
});
|
|
|
|
document.addEventListener('submit', function (e) {
|
|
var form = e.target;
|
|
if (!form.closest('.aksrt-tickets-admin') || form.method.toLowerCase() !== 'post') return;
|
|
var button = form.querySelector('[type="submit"]');
|
|
if (button) {
|
|
// 延后一帧,避免禁用按钮阻断本次提交。
|
|
setTimeout(function () { button.disabled = true; }, 0);
|
|
}
|
|
});
|
|
|
|
document.querySelectorAll('[data-aksrt-ticket-reply-input]').forEach(updateCounter);
|
|
|
|
// 展开的工单在回跳后滚动到视口内(锚点已带 #aksrt-ticket-{id},这里兜底聚焦详情)。
|
|
var open = document.querySelector('.aksrt-tickets-admin__row.is-open');
|
|
if (open && !location.hash) open.scrollIntoView({ block: 'start' });
|
|
})();
|