Leeflour's picture
Upload 197 files
d0dd276 verified
<script setup>
import { useDashboardStore } from '../../../stores/dashboard'
import { ref, reactive } from 'vue'
const dashboardStore = useDashboardStore()
// 创建本地配置副本用于编辑
const localConfig = reactive({
fakeStreaming: dashboardStore.config.fakeStreaming,
enableVertexExpress: dashboardStore.config.enableVertexExpress,
vertexExpressApiKey: dashboardStore.config.vertexExpressApiKey || '',
googleCredentialsJson: dashboardStore.config.googleCredentialsJson || ''
})
// 密码和错误信息
const password = ref('')
const errorMsg = ref('')
const successMsg = ref('')
const isSaving = ref(false)
// 保存所有配置
async function saveAllConfigs() {
if (!password.value) {
errorMsg.value = '请输入密码'
return
}
isSaving.value = true
errorMsg.value = ''
successMsg.value = ''
try {
// 逐个保存配置项
const configKeys = Object.keys(localConfig)
for (const key of configKeys) {
// 如果配置有变化才更新
if (localConfig[key] !== dashboardStore.config[key]) {
await dashboardStore.updateConfig(key, localConfig[key], password.value)
// 更新store中的值
dashboardStore.config[key] = localConfig[key]
}
}
successMsg.value = '所有配置已保存'
} catch (error) {
errorMsg.value = error.message || '保存失败'
} finally {
isSaving.value = false
}
}
// 获取布尔值显示文本
function getBooleanText(value) {
return value ? '启用' : '禁用'
}
</script>
<template>
<div class="vertex-config">
<h3 class="section-title">Vertex 配置</h3>
<div class="config-form">
<!-- 布尔值配置项 -->
<div class="config-row">
<div class="config-group">
<label class="config-label">假流式响应</label>
<div class="toggle-wrapper">
<input type="checkbox" class="toggle" id="fakeStreaming" v-model="localConfig.fakeStreaming">
<label for="fakeStreaming" class="toggle-label">
<span class="toggle-text">{{ getBooleanText(localConfig.fakeStreaming) }}</span>
</label>
</div>
</div>
<div class="config-group">
<label class="config-label">Vertex Express</label>
<div class="toggle-wrapper">
<input type="checkbox" class="toggle" id="enableVertexExpress" v-model="localConfig.enableVertexExpress">
<label for="enableVertexExpress" class="toggle-label">
<span class="toggle-text">{{ getBooleanText(localConfig.enableVertexExpress) }}</span>
</label>
</div>
</div>
</div>
<!-- API Key 配置项 -->
<div class="config-row">
<div class="config-group full-width">
<label class="config-label">Vertex Express API密钥</label>
<input
type="password"
class="config-input"
v-model="localConfig.vertexExpressApiKey"
placeholder="请输入 Vertex Express API密钥"
>
</div>
</div>
<!-- Google Credentials JSON 配置项 -->
<div class="config-row">
<div class="config-group full-width">
<label class="config-label">Google Credentials JSON</label>
<textarea
class="config-input text-area"
v-model="localConfig.googleCredentialsJson"
placeholder="请输入 Google Credentials JSON"
></textarea>
</div>
</div>
<!-- 保存区域 -->
<div class="save-section">
<div class="password-input">
<input
type="password"
v-model="password"
placeholder="请输入管理密码"
class="config-input"
>
</div>
<button
class="save-button"
@click="saveAllConfigs"
:disabled="isSaving"
>
{{ isSaving ? '保存中...' : '保存所有配置' }}
</button>
</div>
<!-- 消息提示 -->
<div v-if="errorMsg" class="error-message">{{ errorMsg }}</div>
<div v-if="successMsg" class="success-message">{{ successMsg }}</div>
</div>
</div>
</template>
<style scoped>
.section-title {
color: var(--color-heading);
border-bottom: 1px solid var(--color-border);
padding-bottom: 10px;
margin-bottom: 20px;
transition: all 0.3s ease;
position: relative;
font-weight: 600;
}
.section-title::after {
content: '';
position: absolute;
bottom: -1px;
left: 0;
width: 50px;
height: 2px;
background: var(--gradient-primary);
}
.vertex-config {
margin-bottom: 25px;
}
.config-form {
background-color: var(--stats-item-bg);
border-radius: var(--radius-lg);
padding: 20px;
box-shadow: var(--shadow-sm);
border: 1px solid var(--card-border);
}
.config-row {
display: flex;
gap: 15px;
margin-bottom: 15px;
flex-wrap: wrap;
}
.config-group {
flex: 1;
min-width: 120px;
}
.full-width {
flex-basis: 100%;
}
.config-label {
display: block;
font-size: 14px;
margin-bottom: 5px;
color: var(--color-text);
font-weight: 500;
}
.config-input {
width: 100%;
padding: 8px 12px;
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
background-color: var(--color-background);
color: var(--color-text);
font-size: 14px;
transition: all 0.3s ease;
}
.config-input:focus {
outline: none;
border-color: var(--button-primary);
box-shadow: 0 0 0 2px rgba(79, 70, 229, 0.2);
}
.text-area {
resize: vertical;
min-height: 80px;
font-family: inherit;
line-height: 1.5;
}
/* 开关样式 */
.toggle-wrapper {
position: relative;
}
.toggle {
position: absolute;
opacity: 0;
width: 0;
height: 0;
}
.toggle-label {
display: flex;
align-items: center;
cursor: pointer;
user-select: none;
}
.toggle-label::before {
content: '';
display: inline-block;
width: 36px;
height: 20px;
background-color: var(--color-border);
border-radius: 10px;
margin-right: 8px;
position: relative;
transition: all 0.3s ease;
}
.toggle-label::after {
content: '';
position: absolute;
left: 3px;
width: 14px;
height: 14px;
background-color: white;
border-radius: 50%;
transition: all 0.3s ease;
}
.toggle:checked + .toggle-label::before {
background-color: var(--button-primary);
}
.toggle:checked + .toggle-label::after {
left: 19px;
}
.toggle-text {
font-size: 14px;
color: var(--color-text);
}
/* 保存区域样式 */
.save-section {
display: flex;
gap: 10px;
margin-top: 20px;
align-items: center;
}
.password-input {
flex: 1;
}
.save-button {
padding: 8px 16px;
background: var(--button-primary);
color: white;
border: none;
border-radius: var(--radius-md);
cursor: pointer;
font-weight: 500;
transition: all 0.3s ease;
}
.save-button:hover {
background: var(--button-primary-hover);
transform: translateY(-2px);
}
.save-button:disabled {
opacity: 0.7;
cursor: not-allowed;
transform: none;
}
.error-message {
color: var(--color-error);
margin-top: 10px;
font-size: 14px;
padding: 8px;
background-color: var(--color-error-bg);
border-radius: var(--radius-md);
}
.success-message {
color: var(--color-success);
margin-top: 10px;
font-size: 14px;
padding: 8px;
background-color: var(--color-success-bg);
border-radius: var(--radius-md);
}
/* 移动端优化 */
@media (max-width: 768px) {
.config-row {
gap: 10px;
}
.config-group {
min-width: 100px;
}
.save-section {
flex-direction: column;
}
.password-input {
width: 100%;
margin-bottom: 10px;
}
.save-button {
width: 100%;
}
}
/* 小屏幕手机进一步优化 */
@media (max-width: 480px) {
.config-row {
flex-direction: column;
gap: 10px;
}
.config-group {
width: 100%;
}
.config-form {
padding: 15px;
}
}
</style>