吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 706|回复: 14
收起左侧

[其他求助] 求助大佬,修改如下连词成句html,具体要求如下。

[复制链接]
nyboy0377 发表于 2025-4-15 20:44
76吾爱币
本帖最后由 nyboy0377 于 2025-4-17 19:01 编辑

@superychen
文件地址:        链接: https://pan.baidu.com/s/1-jLc9z3tFxFIzB8zyIS-yA?pwd=52pj 提取码: 52pj
1.在连词成句模式下,单击汉语意思,可以显示英语原句,便于学生在多次弄错以后   的核对。和学习模式下一样就行。其实就是汉语和英语的自由切换!
2.英语原句中有部分单词重复的情况下,打乱以后,在第二行打乱备选区只出现一次。直至重复单词用完后,才消失。这样可以吗?
如此句中,单词to出现了两次。而在打乱备选区,仅出现一次即可。Silently, he turned around and was determined to do whatever he could to save her life.
3.每个组装的句子的首字母默认是大写字母,只要从备选区拖动到句子的第一个单词,首字母自动变为大写。
4.在打乱备选区中,点击单词可以带出单词读音。直接使用有道词典api就行。谢谢。

最佳答案

查看完整内容

https://wwft.lanzoul.com/i8Gcn2u3cbuj 下载地址,内附了一个演示地址,你测试一下。我还没时间在低版本系统上测试的。

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

vocan 发表于 2025-4-15 20:44
nyboy0377 发表于 2025-4-21 08:34
我电脑系统是win10。方便把html发给我吗?谢谢了!感恩大佬!

https://wwft.lanzoul.com/i8Gcn2u3cbuj

下载地址,内附了一个演示地址,你测试一下。我还没时间在低版本系统上测试的。
墨羽风 发表于 2025-4-15 21:35
 楼主| nyboy0377 发表于 2025-4-15 21:39
墨羽风 发表于 2025-4-15 21:35
加一个分析什么句型 才有利于学习语法把。

您有具体的想法吗?如何加呢?
求赐教
墨羽风 发表于 2025-4-15 21:46
nyboy0377 发表于 2025-4-15 21:39
您有具体的想法吗?如何加呢?
求赐教

具体我没找到api 应该可以通过爬虫获取
 楼主| nyboy0377 发表于 2025-4-15 21:49
墨羽风 发表于 2025-4-15 21:46
具体我没找到api 应该可以通过爬虫获取

谢谢您!
这个应用主要是帮助学生快速,准确背诵作文经典句子的。检测学生掌握情况的。
不太需要分子句子结构!
 楼主| nyboy0377 发表于 2025-4-16 16:54
@superychen
还有一个请求。连词成句下,无法拖动打乱后的单词。笔记本电脑有鼠标,没有问题。
可是教室的一体机,没有鼠标,纯粹依靠触摸屏的手动,但是拖不动。这个问题,可以解决吗?谢谢!
vocan 发表于 2025-4-19 22:27
https://dict.youdao.com/dictvoice?audio=[word]&type=1 发音调用地址,其实你上面的单击发音和单击可以拖动是冲突的。
vocan 发表于 2025-4-19 23:24
英语原句中有部分单词重复的情况下,打乱以后,在第二行打乱备选区只出现一次。直至重复单词用完后,才消失。这样可以吗?
如此句中,单词to出现了两次。而在打乱备选区,仅出现一次即可。Silently, he turned around and was determined to do whatever he could to save her life.
这个功能没必要成,如果学生发现放错了地方,再拖回到上面你还得处理这个问题。

4.在打乱备选区中,点击单词可以带出单词读音。直接使用有道词典api就行。谢谢。 (加发音会存在操作的过程按下拖动时每个都发音的,改为了双击发音)

程序已知BUG,单词可以随意拖动到其它组的句子中(在代码中已修复)。

小提示:发音的响应时间取决于网速,请等待响应后再双击其它单词发音。不分乱序或是句子中均可发音。
在原基础上美英,英英,发音选择
首字母大,单词发音,中英转换已实现。
完整的代码附后:

[HTML] 纯文本查看 复制代码
<!DOCTYPE html>
<html lang="zh">

