mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-15 20:08:18 +08:00
feat(ui): grey out feature flags whose backend consumer is not wired
This commit is contained in:
parent
d8f9b04618
commit
47127fe918
@ -330,6 +330,8 @@ export default {
|
|||||||
disabled: 'Disabled {key}',
|
disabled: 'Disabled {key}',
|
||||||
toggleFailed: 'Toggle failed, please retry',
|
toggleFailed: 'Toggle failed, please retry',
|
||||||
footer: 'New features ship disabled by default. After enabling, watch the metrics and logs for the relevant module before widening the rollout.',
|
footer: 'New features ship disabled by default. After enabling, watch the metrics and logs for the relevant module before widening the rollout.',
|
||||||
|
notWired: 'Not yet wired',
|
||||||
|
notWiredTooltip: 'This flag is reserved but the underlying feature is not implemented yet — toggling has no effect.',
|
||||||
scope: {
|
scope: {
|
||||||
kb: 'KB whitelist',
|
kb: 'KB whitelist',
|
||||||
user: 'User whitelist',
|
user: 'User whitelist',
|
||||||
|
|||||||
@ -320,6 +320,8 @@ export default {
|
|||||||
disabled: '已关闭 {key}',
|
disabled: '已关闭 {key}',
|
||||||
toggleFailed: '切换失败,请稍后重试',
|
toggleFailed: '切换失败,请稍后重试',
|
||||||
footer: '默认情况下新功能以关闭状态发布。开启后请关注对应模块的指标与日志,确认无回归再扩大灰度。',
|
footer: '默认情况下新功能以关闭状态发布。开启后请关注对应模块的指标与日志,确认无回归再扩大灰度。',
|
||||||
|
notWired: '未实装',
|
||||||
|
notWiredTooltip: '该开关已预留但功能尚未实装,开启暂不会生效',
|
||||||
scope: {
|
scope: {
|
||||||
kb: '知识库白名单',
|
kb: '知识库白名单',
|
||||||
user: '用户白名单',
|
user: '用户白名单',
|
||||||
|
|||||||
@ -17,9 +17,19 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else-if="flags.length > 0" class="settings-card">
|
<div v-else-if="flags.length > 0" class="settings-card">
|
||||||
<div v-for="flag in flags" :key="flag.flagKey" class="setting-item">
|
<div
|
||||||
|
v-for="flag in flags"
|
||||||
|
:key="flag.flagKey"
|
||||||
|
class="setting-item"
|
||||||
|
:class="{ 'setting-item--unwired': !isWired(flag) }"
|
||||||
|
>
|
||||||
<div class="setting-info">
|
<div class="setting-info">
|
||||||
<div class="setting-label flag-key">{{ flag.flagKey }}</div>
|
<div class="flag-header">
|
||||||
|
<span class="setting-label flag-key">{{ flag.flagKey }}</span>
|
||||||
|
<span v-if="!isWired(flag)" class="flag-badge">
|
||||||
|
{{ t('settings.featureFlags.notWired') }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
<div v-if="describe(flag)" class="setting-hint">{{ describe(flag) }}</div>
|
<div v-if="describe(flag)" class="setting-hint">{{ describe(flag) }}</div>
|
||||||
<div v-if="hasScope(flag)" class="flag-scope">
|
<div v-if="hasScope(flag)" class="flag-scope">
|
||||||
<span v-if="flag.whitelistKbIds">
|
<span v-if="flag.whitelistKbIds">
|
||||||
@ -34,11 +44,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="setting-control">
|
<div class="setting-control">
|
||||||
<label class="toggle-switch">
|
<label
|
||||||
|
class="toggle-switch"
|
||||||
|
:title="!isWired(flag) ? t('settings.featureFlags.notWiredTooltip') : ''"
|
||||||
|
>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
:checked="flag.enabled"
|
:checked="flag.enabled"
|
||||||
:disabled="pending[flag.flagKey] === true"
|
:disabled="pending[flag.flagKey] === true || !isWired(flag)"
|
||||||
@change="onToggle(flag, ($event.target as HTMLInputElement).checked)"
|
@change="onToggle(flag, ($event.target as HTMLInputElement).checked)"
|
||||||
/>
|
/>
|
||||||
<span class="toggle-slider"></span>
|
<span class="toggle-slider"></span>
|
||||||
@ -64,6 +77,20 @@ import { featureFlagApi, type FeatureFlag } from '@/api/index'
|
|||||||
|
|
||||||
const { t, getLocaleMessage, locale } = useI18n()
|
const { t, getLocaleMessage, locale } = useI18n()
|
||||||
|
|
||||||
|
// Flag keys that have at least one featureFlagService.isEnabled(...) consumer
|
||||||
|
// in the backend. Seeded flags without a consumer toggle to no effect, which
|
||||||
|
// confuses operators — we grey them out + tooltip "not yet implemented".
|
||||||
|
//
|
||||||
|
// Update this set whenever a new flag gets wired. Verify with:
|
||||||
|
// grep -rn 'featureFlagService\.isEnabled' mateclaw-server/src/main/java
|
||||||
|
const WIRED_FLAGS = new Set<string>([
|
||||||
|
'wiki.ocr.enabled',
|
||||||
|
])
|
||||||
|
|
||||||
|
function isWired(flag: FeatureFlag): boolean {
|
||||||
|
return WIRED_FLAGS.has(flag.flagKey)
|
||||||
|
}
|
||||||
|
|
||||||
// Walk the message tree manually — vue-i18n's t()/tm() interpret dots as a
|
// Walk the message tree manually — vue-i18n's t()/tm() interpret dots as a
|
||||||
// nested path, and our flagKey ("wiki.ocr.enabled") contains dots that should
|
// nested path, and our flagKey ("wiki.ocr.enabled") contains dots that should
|
||||||
// be treated as part of a single property name. Backend description is the
|
// be treated as part of a single property name. Backend description is the
|
||||||
@ -150,6 +177,32 @@ onMounted(load)
|
|||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.flag-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flag-badge {
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: var(--mc-bg-sunken);
|
||||||
|
color: var(--mc-text-tertiary);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-item--unwired .setting-label,
|
||||||
|
.setting-item--unwired .setting-hint {
|
||||||
|
opacity: 0.55;
|
||||||
|
}
|
||||||
|
.setting-item--unwired .toggle-switch {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
.flag-scope {
|
.flag-scope {
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
color: var(--mc-text-tertiary);
|
color: var(--mc-text-tertiary);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user