源码介绍

一款解决“吃什么”难题的趣味点餐助手。通过随机转盘形式,为您推荐美食选择,并支持自定义菜单,让点餐过程变得轻松有趣,是朋友聚餐与日常用餐的决策好帮手。

image.png

源码:

演示:https://tool.jetmast.com/chi/

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <title>美食大转盘 - 点餐助手 - 墨桅博客jetmast.com</title>
    <style>
        /* 
        ============================================
        样式表说明 - 现代素雅版
        ============================================
        设计理念:
        - 保留原有的简约、清晰、易用风格
        - 加入现代玻璃拟态和深度设计元素
        - 优化移动端触摸体验和视觉效果
        - 保持功能完整性和代码可维护性
        ============================================
        */

        :root {
            --primary-color: #4a90e2;
            --primary-light: #6aa8f7;
            --primary-dark: #357abd;
            --secondary-color: #50c878;
            --accent-color: #ff8c42;
            --danger-color: #ff6b6b;
            --warning-color: #ffa726;
            --text-primary: #2d3748;
            --text-secondary: #718096;
            --text-light: #a0aec0;
            --bg-primary: #ffffff;
            --bg-secondary: #f7fafc;
            --bg-overlay: rgba(255, 255, 255, 0.85);
            --border-color: #e2e8f0;
            --shadow-light: rgba(0, 0, 0, 0.04);
            --shadow-medium: rgba(0, 0, 0, 0.1);
            --shadow-heavy: rgba(0, 0, 0, 0.15);
            --glass-blur: 12px;
        }

        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            -webkit-tap-highlight-color: transparent;
        }

        html {
            scroll-behavior: smooth;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;
            min-height: 100vh;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            background-attachment: fixed;
            padding: 1rem;
            color: var(--text-primary);
            line-height: 1.6;
            overflow-x: hidden;
        }

        .container {
            max-width: 1200px;
            margin: 0 auto;
        }

        .header {
            text-align: center;
            margin-bottom: 2rem;
            padding-bottom: 1.5rem;
            border-bottom: 1px solid rgba(255, 255, 255, 0.2);
        }

        .header h1 {
            font-size: clamp(1.8rem, 5vw, 2.8rem);
            font-weight: 800;
            color: white;
            margin: 0.5rem 0;
            letter-spacing: -0.5px;
            text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
        }

        .header p {
            color: rgba(255, 255, 255, 0.9);
            font-size: 1.1rem;
            font-weight: 400;
            margin-top: 0.5rem;
            max-width: 600px;
            margin-left: auto;
            margin-right: auto;
        }

        .shortcut-hint {
            display: inline-block;
            background: rgba(255, 255, 255, 0.15);
            color: white;
            padding: 0.4rem 0.8rem;
            border-radius: 20px;
            font-size: 0.9rem;
            margin-top: 1rem;
            border: 1px solid rgba(255, 255, 255, 0.2);
            backdrop-filter: blur(var(--glass-blur));
        }

        .main-grid {
            display: grid;
            grid-template-columns: 350px 1fr;
            gap: 2rem;
            align-items: start;
        }

        @media (max-width: 900px) {
            .main-grid {
                grid-template-columns: 1fr;
            }
            .list-panel { order: 2; }
            .wheel-panel { order: 1; }
        }

        .panel {
            background: var(--bg-overlay);
            border-radius: 20px;
            padding: 1.5rem;
            border: 1px solid rgba(255, 255, 255, 0.3);
            box-shadow: 0 8px 32px var(--shadow-medium);
            transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
            backdrop-filter: blur(var(--glass-blur));
        }

        .panel:hover {
            box-shadow: 0 12px 40px var(--shadow-heavy);
            transform: translateY(-2px);
        }

        .panel-header {
            display: flex;
            align-items: center;
            justify-content: space-between;
            margin-bottom: 1.5rem;
            padding-bottom: 1rem;
            border-bottom: 1px solid var(--border-color);
            flex-wrap: wrap;
            gap: 0.75rem;
        }

        .panel-header h2 {
            font-size: 1.4rem;
            font-weight: 700;
            color: var(--text-primary);
            display: flex;
            align-items: center;
            gap: 0.5rem;
        }

        .count-badge {
            font-size: 0.85rem;
            font-weight: 600;
            color: white;
            background: var(--primary-color);
            padding: 0.3rem 0.7rem;
            border-radius: 20px;
            box-shadow: 0 2px 4px rgba(74, 144, 226, 0.3);
        }

        .input-group {
            margin-bottom: 1.5rem;
        }

        .input-label {
            color: var(--text-primary);
            margin-bottom: 0.5rem;
            display: block;
            font-weight: 600;
            font-size: 1rem;
        }

        .input-row {
            display: flex;
            gap: 0.75rem;
            flex-wrap: wrap;
        }

        input, textarea {
            background: white;
            border: 2px solid var(--border-color);
            border-radius: 12px;
            padding: 0.85rem 1rem;
            color: var(--text-primary);
            outline: none;
            transition: all 0.2s;
            width: 100%;
            font-size: 1rem;
            font-weight: 400;
            box-shadow: 0 2px 4px var(--shadow-light);
        }

        input::placeholder, textarea::placeholder {
            color: var(--text-light);
            font-weight: 400;
        }

        input:focus, textarea:focus {
            border-color: var(--primary-color);
            box-shadow: 0 0 0 3px rgba(74, 144, 226, 0.15);
        }

        .input-row input {
            flex: 1;
            min-width: 150px;
        }

        .btn {
            padding: 0.85rem 1.25rem;
            border: none;
            border-radius: 12px;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
            display: inline-flex;
            align-items: center;
            justify-content: center;
            gap: 0.5rem;
            text-decoration: none;
            user-select: none;
            white-space: nowrap;
            font-size: 0.95rem;
            position: relative;
            box-shadow: 0 4px 6px var(--shadow-light);
            touch-action: manipulation;
        }

        .btn::after {
            content: '';
            position: absolute;
            top: -4px;
            left: -4px;
            right: -4px;
            bottom: -4px;
            border: 2px solid var(--primary-color);
            border-radius: 14px;
            opacity: 0;
            transition: opacity 0.2s;
        }

        .btn.highlight::after {
            opacity: 1;
        }

        .btn:disabled {
            opacity: 0.6;
            cursor: not-allowed;
            transform: none !important;
            box-shadow: none !important;
        }

        .btn:active:not(:disabled) {
            transform: scale(0.96);
        }

        .btn-primary {
            background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-light) 100%);
            color: white;
        }

        .btn-primary:hover:not(:disabled) {
            background: linear-gradient(135deg, var(--primary-dark) 0%, var(--primary-color) 100%);
            box-shadow: 0 6px 12px rgba(74, 144, 226, 0.3);
            transform: translateY(-1px);
        }

        .btn-success {
            background: linear-gradient(135deg, var(--secondary-color) 0%, #66d98a 100%);
            color: white;
        }

        .btn-success:hover:not(:disabled) {
            background: linear-gradient(135deg, #40b368 0%, var(--secondary-color) 100%);
            box-shadow: 0 6px 12px rgba(80, 200, 120, 0.3);
            transform: translateY(-1px);
        }

        .btn-secondary {
            background: white;
            color: var(--text-primary);
            border: 2px solid var(--border-color);
        }

        .btn-secondary:hover:not(:disabled) {
            background: var(--bg-secondary);
            border-color: var(--primary-color);
            box-shadow: 0 4px 8px var(--shadow-light);
            transform: translateY(-1px);
        }

        .btn-danger {
            background: linear-gradient(135deg, var(--danger-color) 0%, #ff8787 100%);
            color: white;
        }

        .btn-danger:hover:not(:disabled) {
            background: linear-gradient(135deg, #ff5252 0%, var(--danger-color) 100%);
            box-shadow: 0 6px 12px rgba(255, 107, 107, 0.3);
            transform: translateY(-1px);
        }

        .btn-warning {
            background: linear-gradient(135deg, var(--warning-color) 0%, #ffb74d 100%);
            color: white;
        }

        .btn-warning:hover:not(:disabled) {
            background: linear-gradient(135deg, #ff9800 0%, var(--warning-color) 100%);
            box-shadow: 0 6px 12px rgba(255, 167, 38, 0.3);
            transform: translateY(-1px);
        }

        .punishment-list {
            max-height: 400px;
            overflow-y: auto;
            margin-bottom: 1.5rem;
            padding-right: 0.5rem;
        }

        .punishment-list::-webkit-scrollbar {
            width: 6px;
        }
        .punishment-list::-webkit-scrollbar-track {
            background: #f1f1f1;
            border-radius: 3px;
        }
        .punishment-list::-webkit-scrollbar-thumb {
            background: #c1c1c1;
            border-radius: 3px;
        }
        .punishment-list::-webkit-scrollbar-thumb:hover {
            background: #a8a8a8;
        }

        .punishment-item {
            background: white;
            padding: 1rem;
            border-radius: 12px;
            margin-bottom: 0.75rem;
            display: flex;
            justify-content: space-between;
            align-items: center;
            transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
            color: var(--text-primary);
            border: 2px solid transparent;
            box-shadow: 0 2px 4px var(--shadow-light);
        }

        .punishment-item:hover {
            background: #f9f9f9;
            transform: translateX(4px);
            box-shadow: 0 4px 8px var(--shadow-light);
        }

        .punishment-item.active {
            background: rgba(74, 144, 226, 0.08);
            border-color: var(--primary-color);
            font-weight: 600;
            box-shadow: 0 0 0 3px rgba(74, 144, 226, 0.1);
        }

        .punishment-item .remove-btn {
            background: transparent;
            border: 2px solid var(--border-color);
            color: var(--text-light);
            cursor: pointer;
            font-size: 1.2rem;
            width: 32px;
            height: 32px;
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: all 0.2s;
            flex-shrink: 0;
            touch-action: manipulation;
        }

        .punishment-item .remove-btn:hover {
            background: var(--danger-color);
            border-color: var(--danger-color);
            color: white;
            transform: scale(1.1);
        }

        .wheel-container {
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            width: 100%;
        }

        .wheel-wrapper {
            position: relative;
            margin-bottom: 2rem;
            width: 100%;
            max-width: 450px;
            aspect-ratio: 1/1;
            filter: drop-shadow(0 8px 16px var(--shadow-medium));
        }

        .wheel-pointer {
            position: absolute;
            top: -18px;
            left: 50%;
            transform: translateX(-50%);
            z-index: 20;
            width: 48px;
            height: 48px;
            background: linear-gradient(135deg, var(--accent-color) 0%, #ffa726 100%);
            border: 4px solid white;
            border-radius: 50%;
            box-shadow: 0 6px 12px var(--shadow-heavy);
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 1.4rem;
        }

        .wheel-pointer::after {
            content: '';
            position: absolute;
            top: 100%;
            left: 50%;
            transform: translateX(-50%);
            border-left: 12px solid transparent;
            border-right: 12px solid transparent;
            border-top: 18px solid var(--accent-color);
            filter: drop-shadow(0 4px 4px var(--shadow-medium));
        }

        .wheel-svg {
            width: 100%;
            height: 100%;
            transition-property: transform;
            transition-timing-function: cubic-bezier(0.17, 0.67, 0.12, 0.99);
        }

        .control-buttons {
            display: flex;
            gap: 1rem;
            margin-bottom: 2rem;
            flex-wrap: wrap;
            justify-content: center;
        }

        .result-display {
            background: white;
            border: 3px solid var(--primary-color);
            color: var(--text-primary);
            padding: 1.5rem 2.5rem;
            border-radius: 16px;
            text-align: center;
            font-weight: 600;
            box-shadow: 0 8px 24px var(--shadow-medium);
            min-width: 280px;
            max-width: 100%;
            transform: scale(0.9);
            opacity: 0;
            transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
        }

        .result-display.show {
            transform: scale(1);
            opacity: 1;
            animation: bounce 0.6s ease;
        }

        .result-display h3 {
            font-size: 1.3rem;
            margin-bottom: 0.5rem;
            color: var(--text-primary);
        }

        .result-display .punishment-text {
            font-size: 1.8rem;
            font-weight: 800;
            word-break: break-word;
            line-height: 1.4;
            color: var(--primary-color);
        }

        .celebration-modal {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: rgba(0, 0, 0, 0.6);
            z-index: 50;
            display: flex;
            align-items: center;
            justify-content: center;
            padding: 1rem;
            backdrop-filter: blur(4px);
        }

        .celebration-content {
            background: white;
            border-radius: 24px;
            padding: 2.5rem;
            max-width: 32rem;
            width: 100%;
            text-align: center;
            position: relative;
            box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
            border: 1px solid rgba(255, 255, 255, 0.2);
        }

        .celebration-close {
            position: absolute;
            top: 1rem;
            right: 1rem;
            width: 2.8rem;
            height: 2.8rem;
            background: #f8f9fa;
            border: 2px solid var(--border-color);
            border-radius: 50%;
            color: var(--text-secondary);
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 1.5rem;
            transition: all 0.2s;
            font-weight: 300;
            touch-action: manipulation;
        }

        .celebration-close:hover {
            background: var(--danger-color);
            border-color: var(--danger-color);
            color: white;
            transform: scale(1.1);
        }

        .celebration-inner {
            position: relative;
        }

        .celebration-emoji {
            font-size: 5rem;
            margin-bottom: 1rem;
            display: block;
            animation: bounce 1s ease infinite;
        }

        .celebration-title {
            font-size: 2rem;
            font-weight: 800;
            color: var(--text-primary);
            margin-bottom: 1.5rem;
            background: linear-gradient(135deg, var(--primary-color) 0%, var(--accent-color) 100%);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            background-clip: text;
        }

        .celebration-result-box {
            background: linear-gradient(135deg, var(--bg-secondary) 0%, white 100%);
            border-radius: 16px;
            padding: 2rem;
            margin-bottom: 2rem;
            border: 2px solid var(--border-color);
            box-shadow: inset 0 2px 4px var(--shadow-light);
        }

        .celebration-result-label {
            font-size: 1.1rem;
            color: var(--text-secondary);
            margin-bottom: 0.5rem;
            font-weight: 600;
        }

        .celebration-result-text {
            font-size: 2rem;
            font-weight: 800;
            color: var(--primary-color);
            line-height: 1.3;
        }

        .celebration-btn {
            background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-light) 100%);
            color: white;
            border: none;
            padding: 1rem 3rem;
            border-radius: 12px;
            font-weight: 700;
            font-size: 1.1rem;
            cursor: pointer;
            transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
            box-shadow: 0 6px 12px rgba(74, 144, 226, 0.3);
            touch-action: manipulation;
        }

        .celebration-btn:hover {
            background: linear-gradient(135deg, var(--primary-dark) 0%, var(--primary-color) 100%);
            box-shadow: 0 8px 16px rgba(74, 144, 226, 0.4);
            transform: translateY(-2px);
        }

        .celebration-btn:active {
            transform: scale(0.96);
        }

        .empty-state {
            padding: 3rem 2rem;
            border-radius: 12px;
            background: var(--bg-secondary);
            color: var(--text-light);
            text-align: center;
            border: 2px dashed var(--border-color);
            font-size: 1.1rem;
        }

        .file-import-note {
            color: var(--text-secondary);
            font-size: 0.85rem;
            margin-top: 0.5rem;
            display: flex;
            align-items: center;
            gap: 0.5rem;
        }

        .clear-all-section {
            display: flex;
            justify-content: flex-end;
            margin-top: 1.5rem;
            padding-top: 1.5rem;
            border-top: 1px solid var(--border-color);
        }

        .confirm-clear-modal {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: rgba(0, 0, 0, 0.6);
            z-index: 100;
            display: flex;
            align-items: center;
            justify-content: center;
            padding: 1rem;
            backdrop-filter: blur(4px);
        }

        .confirm-clear-content {
            background: white;
            border-radius: 24px;
            padding: 2.5rem;
            max-width: 28rem;
            width: 100%;
            text-align: center;
            position: relative;
            box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
            border: 1px solid rgba(255, 255, 255, 0.2);
        }

        .confirm-clear-icon {
            font-size: 4rem;
            margin-bottom: 1rem;
            display: block;
            color: var(--warning-color);
        }

        .confirm-clear-title {
            font-size: 1.8rem;
            font-weight: 800;
            color: var(--text-primary);
            margin-bottom: 1rem;
        }

        .confirm-clear-message {
            font-size: 1rem;
            color: var(--text-secondary);
            margin-bottom: 2rem;
            line-height: 1.6;
        }

        .confirm-clear-count {
            font-weight: 800;
            color: var(--danger-color);
            font-size: 1.3rem;
        }

        .confirm-clear-buttons {
            display: flex;
            gap: 1rem;
            justify-content: center;
            flex-wrap: wrap;
        }

        .confirm-clear-btn {
            padding: 0.85rem 2rem;
            border: none;
            border-radius: 12px;
            font-weight: 600;
            font-size: 1rem;
            cursor: pointer;
            transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
            min-width: 120px;
            touch-action: manipulation;
        }

        .confirm-clear-btn.confirm {
            background: linear-gradient(135deg, var(--secondary-color) 0%, #66d98a 100%);
            color: white;
            box-shadow: 0 4px 8px rgba(80, 200, 120, 0.3);
        }

        .confirm-clear-btn.confirm:hover {
            background: linear-gradient(135deg, #40b368 0%, var(--secondary-color) 100%);
            box-shadow: 0 6px 12px rgba(80, 200, 120, 0.4);
            transform: translateY(-1px);
        }

        .confirm-clear-btn.cancel {
            background: white;
            color: var(--text-primary);
            border: 2px solid var(--border-color);
            box-shadow: 0 2px 4px var(--shadow-light);
        }

        .confirm-clear-btn.cancel:hover {
            background: var(--bg-secondary);
            border-color: var(--primary-color);
            box-shadow: 0 4px 8px var(--shadow-light);
            transform: translateY(-1px);
        }

        @keyframes bounce {
            0%, 20%, 53%, 80%, 100% { transform: translateY(0); }
            40%, 43% { transform: translateY(-12px); }
            70% { transform: translateY(-6px); }
            90% { transform: translateY(-3px); }
        }

        @keyframes modalPop {
            0% { transform: scale(0.85) translateY(30px); opacity: 0; }
            100% { transform: scale(1) translateY(0); opacity: 1; }
        }

        @keyframes pulse {
            0% { transform: scale(1); }
            50% { transform: scale(1.08); }
            100% { transform: scale(1); }
        }

        .hidden {
            display: none !important;
        }

        .modal-enter {
            animation: modalPop 0.4s cubic-bezier(0.4, 0, 0.2, 1) forwards;
        }

        .pulse {
            animation: pulse 0.6s ease;
        }

        /* 移动端深度优化 */
        @media (max-width: 768px) {
            body {
                padding: 0.75rem;
            }

            .panel {
                padding: 1.25rem;
                border-radius: 16px;
            }

            .header h1 {
                font-size: clamp(1.5rem, 6vw, 2.2rem);
            }

            .header p {
                font-size: 1rem;
            }

            .shortcut-hint {
                font-size: 0.85rem;
                padding: 0.35rem 0.7rem;
            }

            .control-buttons {
                flex-direction: column;
                align-items: stretch;
                width: 100%;
                max-width: 350px;
            }

            .btn {
                width: 100%;
                padding: 1rem;
                font-size: 1rem;
            }

            .celebration-content {
                padding: 2rem 1.5rem;
                border-radius: 20px;
            }

            .celebration-title {
                font-size: 1.6rem;
            }

            .celebration-emoji {
                font-size: 4rem;
            }

            .celebration-result-text {
                font-size: 1.6rem;
            }

            .celebration-result-box {
                padding: 1.5rem;
            }

            .confirm-clear-content {
                padding: 2rem 1.5rem;
                border-radius: 20px;
            }

            .confirm-clear-title {
                font-size: 1.5rem;
            }

            .wheel-wrapper {
                max-width: 380px;
            }

            .result-display {
                padding: 1.25rem 1.5rem;
                min-width: auto;
                width: 100%;
                max-width: 350px;
            }

            .result-display .punishment-text {
                font-size: 1.5rem;
            }

            .punishment-item {
                padding: 0.85rem;
            }

            .punishment-item .remove-btn {
                width: 30px;
                height: 30px;
                font-size: 1.1rem;
            }

            .input-row {
                flex-direction: column;
            }

            .input-row input {
                min-width: auto;
            }

            .panel-header {
                flex-direction: column;
                align-items: flex-start;
            }

            .panel-header .btn {
                width: auto;
                align-self: flex-end;
            }
        }

        /* 超小屏幕优化 */
        @media (max-width: 360px) {
            .wheel-wrapper {
                max-width: 300px;
            }

            .celebration-content, .confirm-clear-content {
                padding: 1.5rem 1rem;
            }

            .celebration-title {
                font-size: 1.4rem;
            }

            .celebration-result-text {
                font-size: 1.4rem;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <!-- 头部标题 -->
        <div class="header">
            <h1>🍽️ 美食大转盘</h1>
            <p>还在为"吃什么"发愁吗?让转盘帮你决定今天的美食!</p>
            <div class="shortcut-hint">💡 提示:按空格键可以快速开始点餐</div>
        </div>

        <div class="main-grid">
            <!-- 菜品管理面板 -->
            <div class="panel list-panel">
                <div class="panel-header">
                    <div style="display: flex; align-items: center; gap: 0.5rem;">
                        <h2>📋 我的美食菜单</h2>
                        <div class="count-badge" id="punishmentCount">0项</div>
                    </div>
                    <button class="btn btn-danger" id="clearAllPunishmentsBtn">
                        🗑️ 一键清空
                    </button>
                </div>

                <div class="input-group">
                    <label class="input-label">添加新菜品:</label>
                    <div class="input-row">
                        <input type="text" id="punishmentInput" placeholder="例如:麻辣香锅、披萨、寿司..." autocomplete="off">
                        <button class="btn btn-primary" id="addPunishmentBtn">
                            ➕ 添加
                        </button>
                    </div>
                </div>

                <!-- 文件导入功能 -->
                <div class="input-group">
                    <label class="input-label">导入菜品列表(TXT文件):</label>
                    <div class="input-row">
                        <input type="file" id="importFile" accept=".txt" style="flex: 1; padding: 0.6rem;">
                        <button class="btn btn-secondary" id="importBtn">
                            📁 导入
                        </button>
                    </div>
                    <div class="file-import-note">
                        ℹ️ 每行一个菜品,支持批量导入
                    </div>
                </div>

                <div class="punishment-list" id="punishmentList">
                    <!-- 菜品项目动态加载 -->
                </div>

                <div class="clear-all-section">
                    <button class="btn btn-warning" id="resetToDefaultBtn">
                        🔄 恢复默认菜单
                    </button>
                </div>
            </div>

            <!-- 转盘控制面板 -->
            <div class="panel wheel-panel" style="display: flex; flex-direction: column; align-items: center; justify-content: center;">
                <div class="wheel-container">
                    <div class="wheel-wrapper">
                        <!-- 转盘指针 -->
                        <div class="wheel-pointer">🎯</div>
                        <!-- SVG转盘 -->
                        <svg id="wheelSvg" class="wheel-svg" viewBox="-250 -250 500 500">
                            <defs>
                                <radialGradient id="centerGradient">
                                    <stop offset="0%" stop-color="#ffffff" />
                                    <stop offset="100%" stop-color="#f0f0f0" />
                                </radialGradient>
                                <filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
                                    <feDropShadow dx="0" dy="2" stdDeviation="3" flood-color="rgba(0,0,0,0.15)"/>
                                </filter>
                            </defs>
                            <!-- 转盘扇形区域(动态生成) -->
                            <g id="wheelSections"></g>
                            <!-- 转盘中心装饰 -->
                            <circle cx="0" cy="0" r="50" fill="url(#centerGradient)" stroke="#fff" stroke-width="4" filter="url(#shadow)"/>
                            <circle cx="0" cy="0" r="35" fill="none" stroke="#e0e0e0" stroke-width="2" stroke-dasharray="4 4"/>
                            <text x="0" y="5" fill="#4a90e2" font-size="18" font-weight="bold" text-anchor="middle" dominant-baseline="middle">点餐</text>
                        </svg>
                    </div>

                    <!-- 控制按钮区域 -->
                    <div class="control-buttons">
                        <button class="btn btn-primary" id="startBtn" style="min-width: 160px; padding: 1.1rem;">
                            <span id="startBtnText">🎰 开始点餐</span>
                        </button>
                        <button class="btn btn-secondary" id="resetBtn" style="padding: 1.1rem;">
                            🔄 重置转盘
                        </button>
                    </div>

                    <!-- 结果展示区域 -->
                    <div id="resultDisplay" class="result-display">
                        <h3>🎉 今日推荐</h3>
                        <div class="punishment-text" id="winnerPunishment">等待选择...</div>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <!-- 庆祝弹窗 -->
    <div id="celebrationModal" class="celebration-modal hidden">
        <div class="celebration-content">
            <button class="celebration-close" id="closeCelebrationBtn">×</button>
            <div class="celebration-inner">
                <span class="celebration-emoji">🍽️</span>
                <h2 class="celebration-title">今日美食已选定!</h2>

                <div class="celebration-result-box">
                    <div class="celebration-result-label">转盘推荐:</div>
                    <div class="celebration-result-text" id="celebrationPunishment"></div>
                </div>

                <button class="celebration-btn" id="confirmCelebrationBtn">
                    ✅ 就吃这个了!
                </button>
            </div>
        </div>
    </div>

    <!-- 一键清空确认弹窗 -->
    <div id="confirmClearModal" class="confirm-clear-modal hidden">
        <div class="confirm-clear-content modal-enter">
            <span class="confirm-clear-icon">⚠️</span>
            <h2 class="confirm-clear-title">确认清空菜单</h2>
            <div class="confirm-clear-message">
                您确定要清空所有 <span class="confirm-clear-count" id="clearCount">0</span> 个菜品吗?<br>
                此操作无法撤销,所有自定义和导入的菜品将被永久删除。
            </div>
            <div class="confirm-clear-buttons">
                <button class="confirm-clear-btn cancel" id="cancelClearBtn">
                    ❌ 取消
                </button>
                <button class="confirm-clear-btn confirm" id="confirmClearBtn">
                    ✅ 确定清空
                </button>
            </div>
        </div>
    </div>

    <script>
        // ========== 全局变量声明 ==========

        // 默认菜品列表
        const defaultPunishments = [
            '披萨', 
            '汉堡', 
            '拉面', 
            '寿司', 
            '麻辣香锅', 
            '火锅', 
            '沙拉', 
            '咖喱饭',
            '便当',
            '三明治',
            '意大利面',
            '墨西哥卷饼',
            '饺子',
            '炸虾',
            '冰淇淋'
        ];

        // 从LocalStorage加载数据
        let punishments = JSON.parse(localStorage.getItem('wheelPunishments')) || [...defaultPunishments];

        // 转盘状态控制变量
        let isSpinning = false;
        let currentRotation = 0;
        let currentPunishment = '';

        // 键盘快捷键状态
        let isSpacebarPressed = false;
        let spacebarCooldown = false;

        // 转盘扇形颜色列表 - 现代柔和色调
        const colors = [
            '#4a90e2', '#50c878', '#ff8c42', '#9c5bdf', '#ff6b6b',
            '#20b2aa', '#ffa726', '#7e8c8d', '#e91e63', '#009688',
            '#673ab7', '#ff5722', '#795548', '#607d8b', '#3f51b5',
            '#00bcd4', '#8bc34a', '#ffc107', '#9e9e9e', '#f44336'
        ];

        // ========== 工具函数 ==========

        /**
         * 更新菜品项目计数显示
         */
        function updatePunishmentCount() {
            const countElement = document.getElementById('punishmentCount');
            const count = punishments.length;
            countElement.textContent = `${count}项`;
        }

        /**
         * 保存菜品列表到LocalStorage
         */
        function savePunishments() {
            localStorage.setItem('wheelPunishments', JSON.stringify(punishments));
        }

        /**
         * 显示一键清空确认弹窗
         */
        function showClearConfirmation() {
            if (isSpinning) {
                alert('转盘旋转时不能清空菜品!');
                return;
            }

            if (punishments.length === 0) {
                alert('当前没有菜品可清空!');
                return;
            }

            // 更新清空数量显示
            document.getElementById('clearCount').textContent = punishments.length;

            // 显示确认弹窗
            const modal = document.getElementById('confirmClearModal');
            modal.classList.remove('hidden');
        }

        /**
         * 隐藏一键清空确认弹窗
         */
        function hideClearConfirmation() {
            document.getElementById('confirmClearModal').classList.add('hidden');
        }

        /**
         * 执行一键清空操作
         */
        function clearAllPunishments() {
            if (isSpinning) {
                alert('转盘旋转时不能清空菜品!');
                return;
            }

            // 记录清空前的数量
            const previousCount = punishments.length;

            // 清空菜品列表
            punishments = [];

            // 更新UI
            updatePunishmentList();
            updatePunishmentCount();
            updateWheel();

            // 保存到LocalStorage
            savePunishments();

            // 重置转盘状态
            currentRotation = 0;
            currentPunishment = '';
            const wheelSvg = document.getElementById('wheelSvg');
            wheelSvg.style.transitionDuration = '0ms';
            wheelSvg.style.transform = 'rotate(0deg)';

            // 隐藏结果
            const resultDisplay = document.getElementById('resultDisplay');
            resultDisplay.classList.remove('show');

            // 隐藏庆祝弹窗
            closeCelebration();

            // 显示操作反馈
            alert(`已清空 ${previousCount} 个菜品!`);

            // 隐藏确认弹窗
            hideClearConfirmation();
        }

        /**
         * 恢复默认菜单
         */
        function resetToDefaultMenu() {
            if (isSpinning) {
                alert('转盘旋转时不能恢复默认菜单!');
                return;
            }

            if (!confirm('确定要恢复默认菜单吗?当前所有菜品将被替换为默认菜品。')) {
                return;
            }

            // 恢复默认菜品列表
            punishments = [...defaultPunishments];

            // 更新UI
            updatePunishmentList();
            updatePunishmentCount();
            updateWheel();

            // 保存到LocalStorage
            savePunishments();

            alert('已恢复默认菜单!');
        }

        /**
         * 导入TXT文件并解析菜品列表
         */
        function importPunishmentsFromTxt(file) {
            const reader = new FileReader();

            reader.onload = function(e) {
                try {
                    const content = e.target.result;
                    // 按换行符分割,过滤空行和首尾空格
                    const newPunishments = content.split(/\r?\n/)
                        .map(item => item.trim())
                        .filter(item => item.length > 0);

                    if (newPunishments.length === 0) {
                        alert('文件为空或格式不正确!');
                        return;
                    }

                    // 合并并去重
                    const uniqueNewPunishments = newPunishments.filter(
                        item => !punishments.includes(item)
                    );

                    if (uniqueNewPunishments.length === 0) {
                        alert('所有菜品已存在!');
                        return;
                    }

                    // 添加到现有列表
                    punishments.push(...uniqueNewPunishments);

                    // 更新UI并保存
                    updatePunishmentList();
                    updatePunishmentCount();
                    updateWheel();
                    savePunishments();

                    alert(`成功导入 ${uniqueNewPunishments.length} 个新菜品!${newPunishments.length - uniqueNewPunishments.length > 0 ? `有 ${newPunishments.length - uniqueNewPunishments.length} 个重复菜品已跳过。` : ''}`);

                } catch (error) {
                    console.error('导入文件时出错:', error);
                    alert('文件解析失败,请检查文件格式!');
                }
            };

            reader.onerror = function() {
                alert('读取文件失败!');
            };

            reader.readAsText(file);
        }

        /**
         * 高亮开始按钮(快捷键提示)
         */
        function highlightStartButton() {
            const startBtn = document.getElementById('startBtn');
            startBtn.classList.add('highlight', 'pulse');

            setTimeout(() => {
                startBtn.classList.remove('pulse');
            }, 600);

            setTimeout(() => {
                startBtn.classList.remove('highlight');
            }, 1200);
        }

        // ========== DOM加载完成事件 ==========
        document.addEventListener('DOMContentLoaded', function() {
            // 初始化UI
            updatePunishmentList();
            updatePunishmentCount();
            updateWheel();

            // 输入框回车事件
            const input = document.getElementById('punishmentInput');
            input.addEventListener('keypress', function(e) {
                if (e.key === 'Enter') {
                    addPunishment();
                }
            });

            // 绑定按钮事件
            document.getElementById('addPunishmentBtn').addEventListener('click', addPunishment);
            document.getElementById('importBtn').addEventListener('click', function() {
                const fileInput = document.getElementById('importFile');
                if (fileInput.files.length > 0) {
                    importPunishmentsFromTxt(fileInput.files[0]);
                    fileInput.value = ''; // 清空文件选择
                } else {
                    alert('请先选择文件!');
                }
            });

            document.getElementById('startBtn').addEventListener('click', startGame);
            document.getElementById('resetBtn').addEventListener('click', resetWheel);
            document.getElementById('resetToDefaultBtn').addEventListener('click', resetToDefaultMenu);
            document.getElementById('closeCelebrationBtn').addEventListener('click', closeCelebration);
            document.getElementById('confirmCelebrationBtn').addEventListener('click', closeCelebration);

            // 一键清空相关事件
            document.getElementById('clearAllPunishmentsBtn').addEventListener('click', showClearConfirmation);
            document.getElementById('cancelClearBtn').addEventListener('click', hideClearConfirmation);
            document.getElementById('confirmClearBtn').addEventListener('click', clearAllPunishments);

            // 点击确认弹窗外部关闭弹窗
            document.getElementById('confirmClearModal').addEventListener('click', function(e) {
                if (e.target === this) {
                    hideClearConfirmation();
                }
            });

            // 点击庆祝弹窗外部关闭弹窗
            document.getElementById('celebrationModal').addEventListener('click', function(e) {
                if (e.target === this) {
                    closeCelebration();
                }
            });

            // 使用事件委托处理动态生成的删除按钮
            document.getElementById('punishmentList').addEventListener('click', function(e) {
                if (e.target.classList.contains('remove-btn')) {
                    const item = e.target.closest('.punishment-item');
                    const index = Array.from(this.children).indexOf(item);
                    if (index !== -1) {
                        removePunishment(index);
                    }
                }
            });

            // 键盘事件监听 - 空格键开始点餐
            document.addEventListener('keydown', function(e) {
                // 检查是否按下了空格键
                if (e.code === 'Space' || e.key === ' ') {
                    e.preventDefault(); // 防止页面滚动

                    // 检查是否在输入框中
                    const activeElement = document.activeElement;
                    const isInputFocused = activeElement.tagName === 'INPUT' || activeElement.tagName === 'TEXTAREA';

                    // 检查是否有弹窗打开
                    const isModalOpen = !document.getElementById('celebrationModal').classList.contains('hidden') || 
                                       !document.getElementById('confirmClearModal').classList.contains('hidden');

                    // 如果不在输入框中且没有弹窗打开,且转盘没有在旋转
                    if (!isInputFocused && !isModalOpen && !isSpinning) {
                        startGame();
                        highlightStartButton();
                    }
                }
            });

            // 添加空格键提示的视觉反馈
            document.addEventListener('keyup', function(e) {
                if (e.code === 'Space' || e.key === ' ') {
                    isSpacebarPressed = false;
                }
            });
        });

        // ========== 核心功能函数 ==========

        /**
         * 添加新菜品项目
         */
        function addPunishment() {
            const input = document.getElementById('punishmentInput');
            const name = input.value.trim();

            // 输入验证
            if (!name) {
                alert('请输入菜品名称!');
                return;
            }

            if (name.length > 30) {
                alert('菜品名称过长,请控制在30字以内!');
                return;
            }

            if (!punishments.includes(name)) {
                punishments.push(name);
                input.value = '';
                input.focus();
                updatePunishmentList();
                updatePunishmentCount();
                updateWheel();
                savePunishments();
            } else {
                alert('该菜品已存在!');
            }
        }

        /**
         * 删除菜品项目
         */
        function removePunishment(index) {
            if (isSpinning) {
                alert('转盘旋转时不能删除菜品!');
                return;
            }

            if (confirm('确定要删除这个菜品吗?')) {
                punishments.splice(index, 1);
                updatePunishmentList();
                updatePunishmentCount();
                updateWheel();
                savePunishments();
            }
        }

        /**
         * 更新菜品列表UI
         */
        function updatePunishmentList() {
            const list = document.getElementById('punishmentList');

            // 处理空列表状态
            if (punishments.length === 0) {
                list.innerHTML = '<div class="empty-state">🍽️ 菜品菜单是空的,请先添加菜品!</div>';
                return;
            }

            // 生成列表HTML
            list.innerHTML = punishments.map((punishment, index) => `
                <div class="punishment-item ${currentPunishment === punishment ? 'active' : ''}">
                    <span>${punishment}</span>
                    <button class="remove-btn" title="删除此项">×</button>
                </div>
            `).join('');
        }

        /**
         * 更新转盘SVG图形
         */
        function updateWheel() {
            const sectionsGroup = document.getElementById('wheelSections');
            sectionsGroup.innerHTML = '';

            // 空转盘状态
            if (punishments.length === 0) {
                const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
                path.setAttribute('d', 'M 0 0 L 240 0 A 240 240 0 1 1 -240 0 Z');
                path.setAttribute('fill', '#f5f5f5');
                path.setAttribute('stroke', '#e0e0e0');
                path.setAttribute('stroke-width', '2');

                const text = document.createElementNS('http://www.w3.org/2000/svg', 'text');
                text.setAttribute('x', '0');
                text.setAttribute('y', '0');
                text.setAttribute('fill', '#999');
                text.setAttribute('font-size', '24');
                text.setAttribute('font-weight', '500');
                text.setAttribute('text-anchor', 'middle');
                text.setAttribute('dominant-baseline', 'middle');
                text.textContent = '请添加菜品';

                sectionsGroup.appendChild(path);
                sectionsGroup.appendChild(text);
                return;
            }

            // 计算每个扇形的角度
            const sectionAngle = 360 / punishments.length;
            const radius = 240;

            // 生成每个扇形
            punishments.forEach((punishment, index) => {
                // 计算起始和结束角度(弧度制)
                const startAngle = index * sectionAngle;
                const endAngle = (index + 1) * sectionAngle;

                const startAngleRad = (startAngle * Math.PI) / 180;
                const endAngleRad = (endAngle * Math.PI) / 180;

                // 判断是否为大圆弧
                const largeArcFlag = sectionAngle > 180 ? 1 : 0;

                // 计算弧线起点和终点坐标
                const x1 = Math.cos(startAngleRad) * radius;
                const y1 = Math.sin(startAngleRad) * radius;
                const x2 = Math.cos(endAngleRad) * radius;
                const y2 = Math.sin(endAngleRad) * radius;

                // 构建扇形路径数据
                const pathData = `M 0 0 L ${x1} ${y1} A ${radius} ${radius} 0 ${largeArcFlag} 1 ${x2} ${y2} Z`;

                // 创建扇形路径元素
                const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
                path.setAttribute('d', pathData);
                path.setAttribute('fill', colors[index % colors.length]);
                path.setAttribute('stroke', '#fff');
                path.setAttribute('stroke-width', '3');
                path.setAttribute('filter', 'url(#shadow)');

                // 计算文本位置
                const midAngle = (startAngle + endAngle) / 2;
                const textRadius = radius * 0.65;
                const textX = Math.cos((midAngle * Math.PI) / 180) * textRadius;
                const textY = Math.sin((midAngle * Math.PI) / 180) * textRadius;

                // 根据项目数量动态调整字体大小
                let fontSize = 20;
                if (punishments.length > 20) fontSize = 12;
                else if (punishments.length > 15) fontSize = 14;
                else if (punishments.length > 10) fontSize = 16;
                else if (punishments.length > 8) fontSize = 18;

                // 创建文本元素
                const text = document.createElementNS('http://www.w3.org/2000/svg', 'text');
                text.setAttribute('x', textX);
                text.setAttribute('y', textY);
                text.setAttribute('fill', '#fff');
                text.setAttribute('font-size', fontSize);
                text.setAttribute('font-weight', '700');
                text.setAttribute('text-anchor', 'middle');
                text.setAttribute('dominant-baseline', 'middle');
                text.setAttribute('transform', `rotate(${midAngle}, ${textX}, ${textY})`);
                text.setAttribute('style', 'text-shadow: 0 1px 2px rgba(0,0,0,0.3);');

                // 智能文本截断处理
                const maxChars = Math.max(3, Math.floor(60 / fontSize));
                let displayText = punishment;
                if (punishment.length > maxChars) {
                    displayText = punishment.substring(0, maxChars) + '..';
                }
                text.textContent = displayText;

                // 添加到SVG
                sectionsGroup.appendChild(path);
                sectionsGroup.appendChild(text);
            });
        }

        /**
         * 开始转盘游戏
         */
        function startGame() {
            // 前置检查
            if (punishments.length === 0) {
                alert('请先添加菜品!');
                return;
            }

            if (isSpinning) return;

            // 设置旋转状态
            isSpinning = true;
            currentPunishment = '';

            // 更新按钮状态
            const startBtn = document.getElementById('startBtn');
            const startBtnText = document.getElementById('startBtnText');
            startBtn.disabled = true;
            startBtnText.textContent = '⏳ 点餐中...';

            // 隐藏上一个结果
            const resultDisplay = document.getElementById('resultDisplay');
            resultDisplay.classList.remove('show');

            // 生成随机动画参数(4-8秒随机时长)
            const randomDuration = 4000 + Math.random() * 4000;

            // 计算最终旋转角度
            const randomDegreeOffset = Math.floor(Math.random() * 360);
            const randomSpins = 6 + Math.floor(Math.random() * 6); // 6-11圈
            const finalRotation = currentRotation + (randomSpins * 360) + randomDegreeOffset;

            // 更新当前角度
            currentRotation = finalRotation;

            // 应用旋转动画
            const wheelSvg = document.getElementById('wheelSvg');
            wheelSvg.style.transitionDuration = `${randomDuration}ms`;
            wheelSvg.style.transform = `rotate(${currentRotation}deg)`;

            // 动画结束后计算结果
            setTimeout(() => {
                isSpinning = false;

                // 转盘指针固定角度(顶部)
                const POINTER_ANGLE = 270;

                // 计算实际旋转角度
                const actualRotation = currentRotation % 360;

                // 计算指针指向的角度
                let winningAngle = (POINTER_ANGLE - actualRotation);

                // 角度归一化处理
                winningAngle = winningAngle % 360;
                if (winningAngle < 0) {
                    winningAngle += 360;
                }

                // 根据角度计算选中扇形的索引
                const sectionAngle = 360 / punishments.length;
                const winningIndex = Math.floor(winningAngle / sectionAngle);

                // 获取选中的菜品
                currentPunishment = punishments[winningIndex] || punishments[0];

                // 恢复按钮状态
                startBtn.disabled = false;
                startBtnText.textContent = '🎰 再次点餐';

                // 显示结果
                document.getElementById('winnerPunishment').textContent = currentPunishment;
                resultDisplay.classList.add('show');

                // 更新列表高亮
                updatePunishmentList();

                // 显示庆祝弹窗
                showCelebration();
            }, randomDuration);
        }

        /**
         * 重置转盘
         */
        function resetWheel() {
            if (isSpinning) {
                alert('转盘旋转时不能重置!');
                return;
            }

            if (!confirm('确定要重置转盘吗?')) return;

            currentRotation = 0;
            currentPunishment = '';

            // 立即重置转盘
            const wheelSvg = document.getElementById('wheelSvg');
            wheelSvg.style.transitionDuration = '0ms';
            wheelSvg.style.transform = 'rotate(0deg)';

            // 隐藏结果
            const resultDisplay = document.getElementById('resultDisplay');
            resultDisplay.classList.remove('show');

            // 更新UI
            updatePunishmentList();
            closeCelebration();
        }

        /**
         * 显示庆祝弹窗
         */
        function showCelebration() {
            const modal = document.getElementById('celebrationModal');
            document.getElementById('celebrationPunishment').textContent = currentPunishment;

            // 显示并添加动画
            modal.classList.remove('hidden');
            const content = modal.querySelector('.celebration-content');
            content.classList.remove('modal-enter');
            void content.offsetWidth; // 触发重排
            content.classList.add('modal-enter');
        }

        /**
         * 关闭庆祝弹窗
         */
        function closeCelebration() {
            document.getElementById('celebrationModal').classList.add('hidden');
        }
    </script>
</body>
</html>