<head>
    <meta charset="UTF-8">
    <title>英语练习</title>
    <script src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/xlsx/0.18.2/xlsx.full.min.js"></script>
    <style>
        /* 重置和基础样式 */
        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
            line-height: 1.6;
            color: #333;
            background-color: #f5f7fa;
        }

        /* 容器样式 */
        .container {
            max-width: 800px;
            margin: 40px auto;
            padding: 0 20px;
        }

        /* 标题样式 */
        h2 {
            font-size: 24px;
            font-weight: 500;
            color: #2c3e50;
            margin-bottom: 20px;
        }

        /* 文件输入框样式 */
        .file-input-container {
            margin-bottom: 30px;
        }

        #fileInput {
            display: none;
        }

        .file-input-label {
            display: inline-block;
            padding: 12px 24px;
            background-color: #3498db;
            color: white;
            border-radius: 6px;
            cursor: pointer;
            transition: background-color 0.3s;
        }

        .file-input-label:hover {
            background-color: #2980b9;
        }

        .file-type {
            margin-top: 10px;
            color: #666;
            font-size: 14px;
        }

        /* 语音控制面板样式 */
        .voice-controls {
            background: white;
            border-radius: 12px;
            padding: 20px;
            box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
            margin-bottom: 30px;
        }

        .voice-select {
            margin-bottom: 20px;
        }

        .voice-select select {
            width: 100%;
            padding: 10px;
            border: 1px solid #ddd;
            border-radius: 6px;
            font-size: 16px;
            color: #2c3e50;
        }

        .slider-container {
            margin: 15px 0;
        }

        .slider-container label {
            display: block;
            margin-bottom: 8px;
            color: #2c3e50;
        }

        input[type="range"] {
            width: 100%;
            height: 6px;
            background: #e0e0e0;
            border-radius: 3px;
            outline: none;
        }

        .slider-value {
            display: inline-block;
            min-width: 40px;
            text-align: center;
            margin-left: 10px;
            color: #666;
        }

        /* 开始练习按钮样式 */
        .view-modal-btn {
            display: block;
            width: 100%;
            padding: 15px;
            background-color: #2ecc71;
            color: white;
            border: none;
            border-radius: 8px;
            font-size: 18px;
            cursor: pointer;
            transition: background-color 0.3s;
            margin: 30px 0;
        }

        .view-modal-btn:hover {
            background-color: #27ae60;
        }

        /* Modal 样式优化 */
        .modal {
            display: none;
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: rgb(0, 0, 0);
            z-index: 1000;
        }

        /* 全屏时的样式 */
        .modal.fullscreen {
            width: 100vw;
            height: 100vh;
        }

        .modal-content {
            position: relative;
            height: 100%;
            overflow-y: scroll;
            padding: 80px 40px 40px;
            max-width: 1600px;
            margin: 0 auto;
            scrollbar-width: none;
            -ms-overflow-style: none;
            scroll-behavior: smooth;
        }

        .modal-content::-webkit-scrollbar {
            display: none;
        }

        .modal-content::-webkit-scrollbar-track,
        .modal-content::-webkit-scrollbar-thumb,
        .modal-content::-webkit-scrollbar-thumb:hover {
            display: none;
        }

        .close-btn {
            position: fixed;
            top: 30px;
            right: 40px;
            color: white;
            font-size: 40px;
            cursor: pointer;
            background: none;
            border: none;
            padding: 15px;
            z-index: 1001;
        }

        .sentence-pair {
            background: rgba(255, 255, 255, 0.03);
            border-radius: 16px;
            padding: 30px;
            margin-bottom: 30px;
            display: flex;
            align-items: flex-start;
            transition: background-color 0.3s ease;
            transform-origin: center;
        }

        .sentence-pair:hover {
            background: rgba(255, 255, 255, 0.05);
        }

        .sentence-content {
            flex: 1;
            min-width: 0;
            padding-right: 20px;
        }

        .english,
        .chinese {
            word-wrap: break-word;
            /* 确保长文本会换行 */
        }

        /* 添加滚动条样式 */
        .modal-content::-webkit-scrollbar {
            width: 10px;
        }

        .modal-content::-webkit-scrollbar-track {
            background: rgba(255, 255, 255, 0.1);
        }

        .modal-content::-webkit-scrollbar-thumb {
            background: rgba(255, 255, 255, 0.3);
            border-radius: 5px;
        }

        .modal-content::-webkit-scrollbar-thumb:hover {
            background: rgba(255, 255, 255, 0.5);
        }

        /* 播放按钮基础样式 */
        .play-button {
            width: 60px;
            height: 60px;
            border: none;
            border-radius: 50%;
            background-color: rgba(52, 152, 219, 0.9);
            color: white;
            cursor: pointer;
            margin-right: 25px;
            padding: 0;
            position: relative;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: all 0.3s ease;
            box-shadow: 0 2px 8px rgba(52, 152, 219, 0.3);
            margin-top: 8px;
        }

        /* 播放图标容器 */
        .play-button .icon-container {
            width: 36px;
            height: 36px;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: all 0.3s ease;
        }

        /* 播放三角形图标 */
        .play-button .play-icon {
            width: 0;
            height: 0;
            border-style: solid;
            border-width: 18px 0 18px 27px;
            border-color: transparent transparent transparent #ffffff;
            margin-left: 4px;
        }

        /* 停止方块图标 */
        .play-button .stop-icon {
            width: 24px;
            height: 24px;
            background-color: #ffffff;
        }

        /* 加载动画 */
        @keyframes spin {
            to {
                transform: rotate(360deg);
            }
        }

        .play-button .loading-icon {
            width: 28px;
            height: 28px;
            border: 2px solid rgba(255, 255, 255, 0.3);
            border-top-color: white;
            border-radius: 50%;
            animation: spin 0.8s linear infinite;
        }

        /* 播放状态样式 */
        .play-button.playing {
            background-color: rgba(231, 76, 60, 0.9);
            box-shadow: 0 2px 8px rgba(231, 76, 60, 0.3);
        }

        /* 悬停效果 */
        .play-button:not(.loading):hover {
            transform: scale(1.05);
        }

        .play-button:not(.loading):active {
            transform: scale(0.95);
        }

        @keyframes ripple {
            0% {
                transform: scale(1);
                opacity: 0.4;
            }

            100% {
                transform: scale(1.5);
                opacity: 0;
            }
        }

        @keyframes pulse {
            0% {
                transform: scale(1);
                opacity: 0.5;
            }

            50% {
                transform: scale(1.1);
                opacity: 0.2;
            }

            100% {
                transform: scale(1);
                opacity: 0.5;
            }
        }

        .play-button.playing::before {
            content: '';
            position: absolute;
            top: -2px;
            left: -2px;
            right: -2px;
            bottom: -2px;
            border-radius: 50%;
            border: 2px solid rgba(231, 76, 60, 0.5);
            animation: pulse 2s ease-in-out infinite;
        }

        .play-button::after {
            content: '';
            position: absolute;
            width: 100%;
            height: 100%;
            border-radius: 50%;
            background-color: rgba(255, 255, 255, 0.2);
            opacity: 0;
            transform: scale(1);
            pointer-events: none;
        }

        .play-button:active::after {
            animation: ripple 0.6s ease-out;
        }

        .sentence-pair {
            background: rgba(255, 255, 255, 0.03);
            border-radius: 12px;
            padding: 16px;
            margin-bottom: 16px;
            display: flex;
            align-items: center;
            transition: background-color 0.3s ease;
        }

        .sentence-pair:hover {
            background: rgba(255, 255, 255, 0.05);
        }

        .english {
            font-size: 50px;
            line-height: 1.3;
            color: #fff;
            margin-bottom: 0;
            transition: all 0.3s ease;
            font-weight: 500;
            opacity: 1;
            font-family: 'HengShuiTi', sans-serif;
            display: block;
        }

        .english.hidden {
            display: none;
        }

        .chinese {
            display: block;
            font-size: 40px;
            line-height: 1.3;
            color: #f1c40f;
            opacity: 0;
            transition: all 0.3s ease;
            visibility: hidden;
            height: 0;
            overflow: hidden;
        }

        .chinese.show {
            opacity: 1;
            visibility: visible;
            height: auto;
        }

        .close-btn {
            font-size: 36px;
            color: #fff;
            opacity: 0.8;
            transition: opacity 0.3s;
        }

        .close-btn:hover {
            opacity: 1;
        }

        /* 表格样式优化 */
        table {
            width: 100%;
            background: white;
            border-radius: 12px;
            overflow: hidden;
            box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
            margin-top: 30px;
        }

        th {
            background-color: #f8f9fa;
            color: #2c3e50;
            font-weight: 500;
            padding: 15px;
            text-align: left;
        }

        td {
            padding: 15px;
            border-bottom: 1px solid #eee;
        }

        tr:last-child td {
            border-bottom: none;
        }

        .file-options {
            margin-top: 15px;
        }

        .checkbox-container {
            display: flex;
            align-items: center;
            margin-bottom: 8px;
            cursor: pointer;
        }

        .checkbox-container input[type="checkbox"] {
            margin-right: 8px;
            width: 16px;
            height: 16px;
            cursor: pointer;
        }

        .checkbox-label {
            color: #666;
            font-size: 14px;
        }

        .file-info {
            display: none;
            margin-top: 15px;
            background-color: #fff;
            border-radius: 8px;
            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
            overflow: hidden;
        }

        .file-info-header {
            padding: 15px;
            display: flex;
            justify-content: space-between;
            align-items: center;
            border-bottom: 1px solid #eee;
        }

        .file-info-basic {
            flex: 1;
        }

        .file-info-item {
            color: #2c3e50;
            margin: 4px 0;
            font-size: 14px;
        }

        .file-info-item span {
            color: #3498db;
            font-weight: 500;
        }

        .preview-toggle {
            display: flex;
            align-items: center;
            cursor: pointer;
            padding: 8px 12px;
            border-radius: 4px;
            transition: background-color 0.2s;
        }

        .preview-toggle:hover {
            background-color: #f5f7fa;
        }

        .preview-text {
            color: #2c3e50;
            font-size: 14px;
            margin-right: 8px;
        }

        .preview-icon {
            color: #666;
            transition: transform 0.3s;
        }

        .preview-icon.open {
            transform: rotate(180deg);
        }

        .preview-content {
            max-height: 0;
            overflow: hidden;
            transition: max-height 0.3s ease-out;
        }

        .preview-content.open {
            max-height: 500px;
            overflow-y: auto;
        }

        /* 修改表格样式 */
        table {
            width: 100%;
            margin: 0;
            border: none;
        }

        th {
            background-color: #f8f9fa;
            padding: 12px 15px;
            font-weight: 500;
        }

        td {
            padding: 12px 15px;
            border-bottom: 1px solid #eee;
        }

        tr:last-child td {
            border-bottom: none;
        }

        .progress-ring {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
        }

        .progress-ring circle {
            transform: rotate(-90deg);
            transform-origin: 50% 50%;
            stroke: #fff;
            stroke-width: 2;
            fill: none;
        }

        /* 加载动画样式 */
        @keyframes rotate {
            from {
                transform: rotate(0deg);
            }

            to {
                transform: rotate(360deg);
            }
        }

        .loading-icon {
            display: inline-block;
            width: 20px;
            height: 20px;
            border: 2px solid rgba(255, 255, 255, 0.3);
            border-radius: 50%;
            border-top-color: #fff;
            animation: rotate 1s linear infinite;
        }

        /* 修改播放按钮相样式 */
        .play-button {
            position: relative;
            width: 66px;
            height: 66px;
            border: none;
            border-radius: 50%;
            background-color: #3498db;
            color: white;
            cursor: pointer;
            margin-right: 20px;
            padding: 0;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: all 0.3s ease;
        }

        .play-button.loading {
            background-color: #7f8c8d;
            cursor: wait;
        }

        .play-button.loading:hover {
            transform: none;
            box-shadow: 0 2px 6px rgba(52, 152, 219, 0.3);
        }

        @keyframes progress {
            from {
                stroke-dashoffset: var(--circumference);
            }

            to {
                stroke-dashoffset: 0;
            }
        }

        .progress-ring {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            z-index: 1;
        }

        .progress-ring circle {
            transform: rotate(-90deg);
            transform-origin: 50% 50%;
            stroke: rgba(255, 255, 255, 0.8);
            stroke-width: 2.5;
            fill: none;
        }

        .progress-ring circle.animating {
            animation: progress linear forwards;
            animation-duration: var(--duration);
        }

        .progress-ring circle.paused {
            animation-play-state: paused;
        }

        @keyframes playing-wave {
            0% {
                transform: scale(1);
                opacity: 0.8;
            }

            50% {
                transform: scale(1.2);
                opacity: 0.4;
            }

            100% {
                transform: scale(1);
                opacity: 0.8;
            }
        }

        .play-button {
            position: relative;
            width: 66px;
            height: 66px;
            border: none;
            border-radius: 50%;
            background-color: #3498db;
            color: white;
            cursor: pointer;
            margin-right: 20px;
            padding: 0;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: all 0.3s ease;
            box-shadow: 0 2px 6px rgba(52, 152, 219, 0.3);
        }

        .play-button::after {
            content: '';
            position: absolute;
            top: -2px;
            left: -2px;
            right: -2px;
            bottom: -2px;
            border-radius: 50%;
            border: 2px solid #3498db;
            opacity: 0;
            pointer-events: none;
        }

        .play-button.playing::after {
            animation: playing-wave 2s ease-in-out infinite;
        }

        .play-button.playing {
            background-color: #e74c3c;
            box-shadow: 0 2px 6px rgba(231, 76, 60, 0.3);
        }

        .play-button.playing::after {
            border-color: #e74c3c;
        }

        /* 添加重试状态样式 */
        .play-button.retry {
            background-color: #e74c3c;
            animation: pulse 2s infinite;
        }

        .play-button .retry-icon {
            width: 16px;
            height: 16px;
            border: 2px solid #ffffff;
            border-radius: 50%;
            position: relative;
        }

        .play-button .retry-icon::after {
            content: '';
            position: absolute;
            top: 2px;
            right: -2px;
            width: 0;
            height: 0;
            border-style: solid;
            border-width: 4px;
            border-color: transparent transparent transparent #ffffff;
        }

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

            50% {
                transform: scale(1.05);
            }

            100% {
                transform: scale(1);
            }
        }

        /* 添加句子激活状态的样式 */
        .sentence-pair {
            transition: all 0.3s ease;
            transform-origin: center;
        }

        .sentence-pair.active {
            transform: scale(1.02);
            background: rgba(255, 255, 255, 0.08);
            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
        }

        /* 添加字体声明 */
        @font-face {
            font-family: 'HengShuiTi';
            src: url('./heng-shui.ttf') format('truetype');
        }

        /* 修改预览文本样式 */
        .font-size-preview {
            background: rgb(0, 0, 0);
            border-radius: 12px;
            padding: 20px;
            margin-top: 15px;
            display: block;
        }

        .preview-text-en {
            color: #fff;
            font-family: 'HengShuiTi', sans-serif;
            margin-bottom: 10px;
        }

        .preview-text-zh {
            color: #f1c40f;
        }

        /* 连词成句样式 */
        .word-arrange-container {
            background: rgba(255, 255, 255, 0.03);
            border-radius: 16px;
            padding: 30px;
            margin-bottom: 30px;
            display: flex;
            flex-direction: column;
            transition: all 0.3s ease;
        }

        .word-arrange-container.active {
            transform: scale(1.02);
            background: rgba(255, 255, 255, 0.08);
            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
        }

        .word-arrange-container.correct {
            background: rgba(46, 204, 113, 0.2);
            box-shadow: 0 4px 20px rgba(46, 204, 113, 0.3);
        }

        .word-arrange-container.incorrect {
            background: rgba(231, 76, 60, 0.2);
            box-shadow: 0 4px 20px rgba(231, 76, 60, 0.3);
        }

        .chinese-text {
            color: #f1c40f;
            font-size: 40px;
            margin-bottom: 20px;
        }

        .word-bank {
            display: flex;
            flex-wrap: wrap;
            margin-bottom: 20px;
            min-height: 70px;
            padding: 10px;
            background: rgba(255, 255, 255, 0.05);
            border-radius: 8px;
        }

        .sentence-template {
            display: flex;
            flex-wrap: wrap;
            min-height: 70px;
            padding: 10px;
            background: rgba(255, 255, 255, 0.05);
            border-radius: 8px;
            align-items: center;
        }

        .draggable-word {
            display: inline-flex;
            padding: 2px 8px;
            margin: 5px;
            background: rgba(52, 152, 219, 0.7);
            color: white;
            border-radius: 4px;
            cursor: move;
            user-select: none;
            transition: all 0.2s ease;
            font-family: 'HengShuiTi', sans-serif;
        }

        .draggable-word:hover {
            background: rgba(52, 152, 219, 0.9);
            transform: translateY(-2px);
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
        }

        .draggable-word.dragging {
            opacity: 0.5;
            background: rgba(52, 152, 219, 1);
        }

        .word-slot {
            display: inline-flex;
            height: 40px;
            min-width: 50px;
            margin: 5px;
            background: rgba(255, 255, 255, 0.1);
            border-bottom: 2px solid #3498db;
            border-radius: 2px;
            align-items: center;
            justify-content: center;
        }

        .punctuation {
            display: inline-flex;
            color: white;
            font-size: 26px;
            margin: 5px;
            padding: 8px 0;
            align-items: center;
        }

        .feedback-message {
            margin-top: 20px;
            padding: 15px;
            border-radius: 8px;
            font-size: 24px;
            text-align: center;
            opacity: 0;
            transition: all 0.3s ease;
            height: 0;
            overflow: hidden;
        }

        .feedback-message.correct {
            background: rgba(46, 204, 113, 0.2);
            color: #2ecc71;
            opacity: 1;
            height: auto;
            padding: 15px;
        }

        .feedback-message.incorrect {
            background: rgba(231, 76, 60, 0.2);
            color: #e74c3c;
            opacity: 1;
            height: auto;
            padding: 15px;
        }

        /* 添加干扰词样式 */
        .draggable-word.distractor-word {
            /* 移除特殊背景色,使用与普通单词相同的样式 */
            background: rgba(52, 152, 219, 0.7);
        }

        .draggable-word.distractor-word:hover {
            /* 移除特殊悬停背景色,使用与普通单词相同的样式 */
            background: rgba(52, 152, 219, 0.9);
        }
    </style>
