feat: add Space aggregate login

This commit is contained in:
237899745
2026-05-27 20:39:46 +08:00
parent e725db79a9
commit 056948612a
136 changed files with 2405 additions and 322 deletions

View File

@@ -20,3 +20,19 @@ export async function executeScheduleNow() {
const { data } = await api.post('/schedule/execute', {})
return data
}
export async function fetchSocialLoginConfig() {
const { data } = await api.get('/social-login/config')
return data
}
export async function updateSocialLoginConfig(payload) {
const { data } = await api.post('/social-login/config', payload || {})
systemConfigGetter.clear()
return data
}
export async function testSocialLoginConfig(payload) {
const { data } = await api.post('/social-login/test', payload || {})
return data
}

View File

@@ -2,7 +2,7 @@
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { fetchSystemConfig, updateSystemConfig } from '../api/system'
import { fetchSocialLoginConfig, fetchSystemConfig, testSocialLoginConfig, updateSocialLoginConfig, updateSystemConfig } from '../api/system'
import { fetchKdocsQr, fetchKdocsStatus, clearKdocsLogin } from '../api/kdocs'
import { fetchProxyConfig, testProxy, updateProxyConfig } from '../api/proxy'
import { getCachedKdocsStatus, preloadKdocsStatus, updateCachedKdocsStatus } from '../utils/kdocsStatusCache'
@@ -22,6 +22,21 @@ const autoApproveEnabled = ref(false)
const autoApproveHourlyLimit = ref(10)
const autoApproveVipDays = ref(7)
const socialLoginEnabled = ref(false)
const socialLoginEndpoint = ref('https://www.spacezs.cn/connect.php')
const socialLoginAppid = ref('')
const socialLoginAppkey = ref('')
const socialLoginAppkeyMasked = ref('')
const socialLoginAppkeyConfigured = ref(false)
const socialLoginProviders = ref(['wx'])
const socialLoginSaving = ref(false)
const socialLoginTesting = ref(false)
const socialProviderOptions = [
{ label: 'QQ', value: 'qq' },
{ label: '微信', value: 'wx' },
{ label: '支付宝', value: 'alipay' },
]
const kdocsEnabled = ref(false)
const kdocsDocUrl = ref('')
const kdocsDefaultUnit = ref('')
@@ -83,9 +98,10 @@ function setKdocsHint(message) {
async function loadAll() {
loading.value = true
try {
const [system, proxy] = await Promise.all([
const [system, proxy, social] = await Promise.all([
fetchSystemConfig(),
fetchProxyConfig(),
fetchSocialLoginConfig(),
])
maxConcurrentGlobal.value = system.max_concurrent_global ?? 2
@@ -112,6 +128,16 @@ async function loadAll() {
kdocsRowEnd.value = system.kdocs_row_end ?? 0
kdocsAdminNotifyEnabled.value = (system.kdocs_admin_notify_enabled ?? 0) === 1
kdocsAdminNotifyEmail.value = system.kdocs_admin_notify_email || ''
socialLoginEnabled.value = (social.social_login_enabled ?? 0) === 1
socialLoginEndpoint.value = social.social_login_endpoint || 'https://www.spacezs.cn/connect.php'
socialLoginAppid.value = social.social_login_appid || ''
socialLoginAppkey.value = ''
socialLoginAppkeyMasked.value = social.social_login_appkey_masked || ''
socialLoginAppkeyConfigured.value = Boolean(social.social_login_appkey_configured)
socialLoginProviders.value = Array.isArray(social.social_login_providers) && social.social_login_providers.length
? social.social_login_providers
: ['wx']
} catch {
// handled by interceptor
} finally {
@@ -233,6 +259,78 @@ async function saveAutoApprove() {
}
}
function socialLoginPayload() {
return {
social_login_enabled: socialLoginEnabled.value ? 1 : 0,
social_login_endpoint: socialLoginEndpoint.value.trim() || 'https://www.spacezs.cn/connect.php',
social_login_appid: socialLoginAppid.value.trim(),
social_login_appkey: socialLoginAppkey.value.trim(),
social_login_providers: socialLoginProviders.value,
}
}
function validateSocialLoginForm() {
if (!socialLoginEnabled.value) return true
if (!socialLoginProviders.value.length) {
ElMessage.error('请选择至少一种登录方式')
return false
}
if (!socialLoginEndpoint.value.trim()) {
ElMessage.error('请输入聚合接口地址')
return false
}
if (!socialLoginAppid.value.trim()) {
ElMessage.error('请输入 APPID')
return false
}
if (!socialLoginAppkey.value.trim() && !socialLoginAppkeyConfigured.value) {
ElMessage.error('请输入 APPKEY')
return false
}
return true
}
async function saveSocialLogin() {
if (!validateSocialLoginForm()) return
socialLoginSaving.value = true
try {
const res = await updateSocialLoginConfig(socialLoginPayload())
const cfg = res?.config || {}
socialLoginAppkey.value = ''
socialLoginAppkeyMasked.value = cfg.social_login_appkey_masked || socialLoginAppkeyMasked.value
socialLoginAppkeyConfigured.value = Boolean(cfg.social_login_appkey_configured)
ElMessage.success(res?.message || '聚合登录配置已保存')
} catch {
// handled by interceptor
} finally {
socialLoginSaving.value = false
}
}
async function onTestSocialLogin() {
if (!validateSocialLoginForm()) return
socialLoginTesting.value = true
try {
const url = new URL(window.location.href)
url.pathname = '/login'
url.search = ''
url.hash = ''
const provider = socialLoginProviders.value[0] || 'wx'
const res = await testSocialLoginConfig({
...socialLoginPayload(),
provider,
redirect_uri: url.toString(),
})
await ElMessageBox.alert(res?.success ? '连接正常' : '测试完成', '聚合登录测试', {
confirmButtonText: '知道了',
})
} catch {
// handled by interceptor
} finally {
socialLoginTesting.value = false
}
}
async function saveKdocsConfig() {
const payload = {
kdocs_enabled: kdocsEnabled.value ? 1 : 0,
@@ -459,6 +557,43 @@ onMounted(loadAll)
<el-button type="primary" @click="saveAutoApprove">保存注册设置</el-button>
</div>
</el-card>
<el-card shadow="never" :body-style="{ padding: '16px' }" class="card section-card social-card">
<h3 class="section-title">聚合登录</h3>
<div class="section-sub app-muted">QQ微信支付宝快捷登录</div>
<el-form label-width="122px">
<el-form-item label="启用">
<el-switch v-model="socialLoginEnabled" />
</el-form-item>
<el-form-item label="登录方式">
<el-checkbox-group v-model="socialLoginProviders">
<el-checkbox v-for="item in socialProviderOptions" :key="item.value" :label="item.value">
{{ item.label }}
</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="接口地址">
<el-input v-model="socialLoginEndpoint" placeholder="https://www.spacezs.cn/connect.php" />
</el-form-item>
<el-form-item label="APPID">
<el-input v-model="socialLoginAppid" placeholder="Space APPID" />
</el-form-item>
<el-form-item label="APPKEY">
<el-input v-model="socialLoginAppkey" type="password" show-password placeholder="留空则保持当前密钥" />
<div v-if="socialLoginAppkeyConfigured" class="help">当前{{ socialLoginAppkeyMasked }}</div>
</el-form-item>
</el-form>
<div class="row-actions">
<el-button type="primary" :loading="socialLoginSaving" @click="saveSocialLogin">保存聚合登录</el-button>
<el-button :loading="socialLoginTesting" @click="onTestSocialLogin">测试连接</el-button>
</div>
</el-card>
</div>
<el-card shadow="never" :body-style="{ padding: '16px' }" class="card kdocs-card">
@@ -743,6 +878,12 @@ onMounted(loadAll)
}
}
@media (min-width: 1201px) {
.social-card {
grid-column: span 3;
}
}
@media (max-width: 768px) {
.config-grid {
grid-template-columns: 1fr;