feat(widgets): add phase 4 validation and accessibility

This commit is contained in:
kate522 committed 2026-08-17 19:14:40 +08:00
1 parent 8cad3c2d3d
commit e1723a8f08
117 files changed
+6 -17338

No files matched your search

@@ -1,164 +0,0 @@
<template>
<n-card class="oauth-provider-card" title="">
<!-- 平台信息头部 -->
<div class="oauth-provider-header">
<div class="oauth-header-left">
<span class="oauth-platform-icon">
<img v-if="icon" :src="icon" :alt="name" class="oauth-platform-img" />
<span v-else class="oauth-platform-text">{{ name.charAt(0) }}</span>
</span>
<div class="oauth-platform-info">
<h4>{{ name }}</h4>
<a :href="docUrl" target="_blank" class="oauth-doc-link">
前往 {{ name }} 创建应用 &#8599;
</a>
</div>
</div>
<n-switch v-model:value="modelValue.enabled" class="oauth-enable-switch">
<template #checked>启用</template>
<template #unchecked>停用</template>
</n-switch>
</div>
<!-- 表单区域(启用后才展开) -->
<div v-if="modelValue.enabled" class="oauth-form-body">
<n-form label-placement="top" class="aksrt-form oauth-form-grid">
<n-form-item
v-for="field in fields"
:key="field.key"
:label="field.label"
>
<n-input
v-if="!field.readonly"
v-model:value="modelValue[field.key]"
:type="field.secret ? 'password' : 'text'"
:show-password-on-click="field.secret"
:placeholder="'输入 ' + field.label"
size="large"
/>
<n-input
v-else
:value="callbackUrl"
readonly
size="large"
style='font-family: "SF Mono", Consolas, monospace; font-size:13px;'
/>
<template v-if="field.hint" #feedback>
<span class="aksrt-hint-text">{{ field.hint }}</span>
</template>
<template v-if="field.readonly && !field.hint" #feedback>
<span class="aksrt-hint-text">将此地址填写到对应平台的「授权回调域 / 回调 URL」中</span>
</template>
</n-form-item>
</n-form>
</div>
<!-- 未启用时的提示 -->
<div v-else class="oauth-disabled-hint">
<p>开启开关以配置此平台的 OAuth 凭证</p>
</div>
</n-card>
</template>
<script setup>
import { computed } from 'vue'
const props = defineProps({
modelValue: { type: Object, required: true },
name: { type: String, required: true },
docUrl: { type: String, required: true },
fields: { type: Array, required: true },
icon: { type: String, default: '' },
})
defineEmits(['update:modelValue'])
const callbackUrl = computed(() => {
const siteUrl = window.aksrtAdminData?.siteUrl || window.location.origin
return `${siteUrl}/oauth/callback/`
})
</script>
<style scoped>
.oauth-provider-card {
border-radius: 12px !important;
}
/* 平台头部 */
.oauth-provider-header {
display: flex;
align-items: center;
justify-content: space-between;
padding-bottom: 20px;
margin-bottom: 8px;
}
.oauth-header-left {
display: flex;
align-items: center;
gap: 14px;
}
.oauth-platform-icon {
width: 48px;
height: 48px;
display: flex;
align-items: center;
justify-content: center;
background: #f7f8fa;
border-radius: 12px;
}
.oauth-platform-img {
width: 26px;
height: 26px;
object-fit: contain;
}
.oauth-platform-text {
font-size: 20px;
font-weight: 700;
color: #666;
}
.oauth-platform-info h4 {
font-size: 16px;
font-weight: 600;
color: #1a1a2e;
margin-bottom: 2px;
}
.oauth-doc-link {
font-size: 12px;
color: #1565c0;
text-decoration: none;
}
.oauth-doc-link:hover {
text-decoration: underline;
}
/* 启用开关 */
.oauth-enable-switch {
flex-shrink: 0;
}
/* 表单体 */
.oauth-form-body {
padding-top: 8px;
border-top: 1px solid #f0f0f5;
}
.oauth-form-grid :deep(.n-form-item) {
max-width: 520px;
}
/* 禁用提示 */
.oauth-disabled-hint {
text-align: center;
padding: 28px 0 10px;
}
.oauth-disabled-hint p {
font-size: 13px;
color: #bbb;
}
</style>