feat: enhance API documentation and UI with Tailwind CSS integration, add API Key management features
This commit is contained in:
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* 精简版样式表
|
||||
* 注意:此站点现在主要使用 Tailwind CSS。
|
||||
* 这个文件只包含基本的重置样式,以支持可能不使用 Tailwind 的旧页面。
|
||||
*/
|
||||
|
||||
/* 基本样式重置 */
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
@@ -5,319 +11,12 @@
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
background-color: #f5f7fa;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* 容器 */
|
||||
.container {
|
||||
max-width: 1000px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* 页眉 */
|
||||
header {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 10px;
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
header p {
|
||||
font-size: 1.2rem;
|
||||
color: #7f8c8d;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
/* 导航 */
|
||||
nav {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
nav a {
|
||||
text-decoration: none;
|
||||
color: #3498db;
|
||||
margin: 0 15px;
|
||||
padding: 5px 10px;
|
||||
border-radius: 5px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
nav a:hover {
|
||||
background-color: #3498db;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
nav a.active {
|
||||
background-color: #3498db;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* 卡片 */
|
||||
.card {
|
||||
background-color: #fff;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
padding: 25px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
/* 标题 */
|
||||
h2 {
|
||||
color: #2c3e50;
|
||||
margin-bottom: 20px;
|
||||
border-bottom: 1px solid #ecf0f1;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
color: #3498db;
|
||||
margin: 20px 0 10px;
|
||||
}
|
||||
|
||||
/* 输入区域 */
|
||||
.input-group {
|
||||
position: relative;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
resize: none;
|
||||
font-size: 1rem;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
textarea:focus {
|
||||
outline: none;
|
||||
border-color: #3498db;
|
||||
box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.2);
|
||||
}
|
||||
|
||||
.char-counter {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
right: 10px;
|
||||
font-size: 0.8rem;
|
||||
color: #7f8c8d;
|
||||
}
|
||||
|
||||
/* API Key 输入框样式 */
|
||||
#api-key-group {
|
||||
margin-bottom: 15px;
|
||||
background-color: #f8f9fa;
|
||||
padding: 12px 15px;
|
||||
border-radius: 6px;
|
||||
border-left: 4px solid #3498db;
|
||||
}
|
||||
|
||||
.api-key-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.api-key-container label {
|
||||
width: 80px; /* 增加标签宽度 */
|
||||
flex-shrink: 0; /* 防止标签被压缩 */
|
||||
margin-bottom: 0; /* 覆盖默认的底部边距 */
|
||||
}
|
||||
|
||||
#api-key {
|
||||
width: 100%;
|
||||
padding: 8px 10px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
font-family: inherit;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
#api-key:focus {
|
||||
outline: none;
|
||||
border-color: #3498db;
|
||||
box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.2);
|
||||
}
|
||||
|
||||
#api-key::placeholder {
|
||||
color: #aaa;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* 设置区域 */
|
||||
.settings {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.setting-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
label {
|
||||
margin-bottom: 5px;
|
||||
font-weight: bold;
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
select, input[type="range"] {
|
||||
padding: 8px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
select:focus {
|
||||
outline: none;
|
||||
border-color: #3498db;
|
||||
}
|
||||
|
||||
/* 按钮 */
|
||||
.actions {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.primary-button {
|
||||
background-color: #3498db;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.primary-button:hover {
|
||||
background-color: #2980b9;
|
||||
}
|
||||
|
||||
.secondary-button {
|
||||
background-color: #ecf0f1;
|
||||
color: #2c3e50;
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.secondary-button:hover {
|
||||
background-color: #bdc3c7;
|
||||
}
|
||||
|
||||
/* 音频播放器 */
|
||||
.audio-player {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
/* 针对旧浏览器的代码高亮支持 */
|
||||
pre {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
/* 确保音频播放器响应式适应 */
|
||||
audio {
|
||||
width: 100%;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.audio-controls {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* 表格 */
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding: 12px 15px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: #f8f9fa;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* 代码 */
|
||||
code, pre {
|
||||
font-family: SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 3px;
|
||||
padding: 2px 5px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
pre {
|
||||
padding: 15px;
|
||||
overflow-x: auto;
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
pre code {
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/* 页脚 */
|
||||
footer {
|
||||
text-align: center;
|
||||
margin-top: 40px;
|
||||
padding: 20px;
|
||||
color: #7f8c8d;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
footer a {
|
||||
color: #3498db;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
footer a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* 响应式调整 */
|
||||
@media (max-width: 768px) {
|
||||
.settings {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.settings-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
gap: 20px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.half-width {
|
||||
width: 48%;
|
||||
}
|
||||
@@ -9,6 +9,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
const pitchValue = document.getElementById('pitchValue');
|
||||
const apiKeyInput = document.getElementById('api-key');
|
||||
const apiKeyGroup = document.getElementById('api-key-group');
|
||||
const apiKeyButtons = document.getElementById('api-key-buttons');
|
||||
const speakButton = document.getElementById('speak');
|
||||
const downloadButton = document.getElementById('download');
|
||||
const copyLinkButton = document.getElementById('copyLink');
|
||||
@@ -16,33 +17,34 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
const audioPlayer = document.getElementById('audioPlayer');
|
||||
const resultSection = document.getElementById('resultSection');
|
||||
const charCount = document.getElementById('charCount');
|
||||
|
||||
|
||||
// 保存最后一个音频URL
|
||||
let lastAudioUrl = '';
|
||||
// 存储语音数据
|
||||
let voicesData = [];
|
||||
|
||||
|
||||
// 初始化
|
||||
initVoicesList();
|
||||
initEventListeners();
|
||||
|
||||
loadApiKeyFromLocalStorage(); // 加载API Key
|
||||
|
||||
// 更新字符计数
|
||||
textInput.addEventListener('input', function() {
|
||||
charCount.textContent = this.value.length;
|
||||
});
|
||||
|
||||
|
||||
// 更新语速值显示
|
||||
rateInput.addEventListener('input', function() {
|
||||
const value = this.value;
|
||||
rateValue.textContent = value + '%';
|
||||
});
|
||||
|
||||
|
||||
// 更新语调值显示
|
||||
pitchInput.addEventListener('input', function() {
|
||||
const value = this.value;
|
||||
pitchValue.textContent = value + '%';
|
||||
});
|
||||
|
||||
|
||||
// 语音选择变化时更新可用风格
|
||||
voiceSelect.addEventListener('change', function() {
|
||||
updateStyleOptions();
|
||||
@@ -53,40 +55,40 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
try {
|
||||
const response = await fetch(`${config.basePath}/voices`);
|
||||
if (!response.ok) throw new Error('获取语音列表失败');
|
||||
|
||||
|
||||
voicesData = await response.json();
|
||||
|
||||
|
||||
// 清空并重建选项
|
||||
voiceSelect.innerHTML = '';
|
||||
|
||||
|
||||
// 按语言和名称分组
|
||||
const voicesByLocale = {};
|
||||
|
||||
|
||||
voicesData.forEach(voice => {
|
||||
if (!voicesByLocale[voice.locale]) {
|
||||
voicesByLocale[voice.locale] = [];
|
||||
}
|
||||
voicesByLocale[voice.locale].push(voice);
|
||||
});
|
||||
|
||||
|
||||
// 创建选项组
|
||||
for (const locale in voicesByLocale) {
|
||||
const optgroup = document.createElement('optgroup');
|
||||
optgroup.label = voicesByLocale[locale][0].locale_name;
|
||||
|
||||
|
||||
voicesByLocale[locale].forEach(voice => {
|
||||
const option = document.createElement('option');
|
||||
option.value = voice.short_name;
|
||||
option.textContent = `${voice.local_name || voice.display_name} (${voice.gender})`;
|
||||
|
||||
|
||||
// 如果是默认语音则选中
|
||||
if (voice.short_name === config.defaultVoice) {
|
||||
option.selected = true;
|
||||
}
|
||||
|
||||
|
||||
optgroup.appendChild(option);
|
||||
});
|
||||
|
||||
|
||||
voiceSelect.appendChild(optgroup);
|
||||
}
|
||||
|
||||
@@ -97,7 +99,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
voiceSelect.innerHTML = '<option value="">无法加载语音列表</option>';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 更新风格选项
|
||||
function updateStyleOptions() {
|
||||
// 清空风格选择
|
||||
@@ -119,8 +121,8 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
// 添加可用风格选项
|
||||
voiceData.style_list.forEach(style => {
|
||||
const option = document.createElement('option');
|
||||
option.value = style
|
||||
option.textContent = style
|
||||
option.value = style;
|
||||
option.textContent = style;
|
||||
|
||||
// 如果是默认风格则选中
|
||||
if (style === config.defaultStyle ||
|
||||
@@ -136,7 +138,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
function initEventListeners() {
|
||||
// 转换按钮点击事件
|
||||
speakButton.addEventListener('click', generateSpeech);
|
||||
|
||||
|
||||
// 下载按钮点击事件
|
||||
downloadButton.addEventListener('click', function() {
|
||||
if (lastAudioUrl) {
|
||||
@@ -148,7 +150,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
document.body.removeChild(a);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// 复制链接按钮点击事件
|
||||
copyLinkButton.addEventListener('click', function() {
|
||||
if (lastAudioUrl) {
|
||||
@@ -173,7 +175,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
const apiKey = apiKeyInput.value.trim();
|
||||
|
||||
// 构建HttpTTS链接
|
||||
let httpTtsLink = `${window.location.origin}${config.basePath}/tts?t={{java.encodeURI(speakText)}}&v=${voice}&r={{speakSpeed*4}}&p=${pitch}&s=${style}`;
|
||||
let httpTtsLink = `${window.location.origin}${config.basePath}/tts?t=${encodeURIComponent(text)}&v=${voice}&r=${rate}&p=${pitch}&s=${style}`;
|
||||
|
||||
// 添加API Key参数(如果有)
|
||||
if (apiKey) {
|
||||
@@ -182,8 +184,93 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
copyToClipboard(httpTtsLink);
|
||||
});
|
||||
|
||||
// 保存API Key按钮点击事件
|
||||
const saveApiKeyButton = document.createElement('button');
|
||||
saveApiKeyButton.textContent = '保存';
|
||||
saveApiKeyButton.className = 'flex-1 h-10 px-4 bg-gray-100 text-gray-700 border border-gray-300 rounded-md hover:bg-gray-200 transition-colors focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-opacity-50 flex items-center justify-center';
|
||||
saveApiKeyButton.innerHTML = `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-1 text-gray-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-3m-1 4l-3 3m0 0l-3-3m3 3V4" />
|
||||
</svg>
|
||||
保存
|
||||
`;
|
||||
saveApiKeyButton.addEventListener('click', function() {
|
||||
saveApiKeyToLocalStorage(apiKeyInput.value);
|
||||
|
||||
// 显示保存成功提示
|
||||
const successNotice = document.createElement('div');
|
||||
successNotice.className = 'fixed top-4 right-4 bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded shadow-md flex items-center z-50';
|
||||
successNotice.innerHTML = `
|
||||
<svg class="h-5 w-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
|
||||
</svg>
|
||||
API Key 已保存
|
||||
`;
|
||||
document.body.appendChild(successNotice);
|
||||
|
||||
// 2秒后自动移除提示
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(successNotice);
|
||||
}, 2000);
|
||||
});
|
||||
apiKeyButtons.appendChild(saveApiKeyButton);
|
||||
|
||||
// 清除API Key按钮点击事件
|
||||
const clearApiKeyButton = document.createElement('button');
|
||||
clearApiKeyButton.textContent = '清除';
|
||||
clearApiKeyButton.className = 'flex-1 h-10 px-4 bg-gray-100 text-gray-700 border border-gray-300 rounded-md hover:bg-gray-200 transition-colors focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-opacity-50 flex items-center justify-center';
|
||||
clearApiKeyButton.innerHTML = `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-1 text-gray-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
|
||||
</svg>
|
||||
清除
|
||||
`;
|
||||
clearApiKeyButton.addEventListener('click', function() {
|
||||
localStorage.removeItem('apiKey');
|
||||
apiKeyInput.value = '';
|
||||
|
||||
// 显示清除成功提示
|
||||
const successNotice = document.createElement('div');
|
||||
successNotice.className = 'fixed top-4 right-4 bg-blue-100 border border-blue-400 text-blue-700 px-4 py-3 rounded shadow-md flex items-center z-50';
|
||||
successNotice.innerHTML = `
|
||||
<svg class="h-5 w-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
|
||||
</svg>
|
||||
API Key 已清除
|
||||
`;
|
||||
document.body.appendChild(successNotice);
|
||||
|
||||
// 2秒后自动移除提示
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(successNotice);
|
||||
}, 2000);
|
||||
});
|
||||
apiKeyButtons.appendChild(clearApiKeyButton);
|
||||
}
|
||||
|
||||
|
||||
// 增加密码显示/隐藏功能
|
||||
const togglePasswordButton = document.getElementById('toggle-password');
|
||||
if (togglePasswordButton) {
|
||||
togglePasswordButton.addEventListener('click', function() {
|
||||
const apiKeyInput = document.getElementById('api-key');
|
||||
const type = apiKeyInput.getAttribute('type') === 'password' ? 'text' : 'password';
|
||||
apiKeyInput.setAttribute('type', type);
|
||||
|
||||
// 更新图标
|
||||
if (type === 'password') {
|
||||
this.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
|
||||
</svg>`;
|
||||
} else {
|
||||
this.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l18 18" />
|
||||
</svg>`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 复制内容到剪贴板的通用函数
|
||||
function copyToClipboard(text) {
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
@@ -215,17 +302,17 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
alert('请输入要转换的文本');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const voice = voiceSelect.value;
|
||||
const style = styleSelect.value;
|
||||
const rate = rateInput.value;
|
||||
const pitch = pitchInput.value;
|
||||
const apiKey = apiKeyInput.value.trim();
|
||||
|
||||
|
||||
// 禁用按钮,显示加载状态
|
||||
speakButton.disabled = true;
|
||||
speakButton.textContent = '生成中...';
|
||||
|
||||
|
||||
try {
|
||||
// 构建URL参数
|
||||
const params = new URLSearchParams({
|
||||
@@ -235,14 +322,14 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
r: rate,
|
||||
p: pitch
|
||||
});
|
||||
|
||||
|
||||
// 添加API Key参数(如果有)
|
||||
if (apiKey) {
|
||||
params.append('api_key', apiKey);
|
||||
}
|
||||
|
||||
const url = `${config.basePath}/tts?${params.toString()}`;
|
||||
|
||||
|
||||
// 使用fetch发送请求以便捕获HTTP状态码
|
||||
const response = await fetch(url);
|
||||
|
||||
@@ -264,10 +351,10 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
// 更新音频播放器
|
||||
audioPlayer.src = audioUrl;
|
||||
lastAudioUrl = url; // 保存原始URL用于下载和复制链接
|
||||
|
||||
|
||||
// 显示结果区域
|
||||
resultSection.style.display = 'block';
|
||||
|
||||
resultSection.classList.remove('hidden');
|
||||
|
||||
// 播放音频
|
||||
audioPlayer.play();
|
||||
} catch (error) {
|
||||
@@ -281,4 +368,21 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
speakButton.textContent = '转换为语音';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 保存API Key到localStorage
|
||||
function saveApiKeyToLocalStorage(apiKey) {
|
||||
if (apiKey) {
|
||||
localStorage.setItem('apiKey', apiKey);
|
||||
} else {
|
||||
localStorage.removeItem('apiKey');
|
||||
}
|
||||
}
|
||||
|
||||
// 从localStorage加载API Key
|
||||
function loadApiKeyFromLocalStorage() {
|
||||
const apiKey = localStorage.getItem('apiKey');
|
||||
if (apiKey) {
|
||||
apiKeyInput.value = apiKey;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user