feat: implement API Key visibility toggle and localStorage saving functionality
This commit is contained in:
@@ -192,9 +192,30 @@ async function handleRequest(request) {
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="apiKey" class="block text-sm font-medium text-gray-700">API Key</label>
|
<label for="apiKey" class="block text-sm font-medium text-gray-700">API Key</label>
|
||||||
<input type="text" id="apiKey" name="apiKey" required
|
<div class="mt-1 flex rounded-md shadow-sm">
|
||||||
class="mt-1 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md focus:ring-ms-blue focus:border-ms-blue"
|
<div id="apiKeyInputGroup" class="flex-grow flex relative">
|
||||||
placeholder="输入API Key" />
|
<input type="password" id="apiKey" name="apiKey" required
|
||||||
|
class="block w-full shadow-sm sm:text-sm border-gray-300 rounded-md focus:ring-ms-blue focus:border-ms-blue"
|
||||||
|
placeholder="输入API Key" />
|
||||||
|
<button type="button" id="toggleApiKeyVisibility" class="absolute inset-y-0 right-0 px-3 flex items-center">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-gray-500" 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>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<button type="button" id="saveApiKey" class="ml-2 inline-flex items-center px-3 py-2 border border-gray-300 shadow-sm text-sm leading-4 font-medium rounded-md text-white bg-ms-blue hover:bg-ms-dark-blue focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-ms-blue">
|
||||||
|
保存
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div id="savedApiKeyInfo" style="display:none;" class="mt-2 flex items-center justify-between">
|
||||||
|
<span class="text-sm text-green-600 flex items-center">
|
||||||
|
API Key 已保存
|
||||||
|
</span>
|
||||||
|
<button type="button" id="editApiKey" class="text-sm text-ms-blue hover:text-ms-dark-blue">
|
||||||
|
编辑
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@@ -290,7 +311,7 @@ async function handleRequest(request) {
|
|||||||
<div class="flex">
|
<div class="flex">
|
||||||
<div class="flex-shrink-0">
|
<div class="flex-shrink-0">
|
||||||
<svg class="h-5 w-5 text-red-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
<svg class="h-5 w-5 text-red-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
||||||
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 000 16zM8.707 7.293a1 1 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 101.414 1.414L10 11.414l1.293-1.293a1 1 001.414-1.414L11.414 10l1.293-1.293a1 1 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd" />
|
<path fill-rule="evenodd" d="M10 18a8 8 100-16 8 8 000 16zM8.707 7.293a1 1 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 101.414 1.414L10 11.414l1.293-1.293a1 1 001.414-1.414L11.414 10l1.293-1.293a1 1 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div class="ml-3">
|
<div class="ml-3">
|
||||||
@@ -464,7 +485,8 @@ curl ${baseUrl}/v1/audio/speech \\
|
|||||||
// 隐藏先前的错误信息
|
// 隐藏先前的错误信息
|
||||||
document.getElementById('apiErrorAlert').style.display = 'none';
|
document.getElementById('apiErrorAlert').style.display = 'none';
|
||||||
|
|
||||||
const apiKey = document.getElementById('apiKey').value;
|
// 获取API Key (从输入框或localStorage)
|
||||||
|
const apiKey = document.getElementById('apiKey').value || localStorage.getItem('tts_api_key') || '';
|
||||||
const text = encodeURIComponent(document.getElementById('text').value);
|
const text = encodeURIComponent(document.getElementById('text').value);
|
||||||
const voice = document.getElementById('voice').value;
|
const voice = document.getElementById('voice').value;
|
||||||
const rate = document.getElementById('rate').value;
|
const rate = document.getElementById('rate').value;
|
||||||
@@ -717,7 +739,64 @@ curl ${baseUrl}/v1/audio/speech \\
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 页面加载完成后加载语音列表
|
// 页面加载完成后加载语音列表
|
||||||
window.onload = loadVoices;
|
window.onload = function() {
|
||||||
|
loadVoices();
|
||||||
|
|
||||||
|
// API Key 相关功能
|
||||||
|
const apiKeyInput = document.getElementById('apiKey');
|
||||||
|
const saveApiKeyBtn = document.getElementById('saveApiKey');
|
||||||
|
const editApiKeyBtn = document.getElementById('editApiKey');
|
||||||
|
const savedApiKeyInfo = document.getElementById('savedApiKeyInfo');
|
||||||
|
const apiKeyInputGroup = document.getElementById('apiKeyInputGroup');
|
||||||
|
const toggleApiKeyVisibilityBtn = document.getElementById('toggleApiKeyVisibility');
|
||||||
|
|
||||||
|
// 显示/隐藏API Key
|
||||||
|
toggleApiKeyVisibilityBtn.addEventListener('click', function() {
|
||||||
|
const type = apiKeyInput.getAttribute('type') === 'password' ? 'text' : 'password';
|
||||||
|
apiKeyInput.setAttribute('type', type);
|
||||||
|
|
||||||
|
// 修改图标
|
||||||
|
if (type === 'text') {
|
||||||
|
this.innerHTML = \`<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-gray-500" 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>\`;
|
||||||
|
} else {
|
||||||
|
this.innerHTML = \`<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-gray-500" 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>\`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 保存API Key到localStorage
|
||||||
|
saveApiKeyBtn.addEventListener('click', function() {
|
||||||
|
const apiKey = apiKeyInput.value.trim();
|
||||||
|
if (apiKey) {
|
||||||
|
localStorage.setItem('tts_api_key', apiKey);
|
||||||
|
apiKeyInputGroup.style.display = 'none';
|
||||||
|
saveApiKeyBtn.style.display = 'none';
|
||||||
|
savedApiKeyInfo.style.display = 'flex';
|
||||||
|
} else {
|
||||||
|
alert('请输入有效的API Key');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 编辑已保存的API Key
|
||||||
|
editApiKeyBtn.addEventListener('click', function() {
|
||||||
|
apiKeyInputGroup.style.display = 'flex';
|
||||||
|
saveApiKeyBtn.style.display = 'inline-flex';
|
||||||
|
savedApiKeyInfo.style.display = 'none';
|
||||||
|
});
|
||||||
|
|
||||||
|
// 检查是否有保存的API Key
|
||||||
|
const savedApiKey = localStorage.getItem('tts_api_key');
|
||||||
|
if (savedApiKey) {
|
||||||
|
apiKeyInput.value = savedApiKey;
|
||||||
|
apiKeyInputGroup.style.display = 'none';
|
||||||
|
saveApiKeyBtn.style.display = 'none';
|
||||||
|
savedApiKeyInfo.style.display = 'flex';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
Reference in New Issue
Block a user