</head>

<body>
    <div class="container">
        <h2>英语学习助手</h2>
        <div class="file-input-container">
            <label for="fileInput" class="file-input-label">选择文件</label>
            <input type="file" id="fileInput" accept=".csv,.xls,.xlsx">
            <div class="file-options">
                <label class="checkbox-container">
                    <input type="checkbox" id="skipHeader" checked>
                    <span class="checkbox-label">忽略标题行</span>
                </label>
                <div class="file-type">支持的格式:CSV, XLS, XLSX</div>
            </div>
            <div id="fileInfo" class="file-info">
                <div class="file-info-header">
                    <div class="file-info-basic">
                        <div class="file-info-item">文件名:<span id="fileName">-</span></div>
                        <div class="file-info-item">总行数:<span id="totalRows">-</span></div>
                        <div class="file-info-item">有效行数:<span id="validRows">-</span></div>
                    </div>
                    <div class="preview-toggle">
                        <span class="preview-text">预览内容</span>
                        <span class="preview-icon">▼</span>
                    </div>
                </div>
                <div class="preview-content">
                    <table id="csvTable">
                        <thead>
                            <tr>
                                <th>英文</th>
                                <th>中文</th>
                                <th>干扰词</th>
                            </tr>
                        </thead>
                        <tbody id="tableBody"></tbody>
                    </table>
                </div>
            </div>
        </div>
        <div class="voice-select" style="margin: 20px 0;">
            <label for="voice">选择语音:</label>
            <select id="voice" name="voice" style="padding: 5px; min-width: 200px;">
                <option value="zh-CN-XiaoxiaoMultilingualNeural">晓晓 多语言</option>
                <option value="zh-CN-XiaoxiaoNeural">晓晓</option>
                <option value="zh-CN-YunxiNeural">云希</option>
                <option value="zh-CN-YunjianNeural">云健</option>
                <option value="zh-CN-XiaoyiNeural">晓伊</option>
                <option value="zh-CN-YunyangNeural">云扬</option>
                <option value="zh-CN-XiaochenNeural">晓辰</option>
                <option value="zh-CN-XiaohanNeural">晓涵</option>
                <option value="zh-CN-XiaomengNeural">晓梦</option>
                <option value="zh-CN-XiaomoNeural">晓墨</option>
                <option value="zh-CN-XiaoqiuNeural">晓秋</option>
                <option value="zh-CN-XiaorouNeural">晓柔</option>
                <option value="zh-CN-XiaoruiNeural">晓睿</option>
                <option value="zh-CN-XiaoshuangNeural">晓双</option>
                <option value="zh-CN-XiaoyanNeural">晓颜</option>
                <option value="zh-CN-XiaoyouNeural">晓悠</option>
                <option value="zh-CN-XiaozhenNeural">晓甄</option>
                <option value="zh-CN-YunfengNeural">云枫</option>
                <option value="zh-CN-YunhaoNeural">云皓</option>
                <option value="zh-CN-YunjieNeural">云杰</option>
                <option value="zh-CN-YunxiaNeural">云夏</option>
                <option value="zh-CN-YunyeNeural">云野</option>
                <option value="zh-CN-YunzeNeural">云泽</option>
                <option value="zh-CN-YunfanMultilingualNeural">Yunfan 多语言</option>
                <option value="zh-CN-YunxiaoMultilingualNeural">Yunxiao 多语言</option>
                <option value="zh-CN-guangxi-YunqiNeural">云奇 广西</option>
                <option value="zh-CN-henan-YundengNeural">云登</option>
                <option value="zh-CN-liaoning-XiaobeiNeural">晓北 辽宁</option>
                <option value="zh-CN-liaoning-YunbiaoNeural">云彪 辽宁</option>
                <option value="zh-CN-shaanxi-XiaoniNeural">晓妮</option>
                <option value="zh-CN-shandong-YunxiangNeural">云翔</option>
                <option value="zh-CN-sichuan-YunxiNeural">云希 四川</option>
                <option value="zh-HK-HiuMaanNeural">曉曼</option>
                <option value="zh-HK-WanLungNeural">雲龍</option>
                <option value="zh-HK-HiuGaaiNeural">曉佳</option>
                <option value="zh-TW-HsiaoChenNeural">曉臻</option>
                <option value="zh-TW-YunJheNeural">雲哲</option>
                <option value="zh-TW-HsiaoYuNeural">曉雨</option>
            </select>
        </div>
        <div class="voice-controls">
            <div class="slider-container">
                <label for="rate">语速:</label>
                <input type="range" id="rate" min="-100" max="100" value="0">
                <span class="slider-value" id="rateValue">0</span>
            </div>
            <div class="slider-container">
                <label for="pitch">语调:</label>
                <input type="range" id="pitch" min="-100" max="100" value="0">
                <span class="slider-value" id="pitchValue">0</span>
            </div>
            <div class="slider-container">
                <label for="fontSize">字体大小:</label>
                <input type="range" id="fontSize" min="30" max="80" value="39">
                <span class="slider-value" id="fontSizeValue">50px</span>
            </div>
            <div class="slider-container">
                <label for="pronunciationType">发音类型:</label>
                <select id="pronunciationType" style="width: 120px; padding: 5px; margin-left: 10px;">
                    <option value="2">美式发音</option>
                    <option value="1">英式发音</option>
                </select>
            </div>
            <div class="font-size-preview">
                <div class="preview-text-en">The quick brown fox jumps over the lazy dog.</div>
                <div class="preview-text-zh">敏捷的棕色狐狸跳过了懒狗。</div>
            </div>
        </div>
        <button class="view-modal-btn">开始练习</button>
        <button class="view-modal-btn" style="background-color: #9b59b6;">连词成句</button>
    </div>

    <!-- 添加 Modal -->
    <div id="fullscreenModal" class="modal">
        <button class="close-btn">×</button>
        <div class="modal-content" id="modalContent">
        </div>
    </div>

    <!-- 连词成句 Modal -->
    <div id="wordArrangeModal" class="modal">
        <button class="close-btn">×</button>
        <div class="modal-content" id="wordArrangeContent">
        </div>
    </div>

    <script>
        // 全局变量声明
        let currentPlayingButton = null;
        let currentPlayingAudio = null;
        const audioElements = new Map();  // 存储所有音频元素
        const audioCache = new Map();     // 添加音频缓存 Map

        document.getElementById('fileInput').addEventListener('change', function (e) {
            const file = e.target.files[0];
            if (!file) {
                document.getElementById('fileInfo').style.display = 'none';
                return;
            }

            const reader = new FileReader();

            if (file.name.endsWith('.csv')) {
                reader.onload = function (event) {
                    const csvData = event.target.result;
                    processCSV(csvData);
                };
                reader.readAsText(file);
            } else {
                reader.onload = function (event) {
                    const data = new Uint8Array(event.target.result);
                    const workbook = XLSX.read(data, { type: 'array' });
                    processExcel(workbook);
                };
                reader.readAsArrayBuffer(file);
            }
        });

        function processCSV(csvData) {
            const rows = csvData.split('\n');
            const tableBody = document.getElementById('tableBody');
            tableBody.innerHTML = '';

            const skipHeader = document.getElementById('skipHeader').checked;
            const startIndex = skipHeader ? 1 : 0;
            let validRowCount = 0;

            rows.slice(startIndex).forEach(row => {
                if (row.trim() === '') return;

                const columns = row.match(/(".*?"|[^",]+)(?=\s*,|\s*$)/g);
                if (columns && columns.length >= 2) {
                    const tr = document.createElement('tr');
                    const english = columns[0].replace(/^"|"$/g, '');
                    const chinese = columns[1].replace(/^"|"$/g, '');
                    const keywords = columns[2] ? columns[2].replace(/^"|"$/g, '').split(',').map(k => k.trim()) : [];
                    const distractors = columns[3] ? columns[3].replace(/^"|"$/g, '').split(',').map(d => d.trim()) : [];

                    // 处理英文文本的高亮,添加 font-weight: bold
                    let highlightedText = english;
                    if (keywords.length > 0) {
                        keywords.forEach(keyword => {
                            if (keyword) {
                                const regex = new RegExp(`(${keyword})`, 'gi');
                                highlightedText = highlightedText.replace(regex, '<span class="highlight-word" style="color: #ff4444; font-weight: bold; cursor: pointer;" data-word="$1">$1</span>');
                            }
                        });
                    }

                    tr.innerHTML = `
                        <td>${highlightedText}</td>
                        <td>${chinese}</td>
                        <td>${distractors.join(', ')}</td>
                    `;
                    tableBody.appendChild(tr);
                    validRowCount++;
                }
            });

            const fileName = document.getElementById('fileInput').files[0].name;
            updateFileInfo(fileName, rows.length, validRowCount);
        }

        function processExcel(workbook) {
            const firstSheetName = workbook.SheetNames[0];
            const worksheet = workbook.Sheets[firstSheetName];
            const data = XLSX.utils.sheet_to_json(worksheet, { header: 1 });

            const tableBody = document.getElementById('tableBody');
            tableBody.innerHTML = '';

            const skipHeader = document.getElementById('skipHeader').checked;
            const startIndex = skipHeader ? 1 : 0;
            let validRowCount = 0;

            data.slice(startIndex).forEach(row => {
                if (row.length >= 2) {
                    const tr = document.createElement('tr');
                    const english = row[0];
                    const chinese = row[1];
                    const keywords = row[2] ? row[2].split(',').map(k => k.trim()) : [];
                    const distractors = row[3] ? row[3].split(',').map(d => d.trim()) : [];

                    // 处理英文文本的高亮,添加 font-weight: bold
                    let highlightedText = english;
                    if (keywords.length > 0) {
                        keywords.forEach(keyword => {
                            if (keyword) {
                                const regex = new RegExp(`(${keyword})`, 'gi');
                                highlightedText = highlightedText.replace(regex, '<span class="highlight-word" style="color: #ff4444; font-weight: bold; cursor: pointer;" data-word="$1">$1</span>');
                            }
                        });
                    }

                    tr.innerHTML = `
                        <td>${highlightedText}</td>
                        <td>${chinese}</td>
                        <td>${distractors.join(', ')}</td>
                    `;
                    tableBody.appendChild(tr);
                    validRowCount++;
                }
            });

            const fileName = document.getElementById('fileInput').files[0].name;
            updateFileInfo(fileName, data.length, validRowCount);
        }

        // 添加滑值显示更新
        document.getElementById('rate').addEventListener('input', function () {
            document.getElementById('rateValue').textContent = this.value;
        });

        document.getElementById('pitch').addEventListener('input', function () {
            document.getElementById('pitchValue').textContent = this.value;
        });

        function getRandom32BitString() {
            const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
            let result = '';
            for (let i = 0; i < 32; i++) {
                const randomIndex = Math.floor(Math.random() * characters.length);
                result += characters[randomIndex];
            }
            return result;
        }

        async function fetchAndCacheAudio(url) {
            const response = await fetch(url, {
                method: 'GET',
                mode: 'cors',  // 尝试 cors 模式
                credentials: 'omit',
                headers: {
                    'Accept': 'audio/mpeg',
                }
            });

            if (!response.ok) {
                throw new Error('Network response was not ok');
            }

            // 如果上面的方式不行,尝试使用 XMLHttpRequest
            if (!response.body) {
                return new Promise((resolve, reject) => {
                    const xhr = new XMLHttpRequest();
                    xhr.open('GET', url, true);
                    xhr.responseType = 'blob';

                    xhr.onload = function () {
                        if (this.status === 200) {
                            const blob = new Blob([this.response], { type: 'audio/mpeg' });
                            const blobUrl = URL.createObjectURL(blob);
                            resolve(blobUrl);
                        } else {
                            reject(new Error('XHR request failed'));
                        }
                    };

                    xhr.onerror = function () {
                        reject(new Error('XHR request failed'));
                    };

                    xhr.send();
                });
            }

            const audioBlob = await response.blob();
            const blobUrl = URL.createObjectURL(audioBlob);
            return blobUrl;
        }

        // 用于生成缓存键的函数
        function generateCacheKey(text, voice, rate, pitch) {
            return `${text}_${voice}_${rate}_${pitch}`;
        }

        function createPlayButton(text) {
            const button = document.createElement('button');
            button.className = 'play-button';

            // 创建图标容器
            const iconContainer = document.createElement('div');
            iconContainer.className = 'icon-container';
            const playIcon = document.createElement('div');
            playIcon.className = 'play-icon';
            iconContainer.appendChild(playIcon);
            button.appendChild(iconContainer);

            // 创建音频元素
            const audio = document.createElement('audio');
            audio.preload = 'auto';

            let isLoading = false;
            let isLoaded = false;

            // 定义 stopPlaying 函数在 createPlayButton 函数内部
            function stopPlaying() {
                if (audio) {
                    audio.pause();
                    audio.currentTime = 0;
                }
                button.classList.remove('playing');
                iconContainer.innerHTML = '<div class="play-icon"></div>';

                // 停止播放时移除激活状态
                const sentenceDiv = button.closest('.sentence-pair');
                if (sentenceDiv) {
                    sentenceDiv.classList.remove('active');
                }

                if (currentPlayingButton === button) {
                    currentPlayingButton = null;
                    currentPlayingAudio = null;
                }
            }

            async function loadAudio() {
                if (isLoading || isLoaded) return;

                isLoading = true;
                button.classList.add('loading');
                iconContainer.innerHTML = '<div class="loading-icon"></div>';

                try {
                    const voice = document.getElementById('voice').value;
                    const rate = document.getElementById('rate').value;
                    const pitch = document.getElementById('pitch').value;
                    const encodedText = encodeURIComponent(text);
                    const cacheKey = btoa(`${text}_${voice}_${rate}_${pitch}`).replace(/[+/=]/g, '');
                    const url = `https://t.leftsite.cn/tts?t=${encodedText}&v=${voice}&r=${rate}&p=${pitch}&o=audio-24khz-48kbitrate-mono-mp3&cache=${cacheKey}`;

                    audio.src = url;

                    await new Promise((resolve, reject) => {
                        const loadHandler = () => {
                            resolve();
                            cleanup();
                        };

                        const errorHandler = () => {
                            reject(new Error('Failed to load audio'));
                            cleanup();
                        };

                        const cleanup = () => {
                            audio.removeEventListener('canplaythrough', loadHandler);
                            audio.removeEventListener('error', errorHandler);
                        };

                        audio.addEventListener('canplaythrough', loadHandler, { once: true });
                        audio.addEventListener('error', errorHandler, { once: true });
                    });

                    isLoaded = true;
                    return true;
                } catch (error) {
                    console.error('Error loading audio:', error);
                    return false;
                } finally {
                    isLoading = false;
                    button.classList.remove('loading');
                    iconContainer.innerHTML = '<div class="play-icon"></div>';
                }
            }

            button.loadAudio = loadAudio;

            button.addEventListener('click', async function (e) {
                e.stopPropagation();

                // 获取当前句子元素并激活
                const sentenceDiv = this.closest('.sentence-pair');
                activateSentence(sentenceDiv);

                if (this === currentPlayingButton) {
                    stopPlaying();
                    return;
                }

                // 停止其他正在播放的音频
                if (currentPlayingButton && currentPlayingButton.stopPlaying) {
                    currentPlayingButton.stopPlaying();
                }

                // 如果音频未加载,先加载
                if (!isLoaded) {
                    const success = await loadAudio();
                    if (!success) return;
                }

                try {
                    // 设置播放状态
                    this.classList.add('playing');
                    iconContainer.innerHTML = '<div class="stop-icon"></div>';

                    // 设置播放结束的处理
                    audio.onended = () => {
                        stopPlaying();
                        // 播放结束时移除激活状态
                        sentenceDiv.classList.remove('active');
                    };

                    // 开始播放
                    currentPlayingButton = this;
                    currentPlayingAudio = audio;
                    await audio.play();

                } catch (error) {
                    console.error('Error playing audio:', error);
                    this.classList.remove('playing');
                    iconContainer.innerHTML = '<div class="play-icon"></div>';
                    // 出错时移除激活状态
                    sentenceDiv.classList.remove('active');
                }
            });

            // 将 stopPlaying 函数附加到按钮上
            button.stopPlaying = stopPlaying;

            return button;
        }

        async function showModal() {
            const modal = document.getElementById('fullscreenModal');
            const modalContent = document.getElementById('modalContent');
            modalContent.innerHTML = '';

            // 获取当前设置的字体大小
            const fontSize = document.getElementById('fontSize').value;

            // 获取表格中的所有行
            const rows = document.getElementById('tableBody').getElementsByTagName('tr');
            if (rows.length === 0) {
                modalContent.innerHTML = '<div style="color: white; text-align: center;">请先选择文件</div>';
                modal.style.display = 'block';
                return;
            }

            // 创建所有按钮和内容
            Array.from(rows).forEach((row, index) => {
                const english = row.cells[0].innerHTML;
                const chinese = row.cells[1].textContent;
                const keywords = row.cells[2] ? row.cells[2].textContent.split(',').map(k => k.trim()) : [];

                const sentenceDiv = document.createElement('div');
                sentenceDiv.className = 'sentence-pair';

                const contentDiv = document.createElement('div');
                contentDiv.className = 'sentence-content';

                const englishDiv = document.createElement('div');
                englishDiv.className = 'english';
                englishDiv.innerHTML = english;
                englishDiv.style.fontSize = `${fontSize}px`;

                const chineseDiv = document.createElement('div');
                chineseDiv.className = 'chinese';
                chineseDiv.textContent = chinese;
                chineseDiv.style.fontSize = `${fontSize}px`;

                contentDiv.appendChild(englishDiv);
                contentDiv.appendChild(chineseDiv);

                const playButton = createPlayButton(english.replace(/<[^>]*>/g, ''));
                sentenceDiv.appendChild(playButton);
                sentenceDiv.appendChild(contentDiv);

                // 修改高亮词点击事件处理
                contentDiv.querySelectorAll('.highlight-word').forEach(span => {
                    span.addEventListener('click', async function (e) {
                        e.stopPropagation();  // 阻止事件冒泡到父元素
                        e.preventDefault();    // 阻止默认行为
                        const word = this.dataset.word;

                        // 停止当前正在播放的音频
                        if (currentPlayingButton && currentPlayingButton.stopPlaying) {
                            currentPlayingButton.stopPlaying();
                        }

                        // 创建音频元素并设置参数
                        const voice = document.getElementById('voice').value;
                        const rate = document.getElementById('rate').value;
                        const pitch = document.getElementById('pitch').value;
                        const encodedText = encodeURIComponent(word);
                        const cacheKey = btoa(`${word}_${voice}_${rate}_${pitch}`).replace(/[+/=]/g, '');
                        const url = `https://t.leftsite.cn/tts?t=${encodedText}&v=${voice}&r=${rate}&p=${pitch}&o=audio-24khz-48kbitrate-mono-mp3&cache=${cacheKey}`;

                        const audio = new Audio(url);
                        audio.preload = 'auto';

                        try {
                            // 播放音频
                            await audio.play();

                            // 高亮动画效果
                            this.style.transition = 'all 0.3s';
                            this.style.backgroundColor = 'rgba(255, 68, 68, 0.2)';
                            setTimeout(() => {
                                this.style.backgroundColor = 'transparent';
                            }, 300);
                        } catch (error) {
                            console.error('Error playing audio:', error);
                        }
                    });
                });

                // 修改 contentDiv 的点击事件处理,排除高亮词的点击
                contentDiv.addEventListener('click', function (e) {
                    // 如果点击的是高亮词,不执行切换操作
                    if (e.target.classList.contains('highlight-word')) {
                        return;
                    }

                    const sentenceDiv = this.closest('.sentence-pair');
                    activateSentence(sentenceDiv);

                    const english = this.querySelector('.english');
                    const chinese = this.querySelector('.chinese');

                    if (!chinese.classList.contains('show')) {
                        chinese.classList.add('show');
                        english.classList.add('hidden');
                    } else {
                        chinese.classList.remove('show');
                        english.classList.remove('hidden');
                    }

                    e.stopPropagation();
                });

                modalContent.appendChild(sentenceDiv);
            });

            modal.style.display = 'block';

            // 请求全屏
            try {
                await modal.requestFullscreen();
                // 添加全屏状态类
                modal.classList.add('fullscreen');
            } catch (err) {
                console.warn('Failed to enter fullscreen:', err);
            }

            // 自动加载所有音频
            requestAnimationFrame(() => {
                const buttons = document.querySelectorAll('.play-button');
                buttons.forEach((button, index) => {
                    if (button.loadAudio) {
                        // 错开加载时间,避免同时发起太多请求
                        setTimeout(() => {
                            button.loadAudio();
                        }, index * 200); // 每个音频间隔 200ms 开始加载
                    }
                });
            });
        }

        // 在语音设置改变时更新所有音频源
        function updateAllAudioSources() {
            audioElements.forEach(({ audio, text }) => {
                const voice = document.getElementById('voice').value;
                const rate = document.getElementById('rate').value;
                const pitch = document.getElementById('pitch').value;
                const encodedText = encodeURIComponent(text);
                const cacheKey = btoa(`${text}_${voice}_${rate}_${pitch}`).replace(/[+/=]/g, '');
                const url = `https://t.leftsite.cn/tts?t=${encodedText}&v=${voice}&r=${rate}&p=${pitch}&o=audio-24khz-48kbitrate-mono-mp3&cache=${cacheKey}`;
                audio.src = url;
                audio.load();
            });
        }

        // 添加语音设置变化监听
        document.getElementById('voice').addEventListener('change', updateAllAudioSources);
        document.getElementById('rate').addEventListener('change', updateAllAudioSources);
        document.getElementById('pitch').addEventListener('change', updateAllAudioSources);

        // 修改 hideModal 函数
        async function hideModal() {
            const modal = document.getElementById('fullscreenModal');

            // 如果处于全屏状态,退出全屏
            if (document.fullscreenElement) {
                try {
                    await document.exitFullscreen();
                } catch (err) {
                    console.warn('Failed to exit fullscreen:', err);
                }
            }

            // 移除全屏状态类
            modal.classList.remove('fullscreen');
            modal.style.display = 'none';

            // 消所有加载中的请求
            if (currentPlayingButton && currentPlayingButton.cancelLoading) {
                currentPlayingButton.cancelLoading();
            }

            // 停止当前播放的音频
            if (currentPlayingButton && currentPlayingButton.stopPlaying) {
                currentPlayingButton.stopPlaying();
            }

            currentPlayingButton = null;
            currentPlayingAudio = null;

            // 重置所有句子为初始状态
            document.querySelectorAll('.sentence-pair').forEach(pair => {
                const contentDiv = pair.querySelector('.sentence-content');
                const english = contentDiv.querySelector('.english');
                const chinese = contentDiv.querySelector('.chinese');

                chinese.classList.remove('show');
                english.classList.remove('hidden');
                contentDiv.insertBefore(english, chinese);

                // 重置播放按钮状态
                const playButton = pair.querySelector('.play-button');
                playButton.classList.remove('playing', 'loading');
                playButton.innerHTML = '&#9654;';
            });
        }

        // 监听全屏状态变化
        document.addEventListener('fullscreenchange', function () {
            const modal = document.getElementById('fullscreenModal');
            if (!document.fullscreenElement && modal.style.display === 'block') {
                // 如果退出全屏但 modal 仍在显示,则关闭 modal
                hideModal();
            }
        });

        // 修改预览切换函数
        function togglePreview() {
            const content = document.querySelector('.preview-content');
            const icon = document.querySelector('.preview-icon');

            content.classList.toggle('open');
            icon.classList.toggle('open');

            if (content.classList.contains('open')) {
                const tableHeight = document.getElementById('csvTable').offsetHeight;
                content.style.maxHeight = Math.min(500, tableHeight + 40) + 'px';
            } else {
                content.style.maxHeight = '0';
            }
        }

        // 修改文件信息更新函数
        function updateFileInfo(fileName, totalRows, validRows) {
            const fileInfo = document.getElementById('fileInfo');
            document.getElementById('fileName').textContent = fileName;
            document.getElementById('totalRows').textContent = totalRows;
            document.getElementById('validRows').textContent = validRows;

            if (validRows > 0) {
                fileInfo.style.display = 'block';
            } else {
                fileInfo.style.display = 'none';
            }

            // 确保预览内容是折叠的
            const content = document.querySelector('.preview-content');
            const icon = document.querySelector('.preview-icon');
            content.classList.remove('open');
            icon.classList.remove('open');
            content.style.maxHeight = '0';
        }

        // 修改文件选择处理
        document.getElementById('fileInput').addEventListener('click', function (e) {
            const fileInfo = document.getElementById('fileInfo');
            fileInfo.style.display = 'none';
            document.getElementById('fileName').textContent = '-';
            document.getElementById('totalRows').textContent = '-';
            document.getElementById('validRows').textContent = '-';
        });

        // 在语音设置改变时清除所有音频元素的缓存
        document.getElementById('voice').addEventListener('change', () => {
            document.querySelectorAll('audio').forEach(audio => {
                audio.removeAttribute('src');
            });
        });

        document.getElementById('rate').addEventListener('change', () => {
            document.querySelectorAll('audio').forEach(audio => {
                audio.removeAttribute('src');
            });
        });

        document.getElementById('pitch').addEventListener('change', () => {
            document.querySelectorAll('audio').forEach(audio => {
                audio.removeAttribute('src');
            });
        });

        // 修改 hideModal 函数,确保清理 Blob URLs
        window.addEventListener('beforeunload', () => {
            // 清除所有缓存的音频资源
            audioElements.forEach(({ audio }) => {
                if (audio.src) {
                    audio.src = '';
                }
            });
            audioElements.clear();
        });

        // 添加激句子的函数
        function activateSentence(sentenceDiv) {
            // 移除其他句子的激活状态
            document.querySelectorAll('.sentence-pair.active').forEach(div => {
                if (div !== sentenceDiv) {
                    div.classList.remove('active');
                }
            });

            // 添加当前句子的激活状态
            sentenceDiv.classList.add('active');

            // 计算滚动位置
            const modalContent = document.querySelector('.modal-content');
            const modalHeight = modalContent.clientHeight;
            const sentenceRect = sentenceDiv.getBoundingClientRect();
            const modalRect = modalContent.getBoundingClientRect();
            const relativeTop = sentenceRect.top - modalRect.top;
            const targetScroll = modalContent.scrollTop + relativeTop - (modalHeight / 2) + (sentenceRect.height / 2);

            // 平滑滚动到目标位置
            modalContent.scrollTo({
                top: targetScroll,
                behavior: 'smooth'
            });
        }

        // 添加点击其他区域取消激活状态的处理
        document.querySelector('.modal-content').addEventListener('click', function (e) {
            if (e.target === this) {
                document.querySelectorAll('.sentence-pair.active').forEach(div => {
                    div.classList.remove('active');
                });
            }
        });

        // 字体大小调整相关代码
        const fontSizeSlider = document.getElementById('fontSize');
        const fontSizeValue = document.getElementById('fontSizeValue');
        const preview = document.querySelector('.font-size-preview');
        const previewEn = document.querySelector('.preview-text-en');
        const previewZh = document.querySelector('.preview-text-zh');

        // 初始化预览文本字体大小
        const initialFontSize = fontSizeSlider.value;
        previewEn.style.fontSize = `${initialFontSize}px`;
        previewZh.style.fontSize = `${initialFontSize}px`;
        fontSizeValue.textContent = `${initialFontSize}px`;

        // 移除鼠标悬浮相关的事件监听
        // 更新字体大小
        fontSizeSlider.addEventListener('input', function () {
            const size = this.value;
            fontSizeValue.textContent = `${size}px`;

            // 更新预览文本大小
            previewEn.style.fontSize = `${size}px`;
            previewZh.style.fontSize = `${size}px`;

            // 更新modal中所有文本的大小
            if (document.getElementById('fullscreenModal').style.display === 'block') {
                document.querySelectorAll('.english, .chinese').forEach(element => {
                    element.style.fontSize = `${size}px`;
                });
            }
        });

        // ---------- 连词成句功能 ----------

        // 存储原始句子信息的数组
        let originalSentences = [];

        // 显示连词成句的 Modal
        async function showWordArrangeModal() {
            const modal = document.getElementById('wordArrangeModal');
            const modalContent = document.getElementById('wordArrangeContent');
            modalContent.innerHTML = '';

            // 获取表格中的所有行
            const rows = document.getElementById('tableBody').getElementsByTagName('tr');
            if (rows.length === 0) {
                modalContent.innerHTML = '<div style="color: white; text-align: center;">请先选择文件</div>';
                modal.style.display = 'block';
                return;
            }

            // 清空原始句子数组
            originalSentences = [];

            // 获取当前设置的字体大小
            const fontSize = document.getElementById('fontSize').value;

            // 为每一行创建一个连词成句的容器
            Array.from(rows).forEach((row, index) => {
                // 获取英文和中文文本
                const english = row.cells[0].textContent.replace(/<[^>]*>/g, '');
                const chinese = row.cells[1].textContent;
                const distractors = row.cells[2] ? row.cells[2].textContent.split(',').filter(d => d.trim() !== '') : [];

                // 存储原始句子
                originalSentences.push({
                    english: english,
                    chinese: chinese,
                    distractors: distractors,
                    index: index
                });

                // 创建连词成句容器
                const container = document.createElement('div');
                container.className = 'word-arrange-container';
                container.dataset.index = index;

                // 创建中文提示
                const chineseText = document.createElement('div');
                chineseText.className = 'chinese-text';
                chineseText.textContent = chinese;
                chineseText.style.fontSize = `${fontSize}px`;
                chineseText.addEventListener('click', function() {
                    if (this.dataset.showingEnglish) {
                        this.textContent = chinese;
                        this.dataset.showingEnglish = '';
                    } else {
                        this.textContent = english;
                        this.dataset.showingEnglish = 'true';
                    }
                });
                container.appendChild(chineseText);

                // 创建单词银行区域
                const wordBank = document.createElement('div');
                wordBank.className = 'word-bank';
                wordBank.id = `word-bank-${index}`;
                container.appendChild(wordBank);

                // 创建句子模板区域
                const sentenceTemplate = document.createElement('div');
                sentenceTemplate.className = 'sentence-template';
                sentenceTemplate.id = `sentence-template-${index}`;
                container.appendChild(sentenceTemplate);

                // 添加反馈信息区域
                const feedback = document.createElement('div');
                feedback.className = 'feedback-message';
                feedback.id = `feedback-${index}`;
                container.appendChild(feedback);

                // 将容器添加到 Modal 内容中
                modalContent.appendChild(container);

                // 处理英文句子,抽取单词和标点,并添加干扰词
                processEnglishSentence(english, index, distractors);
            });

            // 显示 Modal
            modal.style.display = 'block';

            // 请求全屏
            try {
                await modal.requestFullscreen();
                modal.classList.add('fullscreen');
            } catch (err) {
                console.warn('Failed to enter fullscreen:', err);
            }

            // 初始化拖放功能
            initDragAndDrop();
        }

        // 处理英文句子,抽取单词和标点
        function processEnglishSentence(sentence, index, distractors = []) {
            // 拆分句子为单词和标点
            const pattern = /(\w+[-']\w+|\w+|[,.!?;:"""''])/g;
            const tokens = sentence.match(pattern) || [];

            // 跟踪当前处理的位置
            let position = 0;

            // 收集单词和标点
            const words = [];
            const punctuations = [];
            const structure = [];

            tokens.forEach(token => {
                // 查找token在原句中的位置,并检查前后是否有空格
                const tokenPos = sentence.indexOf(token, position);
                const hasPrefixSpace = tokenPos > position;
                position = tokenPos + token.length;

                // 检查是否为标点
                if (/^[,.!?;:"""'']$/.test(token)) {
                    punctuations.push({
                        text: token,
                        prefixSpace: hasPrefixSpace
                    });
                    structure.push({
                        type: 'punctuation',
                        text: token,
                        prefixSpace: hasPrefixSpace
                    });
                } else {
                    words.push({
                        text: token,
                        prefixSpace: hasPrefixSpace
                    });
                    structure.push({
                        type: 'word',
                        prefixSpace: hasPrefixSpace
                    });
                }
            });

            // 添加干扰词
            distractors.forEach(distractor => {
                if (distractor && distractor.trim() !== '') {
                    words.push({
                        text: distractor.trim(),
                        prefixSpace: false,
                        isDistractor: true
                    });
                }
            });

            // 随机打乱单词顺序
            shuffleArray(words);

            // 填充单词银行
            const wordBank = document.getElementById(`word-bank-${index}`);
            words.forEach(word => {
                const wordElement = createDraggableWord(word.text);
                // 移除为干扰词添加特殊样式的代码
                // if (word.isDistractor) {
                //     wordElement.classList.add('distractor-word');
                // }
                wordBank.appendChild(wordElement);
            });

            // 创建句子模板
            const sentenceTemplate = document.getElementById(`sentence-template-${index}`);
            structure.forEach(item => {
                if (item.prefixSpace) {
                    sentenceTemplate.appendChild(document.createTextNode(' '));
                }

                if (item.type === 'word') {
                    const slot = document.createElement('div');
                    slot.className = 'word-slot';
                    sentenceTemplate.appendChild(slot);
                } else {
                    const punctuation = document.createElement('span');
                    punctuation.className = 'punctuation';
                    punctuation.textContent = item.text;
                    sentenceTemplate.appendChild(punctuation);
                }
            });
        }

        // 创建可拖动的单词元素
        function createDraggableWord(text) {
            const word = document.createElement('div');
            word.className = 'draggable-word word';
            word.textContent = text;
            word.draggable = true;
            word.style.fontSize = document.getElementById('fontSize').value + 'px'; // 改为读取当前滑块值
            return word;

            // 获取当前字体大小设置并应用
            const fontSize = document.getElementById('fontSize').value;
            word.style.fontSize = `${fontSize}px`;

            return word;
        }

        // 随机打乱数组
        function shuffleArray(array) {
            for (let i = array.length - 1; i > 0; i--) {
                const j = Math.floor(Math.random() * (i + 1));
                [array[i], array[j]] = [array[j], array[i]];
            }
        }

        // 初始化拖放功能
        function initDragAndDrop() {
            // 获取所有可拖动单词和目标槽
            const draggableWords = document.querySelectorAll('.draggable-word');
            const wordSlots = document.querySelectorAll('.word-slot');

            draggableWords.forEach(word => {
                // 拖动开始事件
                word.addEventListener('dragstart', function (e) {
                    this.classList.add('dragging');
                    e.dataTransfer.setData('text/plain', this.textContent);
                    // 记录源容器的索引
                    const container = this.closest('.word-arrange-container');
                    if (container) {
                        e.dataTransfer.setData('sourceContainerIndex', container.dataset.index);
                    }

                    // 如果单词在单词槽中,记录父元素信息
                    if (this.parentElement.classList.contains('word-slot')) {
                        e.dataTransfer.setData('source-slot', 'true');
                    } else if (this.parentElement.classList.contains('word-bank')) {
                        // 添加标记表示来源是单词银行
                        e.dataTransfer.setData('source-bank', 'true');
                    }
                });

                // 拖动结束事件
                word.addEventListener('dragend', function () {
                    this.classList.remove('dragging');
                });
            });

            // 对所有单词槽添加拖放事件
            wordSlots.forEach(slot => {
                // 拖动进入槽区域
                slot.addEventListener('dragover', function (e) {
                    e.preventDefault();
                    this.style.background = 'rgba(255, 255, 255, 0.2)';
                });

                // 拖动离开槽区域
                slot.addEventListener('dragleave', function () {
                    this.style.background = 'rgba(255, 255, 255, 0.1)';
                });

                // 拖动放置事件
                slot.addEventListener('drop', function (e) {
                    e.preventDefault();
                    this.style.background = 'rgba(255, 255, 255, 0.1)';

                    // 检查是否在同一个句子组内
                    const targetContainer = this.closest('.word-arrange-container');
                    const sourceContainerIndex = e.dataTransfer.getData('sourceContainerIndex');
                    if (targetContainer && sourceContainerIndex && 
                        targetContainer.dataset.index !== sourceContainerIndex) {
                        return; // 如果不是同一组,则不允许放置
                    }

                    const wordText = e.dataTransfer.getData('text/plain');
                    const sourceIsSlot = e.dataTransfer.getData('source-slot') === 'true';
                    const sourceIsBank = e.dataTransfer.getData('source-bank') === 'true';

                    // 如果当前槽已有单词
                    if (this.querySelector('.draggable-word')) {
                        // 如果源也是单词槽,则交换单词
                        if (sourceIsSlot) {
                            const draggingWord = document.querySelector('.dragging');
                            if (draggingWord && draggingWord.parentElement !== this) {
                                const existingWord = this.querySelector('.draggable-word');
                                const sourceSlot = draggingWord.parentElement;

                                // 交换单词
                                sourceSlot.appendChild(existingWord);
                                this.appendChild(draggingWord);

                                // 检查是否是句子的第一个槽位
                                const isFirstSlot = Array.from(this.parentElement.children).indexOf(this) === 0;
                                if (isFirstSlot) {
                                    draggingWord.textContent = draggingWord.textContent.charAt(0).toUpperCase() + 
                                                            draggingWord.textContent.slice(1);
                                }

                                // 验证当前句子
                                validateSentence(this.closest('.word-arrange-container'));
                            }
                        } else if (sourceIsBank) {
                            // 如果源是单词银行,将当前槽的单词放回单词银行,新单词放入槽
                            const draggingWord = document.querySelector('.dragging');
                            if (draggingWord) {
                                const existingWord = this.querySelector('.draggable-word');
                                const container = this.closest('.word-arrange-container');
                                const wordBank = container.querySelector('.word-bank');

                                // 将现有单词放回单词银行
                                wordBank.appendChild(existingWord);

                                // 将新单词放入槽位
                                this.appendChild(draggingWord);

                                // 检查是否是句子的第一个槽位
                                const isFirstSlot = Array.from(this.parentElement.children).indexOf(this) === 0;
                                if (isFirstSlot) {
                                    draggingWord.textContent = draggingWord.textContent.charAt(0).toUpperCase() + 
                                                            draggingWord.textContent.slice(1);
                                }

                                // 验证当前句子
                                validateSentence(container);
                            }
                        }
                    } else {
                        // 当前槽没有单词,直接添加
                        const draggingWord = document.querySelector('.dragging');
                        if (draggingWord) {
                            this.appendChild(draggingWord);

                            // 检查是否是句子的第一个槽位
                            const isFirstSlot = Array.from(this.parentElement.children).indexOf(this) === 0;
                            if (isFirstSlot) {
                                draggingWord.textContent = draggingWord.textContent.charAt(0).toUpperCase() + 
                                                        draggingWord.textContent.slice(1);
                            }

                            // 验证当前句子
                            validateSentence(this.closest('.word-arrange-container'));
                        } else {
                            // 创建新单词元素(可能来自外部拖拽)
                            const newWord = createDraggableWord(wordText);
                            newWord.draggable = true;
                            this.appendChild(newWord);

                            // 检查是否是句子的第一个槽位
                            const isFirstSlot = Array.from(this.parentElement.children).indexOf(this) === 0;
                            if (isFirstSlot) {
                                newWord.textContent = newWord.textContent.charAt(0).toUpperCase() + 
                                                    newWord.textContent.slice(1);
                            }

                            // 为新创建的单词添加拖拽事件
                            initWordDragEvents(newWord);

                            // 如果源是单词银行,移除源单词
                            if (!sourceIsSlot) {
                                const sourceWord = document.querySelector(`.draggable-word:not(.dragging):contains('${wordText}')`);
                                if (sourceWord && sourceWord.parentElement.classList.contains('word-bank')) {
                                    sourceWord.remove();
                                }
                            }

                            // 验证当前句子
                            validateSentence(this.closest('.word-arrange-container'));
                        }
                    }
                });
            });

            // 对单词银行区域添加拖放事件
            document.querySelectorAll('.word-bank').forEach(bank => {
                bank.addEventListener('dragover', function (e) {
                    e.preventDefault();
                });

                bank.addEventListener('drop', function (e) {
                    e.preventDefault();

                    // 检查是否在同一个句子组内
                    const targetContainer = this.closest('.word-arrange-container');
                    const sourceContainerIndex = e.dataTransfer.getData('sourceContainerIndex');
                    if (targetContainer && sourceContainerIndex && 
                        targetContainer.dataset.index !== sourceContainerIndex) {
                        return; // 如果不是同一组,则不允许放置
                    }

                    const draggingWord = document.querySelector('.dragging');
                    if (draggingWord && draggingWord.parentElement.classList.contains('word-slot')) {
                        bank.appendChild(draggingWord);

                        // 验证关联的句子
                        const containerIndex = bank.id.split('-').pop();
                        const container = document.querySelector(`.word-arrange-container[data-index="${containerIndex}"]`);
                        validateSentence(container);
                    }
                });
            });
        }

        // 验证句子是否正确组装
        function validateSentence(container) {
            if (!container) return;

            const index = container.dataset.index;
            const originalSentence = originalSentences[index];
            const feedback = document.getElementById(`feedback-${index}`);

            // 获取所有单词槽
            const wordSlots = container.querySelectorAll('.word-slot');

            // 检查所有槽位是否都填充了单词
            let allSlotsFilled = true;
            wordSlots.forEach(slot => {
                if (!slot.querySelector('.draggable-word')) {
                    allSlotsFilled = false;
                }
            });

            // 如果并非所有槽都已填充,清除反馈
            if (!allSlotsFilled) {
                feedback.className = 'feedback-message';
                container.classList.remove('correct', 'incorrect');
                return;
            }

            // 重构用户组装的句子
            let reconstructedSentence = '';
            const sentenceTemplate = container.querySelector('.sentence-template');

            // 遍历句子模板的所有子节点
            for (let i = 0; i < sentenceTemplate.childNodes.length; i++) {
                const node = sentenceTemplate.childNodes[i];

                if (node.nodeType === Node.TEXT_NODE) {
                    // 如果是文本节点(空格),添加到重构句子中
                    reconstructedSentence += node.textContent;
                } else if (node.classList.contains('word-slot')) {
                    // 如果是单词槽,获取其中的单词
                    const word = node.querySelector('.draggable-word');
                    if (word) {
                        reconstructedSentence += word.textContent;
                    }
                } else if (node.classList.contains('punctuation')) {
                    // 如果是标点符号,添加到重构句子中
                    reconstructedSentence += node.textContent;
                }
            }

            // 标准化句子(去除多余空格)进行比较
            const normalizedOriginal = originalSentence.english.replace(/\s+/g, ' ').trim();
            const normalizedReconstructed = reconstructedSentence.replace(/\s+/g, ' ').trim();

            // 更新反馈信息和容器类名
            if (normalizedOriginal === normalizedReconstructed) {
                feedback.textContent = '恭喜!句子组装正确!';
                feedback.className = 'feedback-message correct';
                container.classList.add('correct');
                container.classList.remove('incorrect');

                // 朗读正确的句子
                playCorrectSentence(originalSentence.english);
            } else {
                feedback.textContent = '句子组装不正确,请重试!';
                feedback.className = 'feedback-message incorrect';
                container.classList.add('incorrect');
                container.classList.remove('correct');
            }
        }

        // 朗读正确的句子
        function playCorrectSentence(text) {
            // 创建音频元素
            const audio = new Audio();

            // 获取语音设置
            const voice = document.getElementById('voice').value;
            const rate = document.getElementById('rate').value;
            const pitch = document.getElementById('pitch').value;

            // 构建 TTS 请求 URL
            const encodedText = encodeURIComponent(text);
            const cacheKey = btoa(`${text}_${voice}_${rate}_${pitch}`).replace(/[+/=]/g, '');
            const url = `https://t.leftsite.cn/tts?t=${encodedText}&v=${voice}&r=${rate}&p=${pitch}&o=audio-24khz-48kbitrate-mono-mp3&cache=${cacheKey}`;

            // 设置音频源并播放
            audio.src = url;
            audio.play();
        }

        // 关闭连词成句 Modal
        async function hideWordArrangeModal() {
            const modal = document.getElementById('wordArrangeModal');

            // 如果处于全屏状态,退出全屏
            if (document.fullscreenElement) {
                try {
                    await document.exitFullscreen();
                } catch (err) {
                    console.warn('Failed to exit fullscreen:', err);
                }
            }

            // 清除相关状态
            modal.classList.remove('fullscreen');
            modal.style.display = 'none';
            originalSentences = [];
        }

        // 自定义 contains 选择器,用于查找包含指定文本的元素
        document.querySelectorAll = document.querySelectorAll || function (selector) {
            if (selector.includes(':contains')) {
                const text = selector.match(/'(.*?)'/)[1];
                const elements = document.querySelectorAll(selector.split(':contains')[0]);
                return Array.from(elements).filter(el => el.textContent.includes(text));
            } else {
                return document.querySelectorAll(selector);
            }
        };

        // 添加有道API音频播放函数
        function playYoudaoAudio(word) {
            // 获取选择的发音类型
            const pronunciationType = document.getElementById('pronunciationType').value;
            
            // 构建有道API的音频URL
            const audioUrl = `https://dict.youdao.com/dictvoice?audio=${encodeURIComponent(word)}&type=${pronunciationType}`;
            
            // 创建音频元素
            const audio = new Audio(audioUrl);
            
            // 播放音频
            audio.play().catch(error => {
                console.error('播放音频失败:', error);
            });
        }

        // 修改createDraggableWord函数,添加双击事件
        function createDraggableWord(text) {
            const word = document.createElement('div');
            word.className = 'draggable-word word';
            word.textContent = text;
            word.draggable = true;
            word.style.fontSize = document.getElementById('fontSize').value + 'px';

            // 添加双击事件监听器
            word.addEventListener('dblclick', function() {
                playYoudaoAudio(this.textContent.toLowerCase());
            });

            return word;
        }
    </script>
</body>

</html>
vocan 发表于 2025-4-19 23:35
请查看回复,是否满足要求
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2025-12-12 11:58

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表