调整一下ui,字符比较模式和调色盘高亮改为上下排列,按钮改为复选框.
>
<!DOCTYPE html>
> <html lang=“zh-CN”>
> <head>
> <meta charset=“UTF-8”>
> <meta name=“viewport” content=“width=device-width, initial-scale=1.0”>
> <title>文本对比工具</title>
> <style>
> body {
> font-family: ‘Segoe UI’, Tahoma, Geneva, Verdana, sans-serif;
> margin: 0;
> padding: 20px;
> background-color: #f0f4f8;
> color: #333;
> }
> .container {
> max-width: 1200px;
> margin: 0 auto;
> background-color: #ffffff;
> padding: 30px;
> border-radius: 12px;
> box-shadow: 0 4px 6px rgba(0,0,0,0.1);
> }
> h1 {
> text-align: center;
> color: #2c3e50;
> margin-bottom: 20px;
> }
> .description {
> text-align: center;
> color: #7f8c8d;
> margin-bottom: 20px;
> }
> .input-section {
> display: flex;
> gap: 20px;
> margin-bottom: 20px;
> }
> .textarea-wrapper {
> flex: 1;
> }
> textarea {
> width: 100%;
> height: 150px;
> padding: 15px;
> border: 1px solid #bdc3c7;
> border-radius: 8px;
> resize: vertical;
> font-size: 14px;
> transition: border-color 0.3s ease;
> }
> textarea:focus {
> outline: none;
> border-color: #3498db;
> }
> .button-group {
> display: flex;
> justify-content: center;
> gap: 20px;
> margin-bottom: 20px;
> }
> button {
> padding: 12px 24px;
> background-color: #3498db;
> color: white;
> border: none;
> border-radius: 6px;
> cursor: pointer;
> font-size: 16px;
> transition: background-color 0.3s ease;
> }
> button:hover {
> background-color: #2980b9;
> }
> .output-section {
> display: flex;
> border: 1px solid #bdc3c7;
> border-radius: 8px;
> overflow: hidden;
> opacity: 0;
> transition: opacity 0.5s ease;
> }
> .output-section.visible {
> opacity: 1;
> }
> .output-wrapper {
> flex: 1;
> display: flex;
> }
> .line-numbers {
> padding: 15px 10px;
> background-color: #ecf0f1;
> border-right: 1px solid #bdc3c7;
> text-align: right;
> color: #7f8c8d;
> font-size: 14px;
> min-width: 30px;
> }
> .output {
> flex: 1;
> padding: 15px;
> white-space: pre-wrap;
> word-break: break-all;
> min-height: 200px;
> overflow-y: scroll;
> font-size: 14px;
> line-height: 1.6;
> }
> .divider {
> width: 2px;
> background-color: #bdc3c7;
> }
> .diff {
> background-color: #ffeaa7;
> padding: 2px 0;
> }
> .same {
> background-color: #add8e6;
> padding: 2px 0;
> }
> .toggle-container {
> display: flex;
> align-items: center;
> justify-content: space-between;
> gap: 20px;
> margin-bottom: 20px;
> }
> .toggle-switch {
> position: relative;
> display: inline-block;
> width: 60px;
> height: 34px;
> }
> .toggle-switch input {
> opacity: 0;
> width: 0;
> height: 0;
> }
> .slider {
> position: absolute;
> cursor: pointer;
> top: 0;
> left: 0;
> right: 0;
> bottom: 0;
> background-color: #ccc;
> transition: .4s;
> border-radius: 34px;
> }
> .slider:before {
> position: absolute;
> content: “”;
> height: 26px;
> width: 26px;
> left: 4px;
> bottom: 4px;
> background-color: white;
> transition: .4s;
> border-radius: 50%;
> }
> input:checked + .slider {
> background-color: #2196F3;
> }
> input:checked + .slider:before {
> transform: translateX(26px);
> }
> .toggle-label {
> margin-left: 10px;
> line-height: 34px;
> }
> </style>
> </head>
> <body>
> <div class=“container”>
> <h1>文本对比工具</h1>
> <p class=“description”>输入两段文本,点击"对比文本"按钮查看差异。黄色高亮显示不同之处,蓝色高亮显示相同之处。</p>
> <div class=“toggle-container”>
> <div class=“toggle-container-item”>
> <label class=“toggle-switch”>
> <input type=“checkbox” id=“compareMode”>
> <span class=“slider”></span>
> </label>
> <span class=“toggle-label”>字符比较模式(默认为段落比较,开启后使用贪婪匹配最大相似文本)</span>
> </div>
> <div class=“toggle-container-item”>
> <label class=“toggle-switch”>
> <input type=“checkbox” id=“paletteMode”>
> <span class=“slider”></span>
> </label>
> <span class=“toggle-label”>调色盘高亮(默认黄色显示不同文本,开启后使用蓝色显示相同文本)</span>
> </div>
> </div>
> <div class=“input-section”>
> <div class=“textarea-wrapper”>
> <textarea id=“text1” placeholder=“输入第一段文本”></textarea>
> </div>
> <div class=“textarea-wrapper”>
> <textarea id=“text2” placeholder=“输入第二段文本”></textarea>
> </div>
> </div>
> <div class=“button-group”>
> <button onclick=“compareTexts()”>对比文本</button>
> <button onclick=“clearTexts()”>清除文本</button>
> </div>
> <div class=“output-section” id=“outputSection”>
> <div class=“output-wrapper”>
> <div class=“line-numbers” id=“lineNumbers1”></div>
> <div id=“output1” class=“output”></div>
> </div>
> <div class=“divider”></div>
> <div class=“output-wrapper”>
> <div class=“line-numbers” id=“lineNumbers2”></div>
> <div id=“output2” class=“output”></div>
> </div>
> </div>
> </div>
>
>
<script>
> // 页面加载时恢复保存的文本
> window.onload = function() {
> document.getElementById(‘text1’).value = localStorage.getItem(‘text1’) || ‘’;
> document.getElementById(‘text2’).value = localStorage.getItem(‘text2’) || ‘’;
> }
>
> function compareTexts() {
> const text1 = document.getElementById(‘text1’).value;
> const text2 = document.getElementById(‘text2’).value;
> const isCharMode = document.getElementById(‘compareMode’).checked;
> const isPaletteMode = document.getElementById(‘paletteMode’).checked;
>
> // 保存文本到localStorage
> localStorage.setItem(‘text1’, text1);
> localStorage.setItem(‘text2’, text2);
>
> const output1 = document.getElementById(‘output1’);
> const output2 = document.getElementById(‘output2’);
> const lineNumbers1 = document.getElementById(‘lineNumbers1’);
> const lineNumbers2 = document.getElementById(‘lineNumbers2’);
>
> const result1 = highlightDifferences(text1, text2, isCharMode, isPaletteMode);
> const result2 = highlightDifferences(text2, text1, isCharMode, isPaletteMode);
>
> output1.innerHTML = result1.html;
> output2.innerHTML = result2.html;
>
> // 显示行号
> lineNumbers1.innerHTML = generateLineNumbers(result1.lineCount);
> lineNumbers2.innerHTML = generateLineNumbers(result2.lineCount);
>
> // 设置滚动同步
> setupScrollSync(output1, output2);
>
> // 显示结果区域
> const outputSection = document.getElementById(‘outputSection’);
> outputSection.classList.add(‘visible’);
> }
>
>
>
function escapeHTML(html) {
> return html
> .replace(/&/g, ‘&’)
> .replace(/</g, ‘<’)
> .replace(/>/g, ‘>’)
> .replace(/“/g, '”‘)
> .replace(/’/g, ‘’‘);
> }
>
>
function highlightDifferences(text1, text2, isCharMode, isPaletteMode) {
> const lines1 = text1.split(’\n‘);
> const lines2 = text2.split(’\n‘);
> let result = ‘’;
> let lineCount = 0;
>
>
for (let i = 0; i < Math.max(lines1.length, lines2.length); i++) {
> const line1 = lines1[i] ? escapeHTML(lines1[i]) : ‘’;
> const line2 = lines2[i] ? escapeHTML(lines2[i]) : ‘’;
> let lineResult = ‘’;
>
> if (isCharMode) {
> const lcs = longestCommonSubsequence(line1, line2);
> lineResult = isPaletteMode ? highlightCharSame(line1, line2, lcs) : highlightCharDifferences(line1, line2, lcs);
> } else {
> const words1 = line1.split(/\s+/);
> const words2 = line2.split(/\s+/);
> const lcs = longestCommonSubsequence(words1.join(’ ‘), words2.join(’ '));
> lineResult = isPaletteMode ? highlightWordSame(words1, words2, lcs) : highlightWordDifferences(words1, words2, lcs);
> }
>
> result += lineResult + ‘\n’;
> lineCount++;
> }
>
> return { html: result, lineCount: lineCount };
> }
>
> function longestCommonSubsequence(s1, s2) {
> const m = s1.length;
> const n = s2.length;
> const dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
>
> for (let i = 1; i <= m; i++) {
> for (let j = 1; j <= n; j++) {
> if (s1[i - 1] === s2[j - 1]) {
> dp[i][j] = dp[i - 1][j - 1] + 1;
> } else {
> dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
> }
> }
> }
>
> let lcs = ‘’;
> let i = m, j = n;
> while (i > 0 && j > 0) {
> if (s1[i - 1] === s2[j - 1]) {
> lcs = s1[i - 1] + lcs;
> i–;
> j–;
> } else if (dp[i - 1][j] > dp[i][j - 1]) {
> i–;
> } else {
> j–;
> }
> }
>
> return lcs;
> }
>
> function highlightCharDifferences(line1, line2, lcs) {
> let result = ‘’;
> let i = 0, j = 0;
>
> for (let k = 0; k < lcs.length; k++) {
> while (i < line1.length && line1[i] !== lcs[k]) {
> result += <span class="diff">${line1[i]}</span>
;
> i++;
> }
> while (j < line2.length && line2[j] !== lcs[k]) {
> j++;
> }
> if (i < line1.length && line1[i] === lcs[k]) {
> result += line1[i];
> i++;
> }
> if (j < line2.length && line2[j] === lcs[k]) {
> j++;
> }
> }
>
> while (i < line1.length) {
> result += <span class="diff">${line1[i]}</span>
;
> i++;
> }
>
> return result;
> }
>
> function highlightCharSame(line1, line2, lcs) {
> let result = ‘’;
> let i = 0, j = 0;
>
> for (let k = 0; k < lcs.length; k++) {
> while (i < line1.length && line1[i] !== lcs[k]) {
> result += line1[i];
> i++;
> }
> while (j < line2.length && line2[j] !== lcs[k]) {
> j++;
> }
> if (i < line1.length && line1[i] === lcs[k]) {
> result += <span class="same">${line1[i]}</span>
;
> i++;
> }
> if (j < line2.length && line2[j] === lcs[k]) {
> j++;
> }
> }
>
> while (i < line1.length) {
> result += line1[i];
> i++;
> }
>
> return result;
> }
>
> function highlightWordDifferences(words1, words2, lcs) {
> let result = ‘’;
> let i = 0, j = 0;
>
> for (let k = 0; k < lcs.length; k++) {
> while (i < words1.length && words1[i] !== lcs[k]) {
> result += <span class="diff">${words1[i]}</span>
;
> i++;
> }
> while (j < words2.length && words2[j] !== lcs[k]) {
> j++;
> }
> if (i < words1.length && words1[i] === lcs[k]) {
> result += words1[i] + ’ ';
> i++;
> }
> if (j < words2.length && words2[j] === lcs[k]) {
> j++;
> }
> }
>
> while (i < words1.length) {
> result += <span class="diff">${words1[i]}</span>
;
> i++;
> }
>
> return result;
> }
>
> function highlightWordSame(words1, words2, lcs) {
> let result = ‘’;
> let i = 0, j = 0;
>
> for (let k = 0; k < lcs.length; k++) {
> while (i < words1.length && words1[i] !== lcs[k]) {
> result += words1[i] + ’ ‘;
> i++;
> }
> while (j < words2.length && words2[j] !== lcs[k]) {
> j++;
> }
> if (i < words1.length && words1[i] === lcs[k]) {
> result += <span class="same">${words1[i]}</span>
;
> i++;
> }
> if (j < words2.length && words2[j] === lcs[k]) {
> j++;
> }
> }
>
> while (i < words1.length) {
> result += words1[i] + ’ ‘;
> i++;
> }
>
> return result;
> }
>
> function generateLineNumbers(count) {
> return Array.from({ length: count }, (_, i) => i + 1).join(’<br>’);
> }
>
> function setupScrollSync(element1, element2) {
> element1.onscroll = function() {
> element2.scrollTop = element1.scrollTop;
> };
> element2.onscroll = function() {
> element1.scrollTop = element2.scrollTop;
> };
> }
>
> function clearTexts() {
> document.getElementById(‘text1’).value = ‘’;
> document.getElementById(‘text2’).value = ‘’;
> document.getElementById(‘output1’).innerHTML = ‘’;
> document.getElementById(‘output2’).innerHTML = ‘’;
> document.getElementById(‘lineNumbers1’).innerHTML = ‘’;
> document.getElementById(‘lineNumbers2’).innerHTML = ‘’;
> localStorage.removeItem(‘text1’);
> localStorage.removeItem(‘text2’);
> document.getElementById(‘outputSection’).classList.remove(‘visible’);
> }
> </script>
>
</body>
> </html>
>