Create worker.js
This commit is contained in:
parent
df609cce2f
commit
62054add3b
772
worker.js
Normal file
772
worker.js
Normal file
@ -0,0 +1,772 @@
|
||||
addEventListener('fetch', event => {
|
||||
event.respondWith(handleRequest(event.request));
|
||||
});
|
||||
|
||||
async function handleRequest(request) {
|
||||
const url = new URL(request.url);
|
||||
|
||||
// 处理 CORS 预检请求
|
||||
if (request.method === 'OPTIONS') {
|
||||
return new Response(null, {
|
||||
headers: {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Methods': 'POST, OPTIONS',
|
||||
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// 处理 POST 请求
|
||||
if (request.method === 'POST' && url.pathname === '/recognize') {
|
||||
try {
|
||||
const { token, imageId } = await request.json();
|
||||
|
||||
if (!token || !imageId) {
|
||||
return new Response(JSON.stringify({ error: 'Missing token or imageId' }), {
|
||||
status: 400,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
}
|
||||
|
||||
// 调用 QwenLM API
|
||||
const response = await fetch('https://chat.qwenlm.ai/api/chat/completions', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'accept': '*/*',
|
||||
'authorization': `Bearer ${token}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
stream: false,
|
||||
model: 'qwen-vl-max-latest',
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: [
|
||||
{ type: 'text', text: '请严格只返回图片中的内容,不要添加任何解释、描述或多余的文字' },
|
||||
{ type: 'image', image: imageId }, // 使用上传后的图片 ID
|
||||
],
|
||||
},
|
||||
],
|
||||
session_id: '1',
|
||||
chat_id: '2',
|
||||
id: '3',
|
||||
}),
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
return new Response(JSON.stringify(data), {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
return new Response(JSON.stringify({ error: 'Internal Server Error' }), {
|
||||
status: 500,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 返回前端界面
|
||||
return new Response(getHTML(), {
|
||||
headers: { 'Content-Type': 'text/html' },
|
||||
});
|
||||
}
|
||||
|
||||
function getHTML() {
|
||||
return `
|
||||
<!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>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
|
||||
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.container {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
padding: 2.5rem;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
|
||||
width: 90%;
|
||||
max-width: 800px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #2c3e50;
|
||||
margin-bottom: 1.5rem;
|
||||
font-size: 1.8rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.upload-area {
|
||||
border: 2px dashed #8e9eab;
|
||||
border-radius: 12px;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
transition: all 0.3s ease;
|
||||
margin-bottom: 1.5rem;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.upload-area:hover {
|
||||
border-color: #3498db;
|
||||
background: rgba(52, 152, 219, 0.05);
|
||||
}
|
||||
|
||||
.upload-area.dragover {
|
||||
border-color: #3498db;
|
||||
background: rgba(52, 152, 219, 0.1);
|
||||
transform: scale(1.02);
|
||||
}
|
||||
|
||||
.upload-area i {
|
||||
font-size: 2rem;
|
||||
color: #8e9eab;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.upload-text {
|
||||
color: #7f8c8d;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
#tokens {
|
||||
width: 100%;
|
||||
padding: 0.8rem;
|
||||
border: 1px solid #dcdde1;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 1rem;
|
||||
font-size: 0.9rem;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.result-container {
|
||||
margin-top: 1.5rem;
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.result-container.show {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.result {
|
||||
background: #f8f9fa;
|
||||
padding: 1.2rem;
|
||||
border-radius: 8px;
|
||||
color: #2c3e50;
|
||||
font-size: 1rem;
|
||||
line-height: 1.6;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.loading {
|
||||
display: none;
|
||||
text-align: center;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.loading::after {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 2px solid #3498db;
|
||||
border-radius: 50%;
|
||||
border-top-color: transparent;
|
||||
animation: spin 0.8s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.preview-image {
|
||||
max-width: 100%;
|
||||
max-height: 200px;
|
||||
margin: 1rem 0;
|
||||
border-radius: 8px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 侧边栏样式 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
right: -300px;
|
||||
top: 0;
|
||||
width: 300px;
|
||||
height: 100vh;
|
||||
background: white;
|
||||
box-shadow: -5px 0 15px rgba(0, 0, 0, 0.1);
|
||||
transition: right 0.3s ease;
|
||||
padding: 20px;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.sidebar.open {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.sidebar-toggle {
|
||||
position: fixed;
|
||||
right: 20px;
|
||||
top: 20px;
|
||||
background: #3498db;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 15px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
z-index: 1001;
|
||||
}
|
||||
|
||||
.token-list {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.token-item {
|
||||
background: #f8f9fa;
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.token-item:hover {
|
||||
background: #e9ecef;
|
||||
}
|
||||
|
||||
#tokenInput {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
border: 1px solid #dcdde1;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.save-btn {
|
||||
background: #3498db;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 15px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 历史记录样式 */
|
||||
.history-container {
|
||||
margin-top: 2rem;
|
||||
border-top: 1px solid #eee;
|
||||
padding-top: 1rem;
|
||||
}
|
||||
|
||||
.history-title {
|
||||
color: #2c3e50;
|
||||
font-size: 1.2rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.history-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
padding: 1rem;
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.history-image {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
object-fit: cover;
|
||||
border-radius: 4px;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.history-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.history-text {
|
||||
color: #2c3e50;
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.history-time {
|
||||
color: #7f8c8d;
|
||||
font-size: 0.8rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.no-history {
|
||||
text-align: center;
|
||||
color: #7f8c8d;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.9);
|
||||
z-index: 2000;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
max-width: 90%;
|
||||
max-height: 90vh;
|
||||
margin: auto;
|
||||
display: block;
|
||||
position: relative;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
/* 修改侧边栏样式 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
right: -300px; /* 改为右侧边栏 */
|
||||
top: 0;
|
||||
width: 300px;
|
||||
height: 100vh;
|
||||
background: white;
|
||||
box-shadow: -5px 0 15px rgba(0, 0, 0, 0.1);
|
||||
transition: right 0.3s ease;
|
||||
padding: 20px;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
/* 添加左侧边栏样式 */
|
||||
.history-sidebar {
|
||||
position: fixed;
|
||||
left: -300px;
|
||||
top: 0;
|
||||
width: 300px;
|
||||
height: 100vh;
|
||||
background: white;
|
||||
box-shadow: 5px 0 15px rgba(0, 0, 0, 0.1);
|
||||
transition: left 0.3s ease;
|
||||
padding: 20px;
|
||||
z-index: 1000;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.history-sidebar.open {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.history-toggle {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
top: 20px;
|
||||
background: #3498db;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 15px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
z-index: 1001;
|
||||
}
|
||||
|
||||
/* 添加复制按钮样式 */
|
||||
.result-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.copy-btn {
|
||||
background: #3498db;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 5px 10px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 0.9rem;
|
||||
transition: background 0.3s ease;
|
||||
}
|
||||
|
||||
.copy-btn:hover {
|
||||
background: #2980b9;
|
||||
}
|
||||
|
||||
.copy-btn.copied {
|
||||
background: #27ae60;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<button class="sidebar-toggle" id="sidebarToggle">⚙️ Token设置</button>
|
||||
<div class="sidebar" id="sidebar">
|
||||
<h2>Token 管理</h2>
|
||||
<textarea id="tokenInput" placeholder="输入Token,多个Token请用英文逗号分隔" rows="4"></textarea>
|
||||
<button class="save-btn" id="saveTokens">保存</button>
|
||||
<div class="token-list" id="tokenList"></div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<h1>智能图片识别</h1>
|
||||
<div class="upload-area" id="uploadArea">
|
||||
<i>📸</i>
|
||||
<div class="upload-text">
|
||||
拖拽图片到这里,或点击上传<br>
|
||||
支持复制粘贴图片
|
||||
</div>
|
||||
<img id="previewImage" class="preview-image">
|
||||
</div>
|
||||
<div class="loading" id="loading"></div>
|
||||
<div class="result-container" id="resultContainer">
|
||||
<div class="result-header">
|
||||
<span>识别结果</span>
|
||||
<button class="copy-btn" id="copyBtn">复制结果</button>
|
||||
</div>
|
||||
<div class="result" id="result"></div>
|
||||
</div>
|
||||
<button class="history-toggle" id="historyToggle">📋 识别历史</button>
|
||||
<div class="history-sidebar" id="historySidebar">
|
||||
<h2>识别历史</h2>
|
||||
<div id="historyList"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="imageModal" class="modal">
|
||||
<img class="modal-content" id="modalImage">
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 首先定义类
|
||||
function HistoryManager() {
|
||||
this.maxHistory = 10;
|
||||
}
|
||||
|
||||
// 添加原型方法
|
||||
HistoryManager.prototype.getHistoryKey = function(token) {
|
||||
return 'imageRecognition_history_' + token;
|
||||
};
|
||||
|
||||
HistoryManager.prototype.loadHistory = function(token) {
|
||||
const history = localStorage.getItem(this.getHistoryKey(token));
|
||||
return history ? JSON.parse(history) : [];
|
||||
};
|
||||
|
||||
HistoryManager.prototype.saveHistory = function(token, history) {
|
||||
localStorage.setItem(this.getHistoryKey(token), JSON.stringify(history));
|
||||
};
|
||||
|
||||
HistoryManager.prototype.addHistory = function(token, imageData, result) {
|
||||
const history = this.loadHistory(token);
|
||||
const newRecord = {
|
||||
image: imageData,
|
||||
result: result,
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
|
||||
history.unshift(newRecord);
|
||||
if (history.length > this.maxHistory) {
|
||||
history.pop();
|
||||
}
|
||||
|
||||
this.saveHistory(token, history);
|
||||
this.displayHistory(token);
|
||||
};
|
||||
|
||||
HistoryManager.prototype.displayHistory = function(token) {
|
||||
const history = this.loadHistory(token);
|
||||
|
||||
if (history.length === 0) {
|
||||
historyList.innerHTML = '<div class="no-history">暂无识别历史</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
var html = '';
|
||||
for (var i = 0; i < history.length; i++) {
|
||||
var record = history[i];
|
||||
html += '<div class="history-item">';
|
||||
html += '<img src="' + record.image + '" class="history-image" alt="历史图片" onclick="showFullImage(this.src)">';
|
||||
html += '<div class="history-content">';
|
||||
html += '<div class="history-text">' + record.result + '</div>';
|
||||
html += '<div class="history-time">' + new Date(record.timestamp).toLocaleString() + '</div>';
|
||||
html += '</div></div>';
|
||||
}
|
||||
historyList.innerHTML = html;
|
||||
};
|
||||
|
||||
// 在 HistoryManager 类定义后添加 TokenManager 类
|
||||
function TokenManager() {
|
||||
this.currentIndex = 0;
|
||||
}
|
||||
|
||||
TokenManager.prototype.getNextToken = function(tokens) {
|
||||
if (!tokens || tokens.length === 0) return null;
|
||||
|
||||
// 轮询获取下一个token
|
||||
const token = tokens[this.currentIndex];
|
||||
this.currentIndex = (this.currentIndex + 1) % tokens.length;
|
||||
return token;
|
||||
};
|
||||
|
||||
// 初始化变量
|
||||
const uploadArea = document.getElementById('uploadArea');
|
||||
const tokensInput = document.getElementById('tokenInput');
|
||||
const resultDiv = document.getElementById('result');
|
||||
const resultContainer = document.getElementById('resultContainer');
|
||||
const loading = document.getElementById('loading');
|
||||
const previewImage = document.getElementById('previewImage');
|
||||
const historyList = document.getElementById('historyList');
|
||||
const sidebar = document.getElementById('sidebar');
|
||||
const sidebarToggle = document.getElementById('sidebarToggle');
|
||||
const tokenInput = document.getElementById('tokenInput');
|
||||
const saveTokensBtn = document.getElementById('saveTokens');
|
||||
const tokenList = document.getElementById('tokenList');
|
||||
const historySidebar = document.getElementById('historySidebar');
|
||||
const historyToggle = document.getElementById('historyToggle');
|
||||
|
||||
let currentToken = '';
|
||||
let tokens = [];
|
||||
const historyManager = new HistoryManager();
|
||||
const tokenManager = new TokenManager();
|
||||
|
||||
// 从localStorage加载保存的tokens
|
||||
function loadTokens() {
|
||||
const savedTokens = localStorage.getItem('imageRecognitionTokens');
|
||||
if (savedTokens) {
|
||||
tokens = savedTokens.split(',');
|
||||
updateTokenList();
|
||||
if (tokens.length > 0) {
|
||||
currentToken = tokens[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 修改 updateTokenList 函数
|
||||
function updateTokenList() {
|
||||
tokenList.innerHTML = '';
|
||||
tokens.forEach(function(token, index) {
|
||||
var truncatedToken = token.slice(0, 10) + '...' + token.slice(-10);
|
||||
var div = document.createElement('div');
|
||||
div.className = 'token-item';
|
||||
div.textContent = 'Token ' + (index + 1) + ': ' + truncatedToken;
|
||||
div.addEventListener('click', function() {
|
||||
currentToken = token;
|
||||
historyManager.displayHistory(currentToken);
|
||||
});
|
||||
tokenList.appendChild(div);
|
||||
});
|
||||
tokenInput.value = tokens.join(',');
|
||||
}
|
||||
|
||||
// 保存tokens
|
||||
saveTokensBtn.addEventListener('click', () => {
|
||||
const inputTokens = tokenInput.value.split(',').map(t => t.trim()).filter(t => t);
|
||||
if (inputTokens.length > 0) {
|
||||
tokens = inputTokens;
|
||||
localStorage.setItem('imageRecognitionTokens', tokens.join(','));
|
||||
updateTokenList();
|
||||
currentToken = tokens[0];
|
||||
alert('Tokens已保存');
|
||||
} else {
|
||||
alert('请至少输入一个有效的Token');
|
||||
}
|
||||
});
|
||||
|
||||
// 侧边栏开关
|
||||
sidebarToggle.addEventListener('click', () => {
|
||||
sidebar.classList.toggle('open');
|
||||
});
|
||||
|
||||
// 处理文件上传和识别
|
||||
async function processImage(file) {
|
||||
// 使用 TokenManager 获取下一个可用的 token
|
||||
const nextToken = tokenManager.getNextToken(tokens);
|
||||
if (!nextToken) {
|
||||
alert('请先设置Token');
|
||||
sidebar.classList.add('open');
|
||||
return;
|
||||
}
|
||||
currentToken = nextToken;
|
||||
|
||||
// 显示图片预览
|
||||
const reader = new FileReader();
|
||||
let imageData;
|
||||
reader.onload = (e) => {
|
||||
imageData = e.target.result;
|
||||
previewImage.src = imageData;
|
||||
previewImage.style.display = 'block';
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
|
||||
// 显示加载动画
|
||||
loading.style.display = 'block';
|
||||
resultContainer.classList.remove('show');
|
||||
|
||||
try {
|
||||
// 上传文件
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
|
||||
const uploadResponse = await fetch('https://chat.qwenlm.ai/api/v1/files/', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'accept': 'application/json',
|
||||
'authorization': 'Bearer ' + currentToken,
|
||||
},
|
||||
body: formData,
|
||||
});
|
||||
|
||||
const uploadData = await uploadResponse.json();
|
||||
if (!uploadData.id) throw new Error('文件上传失败');
|
||||
|
||||
// 识别图片
|
||||
const recognizeResponse = await fetch('/recognize', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
token: currentToken,
|
||||
imageId: uploadData.id
|
||||
}),
|
||||
});
|
||||
|
||||
const recognizeData = await recognizeResponse.json();
|
||||
|
||||
// 提取并显示识别结果
|
||||
const result = recognizeData.choices[0]?.message?.content || '识别失败';
|
||||
resultDiv.textContent = result;
|
||||
resultContainer.classList.add('show');
|
||||
copyBtn.textContent = '复制结果';
|
||||
copyBtn.classList.remove('copied');
|
||||
|
||||
// 添加到历史记录
|
||||
historyManager.addHistory(currentToken, imageData, result);
|
||||
} catch (error) {
|
||||
resultDiv.textContent = '处理失败: ' + error.message;
|
||||
resultContainer.classList.add('show');
|
||||
copyBtn.textContent = '复制结果';
|
||||
copyBtn.classList.remove('copied');
|
||||
} finally {
|
||||
loading.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// 文件拖放处理
|
||||
uploadArea.addEventListener('dragover', (e) => {
|
||||
e.preventDefault();
|
||||
uploadArea.classList.add('dragover');
|
||||
});
|
||||
|
||||
uploadArea.addEventListener('dragleave', () => {
|
||||
uploadArea.classList.remove('dragover');
|
||||
});
|
||||
|
||||
uploadArea.addEventListener('drop', (e) => {
|
||||
e.preventDefault();
|
||||
uploadArea.classList.remove('dragover');
|
||||
const file = e.dataTransfer.files[0];
|
||||
if (file && file.type.startsWith('image/')) {
|
||||
processImage(file);
|
||||
}
|
||||
});
|
||||
|
||||
// 点击上传
|
||||
uploadArea.addEventListener('click', () => {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.accept = 'image/*';
|
||||
input.onchange = (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (file) processImage(file);
|
||||
};
|
||||
input.click();
|
||||
});
|
||||
|
||||
// 粘贴处理
|
||||
document.addEventListener('paste', (e) => {
|
||||
const file = e.clipboardData.files[0];
|
||||
if (file && file.type.startsWith('image/')) {
|
||||
processImage(file);
|
||||
}
|
||||
});
|
||||
|
||||
// 初始化
|
||||
loadTokens();
|
||||
if (currentToken) {
|
||||
historyManager.displayHistory(currentToken);
|
||||
}
|
||||
|
||||
const modal = document.getElementById('imageModal');
|
||||
const modalImg = document.getElementById('modalImage');
|
||||
|
||||
function showFullImage(src) {
|
||||
modal.style.display = "block";
|
||||
modalImg.src = src;
|
||||
}
|
||||
|
||||
// 点击模态框关闭
|
||||
modal.onclick = function() {
|
||||
modal.style.display = "none";
|
||||
}
|
||||
|
||||
// ESC 键关闭模态框
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape' && modal.style.display === 'block') {
|
||||
modal.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
// 左侧历史记录边栏开关
|
||||
historyToggle.addEventListener('click', () => {
|
||||
historySidebar.classList.toggle('open');
|
||||
});
|
||||
|
||||
const copyBtn = document.getElementById('copyBtn');
|
||||
|
||||
// 复制结果功能
|
||||
copyBtn.addEventListener('click', async () => {
|
||||
const result = resultDiv.textContent;
|
||||
try {
|
||||
await navigator.clipboard.writeText(result);
|
||||
copyBtn.textContent = '已复制';
|
||||
copyBtn.classList.add('copied');
|
||||
setTimeout(() => {
|
||||
copyBtn.textContent = '复制结果';
|
||||
copyBtn.classList.remove('copied');
|
||||
}, 2000);
|
||||
} catch (err) {
|
||||
console.error('复制失败:', err);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user