chat-helper / static /index.html
ruoxi01's picture
Upload index.html
a562977 verified
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Chat Helper</title>
<style>
:root {
--primary-color: #007AFF;
--bg-color: #F2F2F7;
--card-bg: #FFFFFF;
--text-color: #000000;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
background-color: var(--bg-color);
margin: 0;
padding: 20px;
color: var(--text-color);
padding-bottom: 80px;
}
.container {
max-width: 600px;
margin: 0 auto;
}
h1 {
text-align: center;
font-size: 20px;
margin-bottom: 20px;
color: #333;
}
.card {
background: var(--card-bg);
border-radius: 12px;
padding: 16px;
margin-bottom: 16px;
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
}
.form-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 8px;
font-weight: 500;
font-size: 14px;
color: #666;
}
input[type="text"], textarea, select {
width: 100%;
padding: 12px;
border: 1px solid #E5E5EA;
border-radius: 10px;
font-size: 16px;
box-sizing: border-box;
background: #F9F9F9;
-webkit-appearance: none;
}
textarea {
resize: vertical;
min-height: 100px;
}
.role-selector {
display: flex;
gap: 10px;
overflow-x: auto;
padding-bottom: 5px;
-webkit-overflow-scrolling: touch;
}
.role-btn {
flex: 0 0 auto;
padding: 8px 16px;
border-radius: 20px;
background: #E5E5EA;
color: #333;
border: none;
font-size: 14px;
cursor: pointer;
transition: all 0.2s;
}
.role-btn.active {
background: var(--primary-color);
color: white;
}
.btn-primary {
background: var(--primary-color);
color: white;
border: none;
padding: 14px;
border-radius: 12px;
width: 100%;
font-size: 16px;
font-weight: 600;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
}
.btn-primary:active {
opacity: 0.8;
}
.btn-primary:disabled {
background: #ccc;
}
.file-upload {
position: relative;
display: inline-block;
width: 100%;
}
.file-upload input[type="file"] {
display: none;
}
.file-upload-label {
display: flex;
align-items: center;
justify-content: center;
padding: 12px;
border: 2px dashed #E5E5EA;
border-radius: 10px;
color: var(--primary-color);
cursor: pointer;
background: #fff;
}
#preview-image {
max-width: 100%;
border-radius: 8px;
margin-top: 10px;
display: none;
}
.result-area {
display: none;
}
.reply-card {
background: #fff;
padding: 15px;
border-radius: 10px;
margin-bottom: 10px;
border-left: 4px solid var(--primary-color);
cursor: pointer;
position: relative;
}
.reply-card:active {
background: #f0f0f0;
}
.reply-card::after {
content: '点击复制';
position: absolute;
right: 10px;
top: 10px;
font-size: 10px;
color: #999;
}
.loading {
display: none;
text-align: center;
margin: 20px 0;
}
.spinner {
border: 4px solid rgba(0, 0, 0, 0.1);
width: 36px;
height: 36px;
border-radius: 50%;
border-left-color: var(--primary-color);
animation: spin 1s linear infinite;
display: inline-block;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* Float Button (Simulation of AssistiveTouch) */
.float-btn {
position: fixed;
bottom: 20px;
right: 20px;
width: 50px;
height: 50px;
border-radius: 25px;
background: var(--primary-color);
color: white;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
z-index: 1000;
cursor: pointer;
font-size: 24px;
}
.api-key-section {
font-size: 12px;
color: #888;
margin-top: 10px;
text-align: center;
cursor: pointer;
text-decoration: underline;
}
#apiKeyInput {
margin-top: 5px;
display: none;
}
</style>
</head>
<body>
<div class="container">
<h1>💬 聊天回复助手</h1>
<div class="card">
<label>对方是谁?</label>
<div class="role-selector" id="roleSelector">
<button class="role-btn active" data-role="boss">领导</button>
<button class="role-btn" data-role="client">客户</button>
<button class="role-btn" data-role="colleague">同事</button>
<button class="role-btn" data-role="good_friend">好朋友</button>
<button class="role-btn" data-role="normal_friend">普通朋友</button>
</div>
</div>
<div class="card">
<div class="form-group">
<label>聊天内容 (截图或文字)</label>
<div class="file-upload">
<label for="imageInput" class="file-upload-label">
📷 上传聊天截图
</label>
<input type="file" id="imageInput" accept="image/*">
</div>
<img id="preview-image" alt="Preview">
</div>
<div class="form-group">
<textarea id="textInput" placeholder="或者直接粘贴对方说的话..."></textarea>
</div>
<button class="btn-primary" id="generateBtn">生成回复</button>
<!--
<div class="api-key-section" id="toggleApiKey">设置 API Key</div>
<input type="text" id="apiKeyInput" placeholder="sk-..." value="">
-->
</div>
<div class="loading" id="loading">
<div class="spinner"></div>
<p>正在思考高情商回复...</p>
</div>
<div class="result-area" id="resultArea">
<h3>💡 建议回复 (点击复制)</h3>
<div id="replyContainer"></div>
</div>
</div>
<script>
// State
let currentRole = 'boss';
// DOM Elements
const roleBtns = document.querySelectorAll('.role-btn');
const imageInput = document.getElementById('imageInput');
const previewImage = document.getElementById('preview-image');
const textInput = document.getElementById('textInput');
const generateBtn = document.getElementById('generateBtn');
const loading = document.getElementById('loading');
const resultArea = document.getElementById('resultArea');
const replyContainer = document.getElementById('replyContainer');
// const apiKeyInput = document.getElementById('apiKeyInput');
// const toggleApiKey = document.getElementById('toggleApiKey');
// Load API Key
/*
const savedKey = localStorage.getItem('siliconflow_api_key');
if (savedKey) {
apiKeyInput.value = savedKey;
} else {
apiKeyInput.style.display = 'block'; // Show if empty
}
*/
// Role Selection
roleBtns.forEach(btn => {
btn.addEventListener('click', () => {
roleBtns.forEach(b => b.classList.remove('active'));
btn.classList.add('active');
currentRole = btn.dataset.role;
});
});
// Toggle API Key Input
/*
toggleApiKey.addEventListener('click', () => {
apiKeyInput.style.display = apiKeyInput.style.display === 'none' ? 'block' : 'none';
});
// Save API Key on change
apiKeyInput.addEventListener('change', () => {
localStorage.setItem('siliconflow_api_key', apiKeyInput.value.trim());
});
*/
// Image Preview
imageInput.addEventListener('change', function(e) {
const file = e.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = function(e) {
previewImage.src = e.target.result;
previewImage.style.display = 'block';
}
reader.readAsDataURL(file);
}
});
// Generate
generateBtn.addEventListener('click', async () => {
/*
const apiKey = apiKeyInput.value.trim();
if (!apiKey) {
alert('请先输入 SiliconFlow API Key');
apiKeyInput.style.display = 'block';
return;
}
*/
const text = textInput.value.trim();
const image = imageInput.files[0];
if (!text && !image) {
alert('请提供聊天内容(截图或文字)');
return;
}
// UI Update
loading.style.display = 'block';
resultArea.style.display = 'none';
generateBtn.disabled = true;
replyContainer.innerHTML = '';
// Prepare Data
const formData = new FormData();
formData.append('role', currentRole);
// formData.append('api_key', apiKey); // No longer needed
if (text) formData.append('text', text);
if (image) formData.append('image', image);
try {
const response = await fetch('/api/chat', {
method: 'POST',
body: formData
});
const data = await response.json();
if (data.error) {
throw new Error(data.error);
}
// Parse and display replies
// The AI now returns a list of replies in "replies" field
const replies = data.replies || [data.reply]; // Backwards compatibility
if (Array.isArray(replies) && replies.length > 0) {
replies.forEach(r => addReplyCard(r.trim()));
} else {
addReplyCard("未生成有效回复,请重试。");
}
resultArea.style.display = 'block';
} catch (err) {
alert('Error: ' + err.message);
} finally {
loading.style.display = 'none';
generateBtn.disabled = false;
}
});
function addReplyCard(text) {
const div = document.createElement('div');
div.className = 'reply-card';
div.textContent = text;
div.onclick = () => {
navigator.clipboard.writeText(text);
const original = div.textContent;
div.textContent = '已复制!';
setTimeout(() => div.textContent = original, 1000);
};
replyContainer.appendChild(div);
}
</script>
</body>
</html>