forks

Creation chat

yihtech
请帮我制定一份管理系统的前端规范
07-05 02:43

登录 后可以评论

Assistant
这是为您生成的一份管理系统前端规范的HTML页面,它清晰展示了编码规范、组件设计和主题切换等核心要求。 (HTML code has been generated and updated; see above for the current version.) ### 规范文档与交互示例 这份页面既是规范说明,也是一个可交互的待办应用示例,帮助您理解各项要求如何落地。 * **规范概览**:页面顶部以卡片网格的形式,清晰列出了“编码规范”、“安全 & 交互”、“持久化 & 状态”和“可访问性 & 响应式”四大核心规范要点,便于团队快速查阅和对齐。 * **交互示例**:下方的“待办示例”模块演示了规范的具体应用。您可以通过表单添加待办,点击待办项前的图标切换完成状态,或使用“清空所有”按钮进行批量操作。所有数据都会自动保存到浏览器的 `localStorage` 中,刷新页面后状态依然保留。 * **操作反馈**:页面为所有可交互元素(按钮、链接)提供了 hover 和 focus 样式反馈。同时,在“清空所有”等关键操作时会弹出自定义确认弹窗,替代了浏览器原生的 `alert`,视觉风格更统一。
▼ Code
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>管理系统 · 前端规范</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <script>tailwind.config = { darkMode: 'class' }</script>
</head>
<body class="min-h-screen bg-slate-50 dark:bg-slate-900 text-slate-700 dark:text-slate-200">

    <!-- ===== 主题切换按钮 (固定右上角) ===== -->
    <button id="theme-toggle-btn" class="fixed top-4 right-4 z-10 p-2 rounded-md hover:bg-gray-200 dark:hover:bg-gray-700 text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 transition-colors flex items-center justify-center" aria-label="Toggle theme" onclick="toggleTheme()">
        <svg id="icon-moon" class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M21.752 15.002A9.72 9.72 0 0 1 18 15.75c-5.385 0-9.75-4.365-9.75-9.75 0-1.33.266-2.597.748-3.752A9.753 9.753 0 0 0 3 11.25C3 16.635 7.365 21 12.75 21a9.753 9.753 0 0 0 9.002-5.998z"/></svg>
        <svg id="icon-sun" class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24" aria-hidden="true" style="display:none"><circle cx="12" cy="12" r="4"></circle><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41"></path></svg>
    </button>
    <script>
        function toggleTheme(){ var el=document.documentElement; var isDark=!el.classList.contains('dark'); el.classList.toggle('dark',isDark); localStorage.setItem('theme',isDark?'dark':'light'); applyTheme(isDark); }
        function applyTheme(isDark){ document.getElementById('icon-moon').style.display=isDark?'none':'block'; document.getElementById('icon-sun').style.display=isDark?'block':'none'; }
        document.addEventListener('DOMContentLoaded',function(){ var saved=localStorage.getItem('theme'); var isDark=saved==='dark'||(!saved&&window.matchMedia('(prefers-color-scheme: dark)').matches); document.documentElement.classList.toggle('dark',isDark); applyTheme(isDark); });
    </script>

    <!-- ===== 自定义弹窗 (用于提示/确认) ===== -->
    <div id="custom-modal" class="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4 hidden" aria-hidden="true">
        <div class="rounded-xl bg-white dark:bg-slate-800 p-6 shadow-xl max-w-sm w-full border border-slate-200 dark:border-slate-700">
            <p id="custom-modal-msg" class="text-slate-700 dark:text-slate-200 mb-4"></p>
            <button type="button" onclick="hideModal()" class="w-full rounded-lg bg-blue-600 px-4 py-2 text-white hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-600">确定</button>
        </div>
    </div>
    <script>
        function showModal(msg){ var el=document.getElementById('custom-modal'); document.getElementById('custom-modal-msg').textContent=msg||''; el.classList.remove('hidden'); }
        function hideModal(){ document.getElementById('custom-modal').classList.add('hidden'); }
    </script>

    <!-- ===== 主内容 ===== -->
    <main class="max-w-5xl mx-auto px-4 py-12 md:py-16">

        <!-- 头部 -->
        <header class="mb-12">
            <h1 class="text-4xl md:text-5xl font-extrabold text-slate-900 dark:text-slate-100 tracking-tight">管理系统前端规范</h1>
            <p class="mt-3 text-lg text-slate-600 dark:text-slate-400">基于现代蓝 · 响应式 · 可复用组件体系</p>
        </header>

        <!-- 规范概览卡片网格 -->
        <section class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-16">
            <!-- 卡片 1 -->
            <div class="bg-white dark:bg-slate-800 rounded-2xl shadow-md border border-slate-200 dark:border-slate-700 p-6 transition-shadow hover:shadow-lg">
                <div class="flex items-center gap-3 mb-4">
                    <span class="flex items-center justify-center w-10 h-10 rounded-lg bg-blue-100 dark:bg-blue-900/40 text-blue-600 dark:text-blue-400"><svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6h16.5M3.75 12h16.5m-16.5 6h16.5"/></svg></span>
                    <h2 class="text-xl font-semibold text-slate-900 dark:text-slate-100">编码规范</h2>
                </div>
                <ul class="space-y-2 text-sm text-slate-600 dark:text-slate-300">
                    <li>✓ 语义化 HTML5 (header, main, section, nav)</li>
                    <li>✓ Tailwind CSS 响应式 (sm/md/lg)</li>
                    <li>✓ 组件化思维,复用卡片、表格、表单</li>
                    <li>✓ 所有交互使用 click + touch-action: manipulation</li>
                    <li>✓ dark 模式全面覆盖 (dark:bg-slate-800 等)</li>
                </ul>
            </div>
            <!-- 卡片 2 -->
            <div class="bg-white dark:bg-slate-800 rounded-2xl shadow-md border border-slate-200 dark:border-slate-700 p-6 transition-shadow hover:shadow-lg">
                <div class="flex items-center gap-3 mb-4">
                    <span class="flex items-center justify-center w-10 h-10 rounded-lg bg-emerald-100 dark:bg-emerald-900/40 text-emerald-600 dark:text-emerald-400"><svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75 11.25 15 15 9.75m-3-7.036A11.959 11.959 0 0 1 3.598 6 11.99 11.99 0 0 0 3 9.749c0 5.592 3.824 10.29 9 11.623 5.176-1.332 9-6.03 9-11.622 0-1.31-.21-2.571-.598-3.751h-.152c-3.196 0-6.1-1.248-8.25-3.285Z"/></svg></span>
                    <h2 class="text-xl font-semibold text-slate-900 dark:text-slate-100">安全 & 交互</h2>
                </div>
                <ul class="space-y-2 text-sm text-slate-600 dark:text-slate-300">
                    <li>✓ 表单使用 JS 拦截 submit (preventDefault)</li>
                    <li>✓ 自定义弹窗代替 alert/confirm (showModal)</li>
                    <li>✓ 移动端触摸优化 (touch-action: manipulation)</li>
                    <li>✓ 所有按钮/链接有 hover/focus 反馈</li>
                    <li>✓ 避免依赖顶层导航 (target="_blank")</li>
                </ul>
            </div>
            <!-- 卡片 3 -->
            <div class="bg-white dark:bg-slate-800 rounded-2xl shadow-md border border-slate-200 dark:border-slate-700 p-6 transition-shadow hover:shadow-lg">
                <div class="flex items-center gap-3 mb-4">
                    <span class="flex items-center justify-center w-10 h-10 rounded-lg bg-amber-100 dark:bg-amber-900/40 text-amber-600 dark:text-amber-400"><svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M12 6v6h4.5m4.5 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"/></svg></span>
                    <h2 class="text-xl font-semibold text-slate-900 dark:text-slate-100">持久化 & 状态</h2>
                </div>
                <ul class="space-y-2 text-sm text-slate-600 dark:text-slate-300">
                    <li>✓ localStorage 存储用户偏好/草稿</li>
                    <li>✓ 页面加载自动恢复上次状态</li>
                    <li>✓ 主题切换持久化 (dark/light)</li>
                    <li>✓ 控制台关键操作日志 (console.log)</li>
                    <li>✓ 可恢复的待办/表单数据</li>
                </ul>
            </div>
            <!-- 卡片 4 -->
            <div class="bg-white dark:bg-slate-800 rounded-2xl shadow-md border border-slate-200 dark:border-slate-700 p-6 transition-shadow hover:shadow-lg">
                <div class="flex items-center gap-3 mb-4">
                    <span class="flex items-center justify-center w-10 h-10 rounded-lg bg-violet-100 dark:bg-violet-900/40 text-violet-600 dark:text-violet-400"><svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M9.53 16.122a3 3 0 0 0-5.78 1.128 2.25 2.25 0 0 0 2.4 2.245 4.5 4.5 0 0 0 8.4-2.245c0-.399-.078-.78-.22-1.128m0 0a15.998 15.998 0 0 0 3.388-1.62m-5.043-.025a15.994 15.994 0 0 1 1.622-3.395m3.42 3.42a15.995 15.995 0 0 0 4.764-4.648l3.876-5.814a1.151 1.151 0 0 0-1.597-1.597L14.146 6.32a15.996 15.996 0 0 0-4.649 4.763m3.42 3.42a6.776 6.776 0 0 0-3.42-3.42"/></svg></span>
                    <h2 class="text-xl font-semibold text-slate-900 dark:text-slate-100">可访问性 & 响应式</h2>
                </div>
                <ul class="space-y-2 text-sm text-slate-600 dark:text-slate-300">
                    <li>✓ 移动端优先,min-h-screen 显式高度</li>
                    <li>✓ aria-label, role, focus 管理</li>
                    <li>✓ 图片使用 object-cover + 占位 (picsum)</li>
                    <li>✓ 小游戏使用 Canvas + touch-action:none</li>
                    <li>✓ 字体/间距/留白一致</li>
                </ul>
            </div>
        </section>

        <!-- 组件示例区:待办列表 (展示交互、持久化、表单) -->
        <section class="bg-white dark:bg-slate-800 rounded-2xl shadow-md border border-slate-200 dark:border-slate-700 p-6 md:p-8 mb-12">
            <div class="flex items-center gap-3 mb-6">
                <span class="flex items-center justify-center w-10 h-10 rounded-lg bg-blue-100 dark:bg-blue-900/40 text-blue-600 dark:text-blue-400"><svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M9 12h3.75M9 15h3.75M9 18h3.75m3 .75H18a2.25 2.25 0 0 0 2.25-2.25V6.108c0-1.135-.845-2.098-1.976-2.192a48.424 48.424 0 0 0-1.123-.08m-5.801 0c-.065.21-.1.433-.1.664 0 .414.336.75.75.75h4.5a.75.75 0 0 0 .75-.75 2.25 2.25 0 0 0-.1-.664m-5.8 0A2.251 2.251 0 0 1 13.5 2.25H15c1.012 0 1.867.668 2.15 1.586m-5.8 0c-.376.023-.75.05-1.124.08C9.095 4.01 8.25 4.973 8.25 6.108V8.25m0 0H4.875c-.621 0-1.125.504-1.125 1.125v11.25c0 .621.504 1.125 1.125 1.125h9.75c.621 0 1.125-.504 1.125-1.125V9.375c0-.621-.504-1.125-1.125-1.125H8.25ZM6.75 12h.008v.008H6.75V12Zm0 3h.008v.008H6.75V15Zm0 3h.008v.008H6.75V18Z"/></svg></span>
                <h2 class="text-2xl font-bold text-slate-900 dark:text-slate-100">待办示例 · 带持久化</h2>
            </div>

            <!-- 表单 -->
            <form id="todo-form" class="flex flex-col sm:flex-row gap-3 mb-6">
                <input type="text" id="todo-input" placeholder="输入新待办…" class="flex-1 rounded-lg border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-700 px-4 py-2.5 text-sm text-slate-700 dark:text-slate-200 placeholder-slate-400 dark:placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400" required>
                <button type="submit" class="rounded-lg bg-blue-600 hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-600 text-white px-5 py-2.5 text-sm font-medium transition flex items-center justify-center gap-1 touch-action:manipulation">
                    <svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15"/></svg>
                    添加
                </button>
            </form>

            <!-- 待办列表 -->
            <ul id="todo-list" class="space-y-2">
                <!-- 由 JS 动态渲染 -->
            </ul>

            <!-- 统计 & 操作 -->
            <div class="flex flex-wrap items-center justify-between mt-6 pt-4 border-t border-slate-200 dark:border-slate-700 text-sm text-slate-500 dark:text-slate-400">
                <span id="todo-count">0 项待办</span>
                <button id="clear-all-btn" class="text-amber-600 dark:text-amber-400 hover:underline font-medium">清空所有</button>
            </div>
        </section>

        <!-- 提示 Console 日志可用 -->
        <footer class="text-center text-xs text-slate-400 dark:text-slate-500 border-t border-slate-200 dark:border-slate-700 pt-6">
            <p>💡 预览区右下角有 Console 面板 · 所有操作均记录日志</p>
            <p class="mt-1">规范版本 1.0 · 基于现代蓝配色</p>
        </footer>
    </main>

    <script>
        // ========== 待办模块 ==========
        (function() {
            const STORAGE_KEY = 'vibe_todo_demo';
            const form = document.getElementById('todo-form');
            const input = document.getElementById('todo-input');
            const list = document.getElementById('todo-list');
            const countSpan = document.getElementById('todo-count');
            const clearBtn = document.getElementById('clear-all-btn');

            // 读取或初始化
            let todos = [];
            try {
                const stored = localStorage.getItem(STORAGE_KEY);
                if (stored) todos = JSON.parse(stored);
            } catch(e) { console.warn('读取待办失败', e); }

            // 渲染
            function render() {
                list.innerHTML = '';
                if (todos.length === 0) {
                    list.innerHTML = '<li class="text-slate-400 dark:text-slate-500 text-center py-6 italic">暂无待办,添加一条吧</li>';
                } else {
                    todos.forEach((todo, index) => {
                        const li = document.createElement('li');
                        li.className = 'flex items-center justify-between gap-3 px-4 py-3 rounded-lg bg-slate-50 dark:bg-slate-700/60 border border-slate-200 dark:border-slate-600 transition';
                        li.innerHTML = `
                            <span class="flex-1 text-sm ${todo.done ? 'line-through text-slate-400 dark:text-slate-500' : 'text-slate-700 dark:text-slate-200'}">${escapeHtml(todo.text)}</span>
                            <div class="flex items-center gap-2">
                                <button class="toggle-btn text-emerald-600 dark:text-emerald-400 hover:text-emerald-800 dark:hover:text-emerald-300 p-1 rounded-md transition touch-action:manipulation" data-index="${index}" aria-label="切换完成">
                                    <svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5"/></svg>
                                </button>
                                <button class="delete-btn text-red-500 dark:text-red-400 hover:text-red-700 dark:hover:text-red-300 p-1 rounded-md transition touch-action:manipulation" data-index="${index}" aria-label="删除待办">
                                    <svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12"/></svg>
                                </button>
                            </div>
                        `;
                        list.appendChild(li);
                    });
                }
                countSpan.textContent = `${todos.length} 项待办`;
                // 持久化
                localStorage.setItem(STORAGE_KEY, JSON.stringify(todos));
                console.log('[待办] 当前列表长度:', todos.length, todos.map(t=>t.text).join(', '));
            }

            // 简单转义
            function escapeHtml(text) {
                const div = document.createElement('div');
                div.textContent = text;
                return div.innerHTML;
            }

            // 添加待办
            function addTodo(text) {
                if (!text.trim()) return;
                todos.push({ text: text.trim(), done: false });
                render();
                console.log('[待办] 添加:', text.trim());
            }

            // 切换完成
            function toggleDone(index) {
                if (todos[index]) {
                    todos[index].done = !todos[index].done;
                    render();
                    console.log('[待办] 切换完成:', todos[index].text, todos[index].done);
                }
            }

            // 删除
            function deleteTodo(index) {
                if (todos[index]) {
                    const removed = todos[index].text;
                    todos.splice(index, 1);
                    render();
                    console.log('[待办] 删除:', removed);
                }
            }

            // 清空
            function clearAll() {
                if (todos.length === 0) return;
                showModal('确定要清空所有待办吗?');
                // 利用自定义弹窗的确定按钮做清空:监听弹窗确定按钮点击
                // 但弹窗确定按钮已绑定 hideModal,需要额外逻辑
                const modalBtn = document.querySelector('#custom-modal button');
                const originalClick = modalBtn.onclick;
                modalBtn.onclick = function() {
                    todos = [];
                    render();
                    console.log('[待办] 清空所有');
                    hideModal();
                    modalBtn.onclick = originalClick; // 恢复
                };
            }

            // 事件绑定
            form.addEventListener('submit', function(e) {
                e.preventDefault();
                const val = input.value.trim();
                if (val) {
                    addTodo(val);
                    input.value = '';
                } else {
                    showModal('请输入待办内容');
                }
            });

            // 事件代理 (toggle / delete)
            list.addEventListener('click', function(e) {
                const toggleBtn = e.target.closest('.toggle-btn');
                const deleteBtn = e.target.closest('.delete-btn');
                if (toggleBtn) {
                    const index = parseInt(toggleBtn.dataset.index, 10);
                    toggleDone(index);
                } else if (deleteBtn) {
                    const index = parseInt(deleteBtn.dataset.index, 10);
                    deleteTodo(index);
                }
            });

            // 清空按钮
            clearBtn.addEventListener('click', clearAll);

            // 初始渲染
            render();
            console.log('[待办] 模块初始化完成');
        })();
    </script>

    <!-- 确保移动端触摸时无延迟 -->
    <style>
        button, a, .touch-action\:manipulation { touch-action: manipulation; }
        /* 自定义弹窗过渡 */
        #custom-modal { transition: opacity 0.2s; }
        #custom-modal.hidden { display: none; }
        /* 列表按钮 */
        .toggle-btn, .delete-btn { touch-action: manipulation; }
    </style>
</body>
</html>
07-05 02:51

登录 后可以评论

Comments

登录 后可以评论