Ry Mofadongxue2

<!DOCTYPE html>
<html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>游戏存档数据实时对照工具</title>
        <!-- 引入Vue 2 -->
        <script src="https://unpkg.com/vue@2.6.14/dist/vue.min.js"></script>
        <!-- 引入Font Awesome -->
        <link href="https://unpkg.com/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
 
        <style>
            /* 基础样式 */
            * {
                box-sizing: border-box;
                margin: 0;
                padding: 0;
            }
 
            body {
                font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
                background-color: #f3f4f6;
                color: #1f2937;
                line-height: 1.5;
                padding: 20px;
                min-height: 100vh;
            }
 
            .container {
                max-width: 1200px;
                margin: 0 auto;
                padding: 20px;
            }
 
            /* 头部样式 */
            header {
                text-align: center;
                margin-bottom: 40px;
            }
 
            h1 {
                font-size: clamp(1.8rem, 4vw, 2.5rem);
                font-weight: 700;
                margin-bottom: 10px;
                color: #1e293b;
            }
 
            header p {
                color: #6b7280;
                font-size: 1.1rem;
            }
 
            /* 主内容区样式 */
            main {
                display: flex;
                flex-direction: column;
                gap: 30px;
            }
 
            .card {
                background-color: white;
                border-radius: 8px;
                box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
                padding: 20px;
                transition: box-shadow 0.3s ease;
            }
 
            .card:hover {
                box-shadow: 0 10px 15px rgba(0, 0, 0, 0.1);
            }
 
            .card-header {
                display: flex;
                justify-content: space-between;
                align-items: center;
                margin-bottom: 15px;
            }
 
            .card-title {
                font-size: 1.25rem;
                font-weight: 600;
                display: flex;
                align-items: center;
            }
 
            .card-title i {
                margin-right: 8px;
            }
 
            .encrypted .card-title {
                color: #3b82f6;
            }
 
            .decrypted .card-title {
                color: #10b981;
            }
 
            .map-preview .card-title {
                color: #f59e0b;
            }
 
            /* 文本区域样式 */
            textarea {
                width: 100%;
                min-height: 250px;
                padding: 15px;
                border: 1px solid #d1d5db;
                border-radius: 6px;
                font-family: Consolas, Monaco, monospace;
                font-size: 0.875rem;
                resize: both;
                overflow: auto;
                transition: all 0.3s ease;
            }
 
            textarea:focus {
                outline: none;
                border-color: #3b82f6;
                box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.25);
            }
 
            .decrypted textarea:focus {
                border-color: #10b981;
                box-shadow: 0 0 0 2px rgba(16, 185, 129, 0.25);
            }
 
            /* 按钮样式 */
            .btn-group {
                display: flex;
                gap: 10px;
            }
 
            button {
                display: flex;
                align-items: center;
                gap: 5px;
                padding: 6px 12px;
                border: none;
                border-radius: 4px;
                font-size: 0.875rem;
                cursor: pointer;
                transition: all 0.3s ease;
            }
 
            .btn-primary {
                background-color: #3b82f6;
                color: white;
            }
 
            .btn-primary:hover {
                background-color: #2563eb;
            }
 
            .btn-secondary {
                background-color: #10b981;
                color: white;
            }
 
            .btn-secondary:hover {
                background-color: #059669;
            }
 
            .btn-neutral {
                background-color: #e5e7eb;
                color: #1f2937;
            }
 
            .btn-neutral:hover {
                background-color: #d1d5db;
            }
 
            /* 提示消息样式 */
            .message {
                padding: 15px;
                border-radius: 6px;
                display: flex;
                align-items: center;
                gap: 8px;
                margin-top: 10px;
            }
 
            .message-success {
                background-color: #dcfce7;
                color: #166534;
            }
 
            .message-error {
                background-color: #fee2e2;
                color: #b91c1c;
            }
 
            /* 底部样式 */
            footer {
                text-align: center;
                margin-top: 50px;
                color: #6b7280;
                font-size: 0.875rem;
            }
 
            /* 全屏模式样式 */
            .fullscreen-container {
                position: fixed;
                top: 0;
                left: 0;
                right: 0;
                bottom: 0;
                z-index: 9999;
                background-color: #1a1a1a;
                padding: 20px;
                display: flex;
                flex-direction: column;
            }
 
            .fullscreen-header {
                display: flex;
                justify-content: space-between;
                align-items: center;
                margin-bottom: 20px;
                color: white;
            }
 
            .fullscreen-textarea {
                flex: 1;
                width: 100%;
                min-height: auto !important;
                background-color: #2d2d2d;
                color: #f0f0f0;
                border: none;
                font-size: 1rem !important;
            }
 
            .fullscreen-textarea:focus {
                border-color: #10b981;
                box-shadow: 0 0 0 2px rgba(16, 185, 129, 0.25);
            }
 
            /* 地图预览样式 */
            .map-preview-container {
                margin-top: 15px;
            }
 
            .map-grid {
                border-collapse: collapse;
                margin: 0 auto;
            }
 
            .map-grid td {
                width: 30px;
                height: 30px;
                border: 1px solid #d1d5db;
                text-align: center;
                vertical-align: middle;
                font-size: 10px;
                background-color: #f9fafb;
            }
 
            /* 提示框样式 */
            .cell-tooltip {
                position: fixed;
                /* 改为fixed定位,相对于视口 */
                background-color: rgba(0, 0, 0, 0.8);
                color: white;
                padding: 4px 8px;
                border-radius: 4px;
                font-size: 12px;
                pointer-events: none;
                /* 让鼠标事件穿透提示框 */
                z-index: 1000;
                transition: opacity 0.1s;
                white-space: nowrap;
            }
 
            /* 响应式调整 */
            @media (max-width: 768px) {
                .card-header {
                    flex-direction: column;
                    align-items: flex-start;
                    gap: 10px;
                }
 
                .btn-group {
                    width: 100%;
                    justify-content: flex-start;
                }
 
                .map-grid td {
                    width: 20px;
                    height: 20px;
                }
            }
        </style>
    </head>
    <body>
        <div id="app" class="container">
            <header>
                <h1>游戏存档数据实时对照工具</h1>
                <p>加密数据与解密数据双向实时转换(含中英文翻译)</p>
            </header>
 
            <main>
                <!-- 加密数据区域 -->
                <div class="card encrypted">
                    <div class="card-header">
                        <h2 class="card-title">
                            <i class="fa fa-lock"></i>加密存档数据
                        </h2>
                        <button @click="copyEncrypted" class="btn-primary">
                            <i class="fa fa-copy"></i> 复制
                        </button>
                    </div>
                    <textarea v-model="encryptedData" @input="encryptToDecrypt"
                        placeholder="粘贴加密的游戏存档数据到这里..."></textarea>
                </div>
 
                <!-- 解密数据区域 -->
                <div class="card decrypted">
                    <div class="card-header">
                        <h2 class="card-title">
                            <i class="fa fa-unlock"></i>解密数据(中文)
                        </h2>
                        <div class="btn-group">
                            <button @click="toggleFullscreen" class="btn-neutral">
                                <i class="fa" :class="isFullscreen ? 'fa-compress' : 'fa-expand'"></i>
                                {{ isFullscreen ? '退出全屏' : '全屏编辑' }}
                            </button>
                            <button @click="copyDecrypted" class="btn-secondary">
                                <i class="fa fa-copy"></i> 复制
                            </button>
                        </div>
                    </div>
                    <textarea v-model="decryptedData" @input="decryptToEncrypt"
                        placeholder="解密后的中文数据会显示在这里,也可以直接编辑...未知字符会显示为【原字符】"></textarea>
                </div>
 
                <!-- 地图预览区域 -->
                <div class="card map-preview">
                    <div class="card-header">
                        <h2 class="card-title">
                            <i class="fa fa-map"></i>楼层地图预览
                        </h2>
                        <button @click="refreshMap" class="btn-neutral">
                            <i class="fa fa-refresh"></i> 刷新地图
                        </button>
                    </div>
 
                    <div v-if="mapData.length > 0" class="map-preview-container">
                        <!-- 浮动提示框 -->
                        <div v-if="showTooltip" class="cell-tooltip" :style="{ 
                      left: tooltipX + 'px', 
                      top: tooltipY + 'px' 
                    }">
                            {{ tooltipValue }}
                        </div>
 
                        <table class="map-grid">
                            <tbody>
                                <tr v-for="(row, rowIndex) in mapData" :key="rowIndex">
                                    <td v-for="(cell, colIndex) in row" :key="colIndex" :style="{ 
                                  backgroundColor: cell === '墙' ? 'black' : 
                                                cell === '空' ? 'white' : 'gray',
                                  width: '30px',
                                  height: '30px',
                                  border: '1px solid #ccc',
                                  position: 'relative'
                                }" @mouseenter="handleMouseEnter(cell, $event)" @mousemove="handleMouseMove($event)"
                                        @mouseleave="handleMouseLeave">
                                        <!-- 单元格视觉显示逻辑(按 o 值区分) -->
                                        <template v-if="typeof cell === 'object'">
                                            <!-- o===0 怪物(红色圆点) -->
                                            <template v-if="cell.o === 0">
                                                <span :style="{
                                              position: 'absolute',
                                              top: '50%',
                                              left: '50%',
                                              transform: 'translate(-50%, -50%)',
                                              width: '15px',
                                              height: '15px',
                                              borderRadius: '50%',
                                              backgroundColor: 'red',
                                              boxShadow: '0 0 3px rgba(255, 0, 0, 0.8)'
                                            }"></span>
                                            </template>
 
                                            <!-- o===1 装备宝箱(蓝色菱形) -->
                                            <template v-else-if="cell.o === 1">
                                                <span :style="{
                                              position: 'absolute',
                                              top: '50%',
                                              left: '50%',
                                              transform: 'translate(-50%, -50%) rotate(45deg)',
                                              width: '14px',
                                              height: '14px',
                                              backgroundColor: '#1e90ff',
                                              boxShadow: '0 0 4px rgba(30, 144, 255, 0.8)',
                                              border: '1px solid rgba(255, 255, 255, 0.5)'
                                            }"></span>
                                            </template>
 
                                            <!-- o===3 金钱(金黄色圆形) -->
                                            <template v-else-if="cell.o === 3">
                                                <span :style="{
                                              position: 'absolute',
                                              top: '50%',
                                              left: '50%',
                                              transform: 'translate(-50%, -50%)',
                                              width: '12px',
                                              height: '12px',
                                              borderRadius: '50%',
                                              backgroundColor: '#ffd700',
                                              boxShadow: '0 0 3px rgba(255, 215, 0, 0.8)'
                                            }"></span>
                                            </template>
 
                                            <!-- o===5 书柜装饰(棕色矩形) -->
                                            <template v-else-if="cell.o === 5">
                                                <span :style="{
                                              position: 'absolute',
                                              top: '50%',
                                              left: '50%',
                                              transform: 'translate(-50%, -50%)',
                                              width: '18px',
                                              height: '10px',
                                              backgroundColor: '#8b4513',
                                              border: '1px solid #5d2f09',
                                              borderRadius: '2px'
                                            }"></span>
                                            </template>
 
                                            <!-- o===6 剧情触发点(亮紫色五角星,增强静态发光效果) -->
                                            <template v-else-if="cell.o === 6">
                                              <span :style="{
                                                position: 'absolute',
                                                top: '50%',
                                                left: '50%',
                                                transform: 'translate(-50%, -50%)',
                                                width: '18px',
                                                height: '18px',
                                                backgroundColor: '#e6ccff', /* 更明亮的浅紫色 */
                                                clipPath: 'polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%)',
                                                boxShadow: '0 0 10px 3px rgba(230, 204, 255, 0.9), 0 0 15px 5px rgba(230, 204, 255, 0.6)', /* 强化静态发光 */
                                                border: '1px solid rgba(255, 255, 255, 0.8)', /* 白色边框增加亮度 */
                                                zIndex: 2 /* 确保在其他元素上方显示 */
                                              }"></span>
                                            </template>
 
                                            <!-- o===7 出口(绿色三角形) -->
                                            <template v-else-if="cell.o === 7">
                                                <span :style="{
                                              position: 'absolute',
                                              top: '50%',
                                              left: '50%',
                                              transform: 'translate(-50%, -50%)',
                                              width: 0,
                                              height: 0,
                                              borderLeft: '8px solid transparent',
                                              borderRight: '8px solid transparent',
                                              borderBottom: '16px solid #32cd32',
                                              boxShadow: '0 0 4px rgba(50, 205, 50, 0.8)'
                                            }"></span>
                                            </template>
 
                                            <!-- 未定义的 o 值(灰色问号) -->
                                            <template v-else>
                                                <span :style="{
                                              position: 'absolute',
                                              top: '50%',
                                              left: '50%',
                                              transform: 'translate(-50%, -50%)',
                                              color: '#888',
                                              fontSize: '16px',
                                              fontWeight: 'bold'
                                            }">?</span>
                                            </template>
                                        </template>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                    </div>
 
                    <div v-else class="message message-error">
                        <i class="fa fa-exclamation-circle"></i>
                        <span>未找到楼层地砖数据或数据格式不正确</span>
                    </div>
                </div>
 
                <!-- 状态提示 -->
                <div v-if="message" :class="'message message-' + messageType">
                    <i :class="messageType === 'success' ? 'fa fa-check-circle' : 'fa fa-exclamation-circle'"></i>
                    <span>{{ message }}</span>
                </div>
 
                <!-- 全屏编辑容器 -->
                <div v-if="isFullscreen" class="fullscreen-container">
                    <div class="fullscreen-header">
                        <h2><i class="fa fa-unlock"></i> 解密数据全屏编辑</h2>
                        <button @click="toggleFullscreen" class="btn-neutral">
                            <i class="fa fa-compress"></i> 退出全屏
                        </button>
                    </div>
                    <textarea v-model="fullscreenContent" @input="syncFullscreenChanges" class="fullscreen-textarea"
                        placeholder="在此编辑解密数据..."></textarea>
                </div>
            </main>
 
            <footer>
                <p>游戏存档数据转换工具 &copy; 2023</p>
            </footer>
        </div>
 
        <script>
            new Vue({
                el: '#app',
                data() {
                    return {
                        // 加密和解密数据
                        encryptedData: '',
                        decryptedData: '',
                        // 存储"楼层地砖"所在行的内容
                        floorTilesData: '',
                        // 解析后的地图数据(二维数组)
                        mapData: [],
                        showTooltip: false,
                        tooltipValue: '',
                        tooltipX: 0,
                        tooltipY: 0,
                        // 全屏相关数据
                        isFullscreen: false,
                        fullscreenContent: '',
                        // 消息提示
                        message: '',
                        messageType: 'success',
                        // 替换加密的密码对应表
                        cipherMap: {
                            // 未破解字母 --begin
                            "G": "★",
                            "\"": "■",
                            // 未破解字母 --end
                            "]": "{",
                            "[": "}",
                            "z": ":",
                            ".": "\"",
                            ":": ",",
                            ",": "[",
                            "!": "]",
                            "-": "-",
                            " ": " ",
                            "h": "a",
                            "Q": "b",
                            "L": "c",
                            "y": "d",
                            "1": "e",
                            "l": "f",
                            "X": "g",
                            "s": "h",
                            "Y": "i",
                            "d": "j",
                            "n": "k",
                            "i": "l",
                            "P": "m",
                            "J": "n",
                            "r": "o",
                            "F": "p",
                            "t": "q",
                            "Z": "r",
                            "V": "s",
                            "f": "t",
                            "w": "u",
                            "#": "v",
                            "I": "w",
                            "u": "x",
                            "C": "y",
                            "e": "A",
                            "v": "B",
                            "O": "C",
                            "T": "D",
                            "E": "E",
                            "j": "F",
                            "q": "G",
                            "@": "H",
                            "5": "I",
                            "o": "L",
                            "b": "M",
                            "4": "N",
                            "N": "O",
                            "K": "P",
                            "c": "R",
                            "7": "S",
                            "p": "T",
                            "k": "U",
                            "g": "W",
                            "}": "0",
                            "3": "1",
                            "S": "2",
                            "0": "3",
                            "B": "4",
                            "D": "5",
                            "9": "6",
                            "a": "7",
                            "8": "8",
                            "W": "9",
                            "{": ".",
                        },
                        // 英文到汉字的对照表
                        englishToChineseMap: {
                            // 基础属性
                            "shopsUnlocked": "已解锁商店",
                            "items": "物品",
                            "defense": "防御力",
                            "bestiaryMonsterIDs": "图鉴怪物ID",
                            "hpGemsThisRun": "本次获得的生命宝石",
                            "goldMultiplier": "金币倍率",
                            "storyProgress": "剧情进度",
                            "successfulRuns": "成功运行次数",
                            "attGems": "攻击宝石",
                            "defEarth": "土系防御",
                            "mpGemsThisRun": "本次获得的魔法宝石",
                            "defIce": "冰系防御",
                            "mpGems": "魔法宝石",
                            "potionDuration": "药水持续时间",
                            "maxMP": "最大魔法值",
                            "attGemsThisRun": "本次获得的攻击宝石",
                            "shopsFoundThisRun": "本次发现的商店",
                            "hp": "当前生命值",
                            "attack": "攻击力",
                            "attWind": "风系攻击",
                            "foundFloorObjects": "发现的楼层物品",
                            "runsToBeatTheGame": "通关所需运行次数",
                            "secretTheseFiveFloors": "这五层的秘密",
                            "gamepadSelectedHotkeySlot": "手柄选中的快捷键槽",
                            "creationTime": "创建时间",
                            "numFloorObjects": "楼层物品数量",
                            "snapshotData": "快照数据",
                            "gold": "金币",
                            "equippedItems": "已装备物品",
                            "skillNodesUnlocked": "已解锁技能节点",
                            "mp": "当前魔法值",
                            "nextLevelAt": "下一等级所需经验",
                            "level": "等级",
                            "magGemCost": "魔法宝石成本",
                            "defGems": "防御宝石",
                            "attEarth": "土系攻击",
                            "attIce": "冰系攻击",
                            "skillGemCost": "技能宝石成本",
                            "dodgeChance": "闪避几率",
                            "hpGems": "生命宝石",
                            "maxHP": "最大生命值",
                            "magic": "魔法值",
                            "defWind": "风系防御",
                            "critChance": "暴击几率",
                            "hotkeySlotItems": "快捷键槽物品",
                            "dropBonus": "掉落加成",
                            "defLight": "光系防御",
                            "killChance": "击杀几率",
                            "sellPercent": "出售百分比",
                            "gameSwitches": "游戏开关",
                            "hpGemCost": "生命宝石成本",
                            "skillPoints": "技能点",
                            "skillGems": "技能宝石",
                            "attLight": "光系攻击",
                            "hpRegen": "生命回复",
                            "defGemCost": "防御宝石成本",
                            "magGems": "魔法宝石",
                            "attFire": "火系攻击",
                            "attDark": "暗系攻击",
                            "mpGemCost": "魔法宝石成本",
                            "mpRegen": "魔法回复",
                            "bleed": "流血效果",
                            "attGemCost": "攻击宝石成本",
                            "skillPointsPerLevel": "每级技能点",
                            "defDark": "暗系防御",
                            "defFire": "火系防御",
                            "attackDelay": "攻击延迟",
                            "critMultiplier": "暴击倍率",
                            "potionEfficiency": "药水效率",
                            "lifeDrain": "生命吸取",
                            "exp": "经验值",
                            "expMultiplier": "经验倍率",
 
                            // 其他属性
                            "startPoint": "起始点",
                            "museumItems": "博物馆物品",
                            "deepestFloor": "最深楼层",
                            "currentFloor": "当前楼层",
                            "heroType": "英雄类型",
                            "magGemsThisRun": "本次获得的魔法宝石",
                            "runStartGold": "运行开始时的金币",
                            "itemsEnchanted": "已附魔物品数量",
                            "skillGemsThisRun": "本次获得的技能宝石",
                            "floorTiles": "楼层地砖",
                            "wingsFound": "已发现翅膀",
                            "legacySavesChecked": "已检查旧存档",
                            "activeEffects": "活跃效果",
                            "exists": "是否存在",
                            "sorcerorPhase": "巫师阶段",
                            "potionsCrafted": "已制作药水数量",
                            "runStartLevel": "运行开始时的等级",
                            "defGemsThisRun": "本次获得的防御宝石",
                            "canChangeHeroes": "是否可更换英雄",
                            "shopItems": "商店物品",
                            "hotkeyItems": "快捷键物品",
                            "wingsSoldFloor": "翅膀出售楼层",
                            "runStartFloor": "运行开始时的楼层",
                            "gameMode": "游戏模式",
                            "newGamePlusNum": "游戏周目",
                            "secretRoomsFound": "已发现的秘密房间",
                            "messageWins": "胜利消息",
 
                            // 地图信息
                            "★": "空",
                            "■": "墙",
                        },
                        // 反向映射表(用于解密到加密)
                        reverseCipherMap: {},
                        // 汉字到英文的反向对照表(用于中文编辑后重新加密)
                        chineseToEnglishMap: {},
                        // 排序后的中文键列表(按长度排序,用于正确翻译)
                        sortedChineseKeys: []
                    }
                },
                created() {
                    // 生成反向映射表
                    this.generateReverseMap();
                    // 生成中文到英文的反向对照表及排序后的键列表
                    this.generateChineseToEnglishMap();
                },
                methods: {
                    // 生成反向映射表(加密映射的反向)
                    generateReverseMap() {
                        for (const [key, value] of Object.entries(this.cipherMap)) {
                            this.reverseCipherMap[value] = key;
                        }
                    },
 
                    // 生成中文到英文的反向对照表及排序后的键列表
                    generateChineseToEnglishMap() {
                        // 创建中文到英文的映射
                        for (const [english, chinese] of Object.entries(this.englishToChineseMap)) {
                            this.chineseToEnglishMap[chinese] = english;
                        }
 
                        // 提取所有中文键并按长度降序排序(长词汇优先)
                        this.sortedChineseKeys = Object.keys(this.chineseToEnglishMap)
                            .sort((a, b) => b.length - a.length);
                    },
 
                    // 将英文转换为中文
                    translateToChinese(text) {
                        let result = text;
                        // 先将较长的英文词汇翻译,避免被短词汇分割
                        const sortedEnglishEntries = Object.entries(this.englishToChineseMap)
                            .sort((a, b) => b[0].length - a[0].length);
 
                        // 遍历排序后的英文-中文映射表,替换所有匹配的英文为中文
                        for (const [english, chinese] of sortedEnglishEntries) {
                            const regex = new RegExp(`${english}`, 'g');
                            result = result.replace(regex, chinese);
                        }
                        return result;
                    },
 
                    // 将中文转换为英文(用于编辑后重新加密)
                    translateToEnglish(text) {
                        let result = text;
 
                        // 使用排序后的中文键列表(长词汇优先)进行替换
                        for (const chinese of this.sortedChineseKeys) {
                            const english = this.chineseToEnglishMap[chinese];
                            // 使用正则表达式确保匹配整个词汇
                            const regex = new RegExp(`${chinese}`, 'g');
                            result = result.replace(regex, english);
                        }
 
                        return result;
                    },
 
                    // 处理格式化:遇到[时,在遇到]之前不换行,其他情况遇到{或}时换行
                    formatText(jsonStr) {
                        let result = '';
                        let indentLevel = 0; // 缩进层级
                        let inString = false; // 是否在字符串中
                        let prevChar = ''; // 上一个字符
                        let currentValue = ''; // 当前值缓存
                        const fullWidthSpace = ' '; // 中文全角空格
                        let openBraces = 0; // 未闭合的"{"个数
                        let openBrackets = 0; // 未闭合的"["个数
 
                        // 用于生成缩进的辅助函数,使用全角空格
                        const getIndent = () => fullWidthSpace.repeat(indentLevel * 2);
 
                        for (const char of jsonStr) {
                            // 处理字符串边界(考虑转义的引号)
                            if (char === '"' && prevChar !== '\\') {
                                inString = !inString;
                                result += char;
                                prevChar = char;
                                continue;
                            }
 
                            // 不在字符串中时才处理格式化
                            if (!inString) {
                                // 缓存当前值字符
                                if (char !== '{' && char !== '}' && char !== '[' && char !== ']' &&
                                    char !== ',' && char !== ':' && char.trim() !== '') {
                                    currentValue += char;
                                }
 
                                // 判断是否在数组内部
                                const inArray = openBrackets > 0;
 
                                switch (char) {
                                    case '{':
                                        indentLevel++;
                                        openBraces++;
                                        if (currentValue) {
                                            result += currentValue;
                                            currentValue = '';
                                        }
 
                                        result += char;
                                        // 数组内部不添加任何空格和换行,外部则换行缩进
                                        if (!inArray) {
                                            result += '\n' + getIndent();
                                        }
                                        break;
 
                                    case '[':
                                        if (currentValue) {
                                            result += currentValue;
                                            currentValue = '';
                                        }
                                        result += char;
                                        indentLevel++;
                                        openBrackets++;
                                        break;
 
                                    case '}':
                                        indentLevel = Math.max(0, indentLevel - 1);
                                        openBraces = Math.max(0, openBraces - 1);
                                        if (currentValue) {
                                            result += currentValue;
                                            currentValue = '';
                                        }
 
                                        // 数组内部不添加任何空格和换行,外部则换行缩进
                                        if (!inArray) {
                                            result += '\n' + getIndent() + char;
                                        } else {
                                            result += char;
                                        }
                                        break;
 
                                    case ']':
                                        indentLevel = Math.max(0, indentLevel - 1);
                                        openBrackets = Math.max(0, openBrackets - 1);
                                        if (currentValue) {
                                            result += currentValue;
                                            currentValue = '';
                                        }
                                        result += char;
                                        break;
 
                                    case ',':
                                        if (currentValue) {
                                            result += currentValue;
                                            currentValue = '';
                                        }
 
                                        result += char;
                                        // 数组内部不添加任何空格和换行,外部则换行缩进
                                        if (!inArray) {
                                            result += '\n' + getIndent();
                                        }
                                        break;
 
                                    case ':':
                                        if (currentValue) {
                                            result += currentValue;
                                            currentValue = '';
                                        }
                                        // 数组内部不添加空格,外部添加全角空格
                                        result += ':' + (inArray ? '' : fullWidthSpace);
                                        break;
 
                                    case ' ':
                                    case '\n':
                                    case '\t':
                                        // 忽略原有的空白字符,但如果有缓存值,先添加
                                        if (currentValue) {
                                            result += currentValue;
                                            currentValue = '';
                                        }
                                        break;
 
                                    default:
                                        // 非特殊字符且不在字符串中,不直接添加,由缓存处理
                                        break;
                                }
                            } else {
                                // 在字符串中,直接添加字符
                                result += char;
                            }
 
                            prevChar = char;
                        }
 
                        // 添加最后可能剩余的缓存值
                        if (currentValue) {
                            result += currentValue;
                        }
 
                        return result;
                    },
 
                    // 提取"楼层地砖"所在行的内容
                    extractFloorTilesData(text) {
                        // 查找包含"楼层地砖"的行
                        const lines = text.split('\n');
                        for (const line of lines) {
                            if (line.includes('楼层地砖')) {
                                // 提取并清理数据(去除前后空格和逗号)
                                this.floorTilesData = line.trim().replace(/,$/, '');
                                // 提取后立即解析地图数据
                                this.parseFloorTilesData();
                                break;
                            }
                        }
                    },
 
                    // 解析楼层地砖数据为二维数组
                    // 解析楼层地砖数据为二维数组,并处理全是"墙"的起始行和列
                    parseFloorTilesData() {
                        // 重置地图数据
                        this.mapData = [];
 
                        if (!this.floorTilesData) {
                            return;
                        }
 
                        try {
                            // 提取冒号后的数组部分
                            const colonIndex = this.floorTilesData.indexOf(':');
                            const dataPart = colonIndex !== -1 ? this.floorTilesData.slice(colonIndex + 1).trim() : '';
 
                            if (!dataPart) {
                                throw new Error("未找到有效的数组数据");
                            }
 
                            // 处理没有引号的字符串元素,给它们添加引号
                            let formattedData = dataPart.replace(/([^,\[\]{}:]+?)(?=[,\[\]}:])/g, (match) => {
                                if (!/^\d+$/.test(match.trim()) && !/^["'].*["']$/.test(match.trim())) {
                                    return `"${match.trim()}"`;
                                }
                                return match;
                            });
 
                            // 将处理后的字符串解析为JSON
                            const parsedData = JSON.parse(formattedData);
 
                            // 验证是否为二维数组
                            if (Array.isArray(parsedData) && parsedData.every(row => Array.isArray(row))) {
                                // 处理全是"墙"的行和列
                                let processedData = [...parsedData];
 
                                // 移除全是"墙"的起始行
                                while (processedData.length > 0 && processedData[0].every(cell => cell === "墙")) {
                                    processedData.shift();
                                }
 
                                // 移除全是"墙"的末尾行
                                while (processedData.length > 0 && processedData[processedData.length - 1].every(cell =>
                                        cell === "墙")) {
                                    processedData.pop();
                                }
 
                                // 关键修改:进行矩阵转置,将列转为行
                                if (processedData.length > 0) {
                                    const rows = processedData.length;
                                    const cols = processedData[0].length;
 
                                    // 初始化转置后的数组
                                    let transposedData = [];
                                    for (let j = 0; j < cols; j++) {
                                        transposedData[j] = [];
                                        for (let i = 0; i < rows; i++) {
                                            // 原[i][j]变为新[j][i]
                                            transposedData[j][i] = processedData[i][j];
                                        }
                                    }
 
                                    // 移除全是""的起始行
                                    while (transposedData.length > 0 && transposedData[0].every(cell => cell === "墙")) {
                                        transposedData.shift();
                                    }
 
                                    // 移除全是"墙"的末尾行
                                    while (transposedData.length > 0 && transposedData[transposedData.length - 1].every(
                                            cell => cell === "墙")) {
                                        transposedData.pop();
                                    }
 
                                    // 新增:在最外层添加一层"墙"
                                    if (transposedData.length > 0) {
                                        const colsCount = transposedData[0].length;
 
                                        // 创建顶部和底部的墙行(全是"墙")
                                        const wallRow = Array(colsCount).fill("墙");
 
                                        // 在顶部添加墙行
                                        transposedData.unshift(wallRow);
                                        // 在底部添加墙行
                                        transposedData.push(wallRow);
 
                                        // 在每行的首尾添加墙
                                        transposedData = transposedData.map(row => {
                                            return ["墙", ...row, "墙"];
                                        });
                                    }
 
                                    this.mapData = transposedData;
                                } else {
                                    this.mapData = processedData;
                                }
 
                                // 显示解析成功信息
                                const rowCount = this.mapData.length;
                                const colCount = rowCount > 0 ? this.mapData[0].length : 0;
                                this.showMessage(`地图解析成功,共 ${rowCount} 行 ${colCount} 列,已添加外层墙`, 'success');
                            } else {
                                throw new Error("解析的数据不是二维数组");
                            }
                        } catch (error) {
                            console.error("解析楼层地砖数据失败:", error);
                            this.showMessage(`地图解析失败: ${error.message}`, 'error');
                            this.mapData = [];
                        }
                    },
 
                    // 刷新地图预览
                    refreshMap() {
                        this.parseFloorTilesData();
                    },
 
                    // 加密数据转解密数据:先字符映射,再翻译为中文,最后格式化
                    encryptToDecrypt() {
                        // 1. 应用字符映射转换,未找到映射的字符用【】包裹
                        let result = '';
                        for (const char of this.encryptedData) {
                            if (this.cipherMap.hasOwnProperty(char)) {
                                result += this.cipherMap[char];
                            } else {
                                console.log('char', char)
                                // 对未知字符使用【】包裹
                                result += `【${char}】`;
                            }
                        }
 
                        // 2. 将结果中的英文翻译为中文
                        const translatedResult = this.translateToChinese(result);
 
                        // 3. 应用格式化规则
                        const formattedResult = this.formatText(translatedResult);
 
                        // 4. 提取楼层地砖数据
                        this.extractFloorTilesData(formattedResult);
 
                        // 避免循环更新
                        if (this.decryptedData !== formattedResult) {
                            this.decryptedData = formattedResult;
 
                            // 如果在全屏模式下,同步更新全屏内容
                            if (this.isFullscreen) {
                                this.fullscreenContent = formattedResult;
                            }
                        }
                    },
 
                    // 解密数据转加密数据:先将中文翻译为英文,移除格式,再字符反向映射
                    decryptToEncrypt() {
                        this.fullscreenContent = this.decryptedData;
 
                        // 1. 先将中文翻译回英文
                        const englishText = this.translateToEnglish(this.decryptedData);
 
                        // 2. 移除所有换行符、制表符、中文空格
                        let textWithoutNewlines = englishText.replace(/[\n\r\t ]/g, '');
 
                        // 3. 处理【】包裹的未知字符,提取原始字符
                        // 使用正则表达式匹配【...】格式并提取内容
                        const unknownCharRegex = /【(.*?)】/g;
                        textWithoutNewlines = textWithoutNewlines.replace(unknownCharRegex, (match, p1) => p1);
 
                        // 4. 应用反向字符映射
                        let result = '';
                        for (const char of textWithoutNewlines) {
                            if (this.reverseCipherMap.hasOwnProperty(char)) {
                                result += this.reverseCipherMap[char];
                            } else {
                                // 对于解密区新增的未知字符,直接保留
                                result += char;
                            }
                        }
 
                        // 5. 更新楼层地砖数据
                        this.extractFloorTilesData(this.decryptedData);
 
                        // 避免循环更新
                        if (this.encryptedData !== result) {
                            this.encryptedData = result;
                        }
                    },
 
                    // 复制加密数据
                    copyEncrypted() {
                        this.copyToClipboard(this.encryptedData, '加密数据已复制到剪贴板');
                    },
 
                    // 复制解密数据
                    copyDecrypted() {
                        this.copyToClipboard(this.decryptedData, '解密数据已复制到剪贴板');
                    },
 
                    // 复制到剪贴板通用方法
                    copyToClipboard(text, successMsg) {
                        if (!text) {
                            this.showMessage('没有可复制的内容', 'error');
                            return;
                        }
 
                        navigator.clipboard.writeText(text)
                            .then(() => {
                                this.showMessage(successMsg, 'success');
                            })
                            .catch(err => {
                                this.showMessage('复制失败,请手动复制', 'error');
                                console.error('复制失败:', err);
                            });
                    },
 
                    // 显示消息提示
                    showMessage(text, type = 'success') {
                        this.message = text;
                        this.messageType = type;
 
                        // 3秒后自动隐藏消息
                        setTimeout(() => {
                            this.message = '';
                        }, 3000);
                    },
 
                    // 切换全屏模式
                    toggleFullscreen() {
                        if (!this.isFullscreen) {
                            // 进入全屏模式前保存当前内容
                            this.fullscreenContent = this.decryptedData;
                            this.isFullscreen = true;
 
                            // 进入全屏后自动聚焦文本框
                            this.$nextTick(() => {
                                document.querySelector('.fullscreen-textarea').focus();
                            });
 
                            this.showMessage('已进入全屏编辑模式', 'success');
                        } else {
                            // 退出全屏模式时更新内容
                            this.decryptedData = this.fullscreenContent;
                            this.decryptToEncrypt(); // 同步到加密区域
                            this.isFullscreen = false;
                            this.showMessage('已退出全屏编辑模式', 'success');
                        }
                    },
 
                    // 同步全屏模式下的编辑内容
                    syncFullscreenChanges() {
                        this.decryptedData = this.fullscreenContent;
 
                        // 1. 先将中文翻译回英文
                        const englishText = this.translateToEnglish(this.fullscreenContent);
 
                        // 2. 移除所有换行符、制表符、中文空格
                        let textWithoutNewlines = englishText.replace(/[\n\r\t ]/g, '');
 
                        // 3. 处理【】包裹的未知字符,提取原始字符
                        const unknownCharRegex = /【(.*?)】/g;
                        textWithoutNewlines = textWithoutNewlines.replace(unknownCharRegex, (match, p1) => p1);
 
                        // 4. 应用反向字符映射
                        let result = '';
                        for (const char of textWithoutNewlines) {
                            if (this.reverseCipherMap.hasOwnProperty(char)) {
                                result += this.reverseCipherMap[char];
                            } else {
                                // 对于解密区新增的未知字符,直接保留
                                result += char;
                            }
                        }
 
                        // 5. 更新楼层地砖数据
                        this.extractFloorTilesData(this.fullscreenContent);
 
                        // 避免循环更新
                        if (this.encryptedData !== result) {
                            this.encryptedData = result;
                        }
                    },
                    // 鼠标进入单元格时显示提示
                    handleMouseEnter(cellValue, event) {
                        this.showTooltip = true;
                        this.updateTooltipPosition(event);
 
                        // 根据cellValue的类型和属性设置提示文字
                        if (typeof cellValue === 'object' && cellValue !== null) {
                            if ('o' in cellValue) {
                                switch (cellValue.o) {
                                    case 0:
                                        // 处理存在p属性的情况(怪物)
                                        if ('p' in cellValue) {
                                            switch (cellValue.p) {
                                                case 266:
                                                    this.tooltipValue = `老鼠[${JSON.stringify(cellValue)}]`;
                                                    break;
                                                case 273:
                                                    this.tooltipValue = `蝙蝠[${JSON.stringify(cellValue)}]`;
                                                    break;
                                                default:
                                                    this.tooltipValue = `未知怪物[${JSON.stringify(cellValue)}]`;
                                            }
                                        } else
                                            this.tooltipValue = `未知[${JSON.stringify(cellValue)}]`;
                                        break;
                                    case 1:
                                        this.tooltipValue = `装备宝箱[${JSON.stringify(cellValue)}]`;
                                        break;
                                    case 3:
                                        if ('g' in cellValue) {
                                            this.tooltipValue = `金钱: ${cellValue.g}[${JSON.stringify(cellValue)}]`;
                                        } else
                                            this.tooltipValue = `未知[${JSON.stringify(cellValue)}]`;
                                        break;
                                    case 5:
                                        this.tooltipValue = `书柜装饰[${JSON.stringify(cellValue)}]`;
                                        break;
                                    case 6:
                                        this.tooltipValue = `剧情触发点[${JSON.stringify(cellValue)}]`;
                                        break;
                                    case 7:
                                        this.tooltipValue = `出口[${JSON.stringify(cellValue)}]`;
                                        break;
                                    default:
                                        this.tooltipValue = `未知[${JSON.stringify(cellValue)}]`;
                                        break;
                                }
                            } else
                                this.tooltipValue = `未知[${JSON.stringify(cellValue)}]`;
                        } else { // 非JSON对象的情况(墙、空等)
                            this.tooltipValue = cellValue;
                        }
                    },
 
                    // 鼠标在单元格内移动时更新提示位置
                    handleMouseMove(event) {
                        this.updateTooltipPosition(event);
                    },
 
                    // 鼠标离开单元格时隐藏提示
                    handleMouseLeave() {
                        this.showTooltip = false;
                    },
 
                    // 更新提示框位置,考虑滚动偏移
                    updateTooltipPosition(event) {
                        // 使用clientX和clientY获取相对于视口的位置
                        // 这样即使页面滚动,提示框也能正确显示
                        this.tooltipX = event.clientX + 10;
                        this.tooltipY = event.clientY + 10;
 
                        // 可选:检测边界,确保提示框不会超出视口
                        const tooltipWidth = 100; // 估算提示框宽度
                        const tooltipHeight = 30; // 估算提示框高度
 
                        // 如果右侧超出视口,调整到左侧
                        if (this.tooltipX + tooltipWidth > window.innerWidth) {
                            this.tooltipX = event.clientX - tooltipWidth - 10;
                        }
 
                        // 如果底部超出视口,调整到上方
                        if (this.tooltipY + tooltipHeight > window.innerHeight) {
                            this.tooltipY = event.clientY - tooltipHeight - 10;
                        }
                    }
                }
            });
        </script>
    </body>
</html>
除非特别注明,本页内容采用以下授权方式: Creative Commons Attribution-ShareAlike 3.0 License