feat(ticket): redesign user-center ticket center UI/UX

- Status stat cards double as quick filters; debounced search with stale-response guard
- Card list with status stripe, windowed pagination, skeleton/empty/error states
- Detail dialog as chat timeline with in-place refresh, quote preview, Ctrl+Enter send
- Create form with department chips, inline validation, multi-image upload, success notice
- Split shared helpers into ticket-center-core.js and enqueue it before the panel script
- Drop dead data-aksrt-ticket-config and legacy colorful modal header

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
kate522andClaude Fable 5 committed 2026-10-26 23:06:34 +08:00
1 parent af4fb87e6d
commit 3bd15feccb
7 files changed
+1819 -621

No files matched your search

@@ -67,6 +67,7 @@ test('the ticket panel mounts both captcha scenes', () => {
const js = read('assets/js/ticket-center.js');
assert.match(js, /runCaptcha\('ticket_create'\)/);
assert.match(js, /runCaptcha\('ticket_reply'\)/);
// 取消验证时中止提交,不静默放行。
assert.match(js, /return payload === null \? Promise\.reject\(\{ cancelled: true \}\)/);
// 取消验证时中止提交,不静默放行(runCaptcha 实现位于共享工具层)。
const core = read('assets/js/ticket-center-core.js');
assert.match(core, /return payload === null \? Promise\.reject\(\{ cancelled: true \}\)/);
});
@@ -119,7 +119,8 @@ test('SPA deep-links the native ticket page instead of hosting it', () => {
test('frontend hooks match the renderer and the JS module', () => {
const renderer = read('inc/helpers/user-center-renderers/profile-tickets.php');
const js = read('assets/js/ticket-center.js');
// 面板脚本 + 共享工具层(请求 / 弹窗 / 上传)共同构成前端模块。
const js = read('assets/js/ticket-center.js') + '\n' + read('assets/js/ticket-center-core.js');
for (const hook of [
'data-aksrt-ticket-center',
@@ -130,7 +131,6 @@ test('frontend hooks match the renderer and the JS module', () => {
'data-aksrt-ticket-pagination',
'data-aksrt-tickets-search',
'data-aksrt-ticket-filter=',
'data-aksrt-ticket-config',
]) {
assert.ok(renderer.includes(hook), `renderer missing ${hook}`);
}