feat(admin): collapse provider/gateway groups by default, mask alipay private key, drop inline notice styles
- OAuth provider and payment gateway groups are collapsible and collapsed by default; remove dead expandWhen logic - Textarea fields support secret masking; mark alipay_privatekey as secret in JS and PHP schemas - Extract AksSecretClear shared by input and textarea secret fields - Replace inline styles in payment/post-list notice HTML with aksrt-hint / aksrt-warn classes Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
3179754670
commit
fa89f43ac9
18 files changed
+129
-94
No files matched your search
@@ -36,28 +36,21 @@
|
||||
<template v-if="schema.prefix" #prefix>{{ schema.prefix }}</template>
|
||||
<template v-if="schema.suffix" #suffix>{{ schema.suffix }}</template>
|
||||
</n-input>
|
||||
<n-button
|
||||
v-if="schema.secret && (modelValue === '***' || secretMarkedForClear)"
|
||||
size="small"
|
||||
secondary
|
||||
:type="secretMarkedForClear ? 'warning' : 'error'"
|
||||
@click="toggleSecretClear"
|
||||
>
|
||||
{{ secretMarkedForClear ? '取消清除' : '清除已配置密钥' }}
|
||||
</n-button>
|
||||
<span v-if="secretMarkedForClear" class="aksrt-field__secret-clear-hint">保存后将删除此密钥</span>
|
||||
<AksSecretClear :visible="secretClearVisible" :marked="secretMarkedForClear" @toggle="toggleSecretClear" />
|
||||
</div>
|
||||
|
||||
<!-- Textarea -->
|
||||
<n-input
|
||||
v-else-if="schema.type === 'textarea'"
|
||||
:value="modelValue"
|
||||
type="textarea"
|
||||
:placeholder="schema.placeholder || ''"
|
||||
:rows="schema.rows || 4"
|
||||
:disabled="schema.disabled"
|
||||
@update:value="emit('update:modelValue', $event)"
|
||||
/>
|
||||
<!-- Textarea(secret 时不回显已配置值,聚焦后输入新值即覆盖) -->
|
||||
<div v-else-if="schema.type === 'textarea'" class="aksrt-field__input-wrap">
|
||||
<n-input
|
||||
:value="secretTextareaValue"
|
||||
type="textarea"
|
||||
:placeholder="secretConfigured ? '已配置,留空保持不变;输入新值将覆盖' : (schema.placeholder || '')"
|
||||
:rows="schema.rows || 4"
|
||||
:disabled="schema.disabled"
|
||||
@update:value="emit('update:modelValue', $event)"
|
||||
/>
|
||||
<AksSecretClear :visible="secretClearVisible" :marked="secretMarkedForClear" @toggle="toggleSecretClear" />
|
||||
</div>
|
||||
|
||||
<!-- Number -->
|
||||
<div v-else-if="schema.type === 'number' || schema.type === 'spinner'" class="aksrt-field__number">
|
||||
@@ -534,6 +527,7 @@ import AksCodeEditor from './AksCodeEditor.vue'
|
||||
import AksCompositeField from './AksCompositeField.vue'
|
||||
import AksImageSelectField from './AksImageSelectField.vue'
|
||||
import AksSortHandle from './AksSortHandle.vue'
|
||||
import AksSecretClear from './AksSecretClear.vue'
|
||||
import AksSelectedOrder from './AksSelectedOrder.vue'
|
||||
import AksRepeaterListModal from './AksRepeaterListModal.vue'
|
||||
import { buildFieldsetSections } from './fieldset-sections.js'
|
||||
@@ -578,6 +572,10 @@ const fieldClasses = computed(() => ({
|
||||
|
||||
const secretMarkedForClear = computed(() => props.schema.secret && props.modelValue === '__AKSRT_CLEAR_SECRET__')
|
||||
const secretDisplayValue = computed(() => secretMarkedForClear.value ? '' : props.modelValue)
|
||||
/** 已配置密钥(服务端回传 '***'):textarea 不回显掩码,留空即保持原值。 */
|
||||
const secretConfigured = computed(() => props.schema.secret && props.modelValue === '***')
|
||||
const secretClearVisible = computed(() => secretConfigured.value || secretMarkedForClear.value)
|
||||
const secretTextareaValue = computed(() => secretConfigured.value ? '' : secretDisplayValue.value)
|
||||
|
||||
function toggleSecretClear() {
|
||||
emit('update:modelValue', secretMarkedForClear.value ? '***' : '__AKSRT_CLEAR_SECRET__')
|
||||
@@ -776,15 +774,6 @@ function updateRepeaterItem(index, fieldKey, value) {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.aksrt-field__input-wrap .n-button {
|
||||
justify-self: start;
|
||||
}
|
||||
|
||||
.aksrt-field__secret-clear-hint {
|
||||
color: var(--aksrt-color-warning, #d97706);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.aksrt-field__label-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -948,12 +937,35 @@ function updateRepeaterItem(index, fieldKey, value) {
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.aksrt-field__content :deep(p) {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.aksrt-field__content :deep(p + ul) {
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.aksrt-field__content :deep(ul) {
|
||||
margin: 0;
|
||||
padding-left: 18px;
|
||||
line-height: 1.9;
|
||||
}
|
||||
|
||||
/* 说明文字的小号灰色段落与警示文字(schema content 里用 class 引用) */
|
||||
.aksrt-field__content :deep(.aksrt-hint) {
|
||||
color: var(--aksrt-color-muted);
|
||||
font-size: 12px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.aksrt-field__content :deep(.aksrt-hint + .aksrt-hint) {
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.aksrt-field__content :deep(.aksrt-warn) {
|
||||
color: var(--aksrt-color-warning, #d97706);
|
||||
}
|
||||
|
||||
/* Mobile */
|
||||
@media (max-width: 768px) {
|
||||
.aksrt-field__link,
|
||||
|
||||
@@ -47,8 +47,7 @@
|
||||
* AksFieldGroup — card-style container for grouping related form fields.
|
||||
* Supports collapsible sections, icon headers, and description text.
|
||||
*
|
||||
* defaultCollapsed only seeds initial state. Parent should remount the group
|
||||
* (e.g. via :key when a channel turns on) if it wants to force expand again.
|
||||
* defaultCollapsed only seeds the initial state; afterwards the user toggles freely.
|
||||
*/
|
||||
import { ref } from 'vue'
|
||||
import FaIcon from './FaIcon.vue'
|
||||
|
||||
@@ -248,31 +248,13 @@ function isGroupVisible(group) {
|
||||
return matchDependsOn(group.dependsOn)
|
||||
}
|
||||
|
||||
/**
|
||||
* expandWhen 与 dependsOn 同语法(表单 key 字符串或规则对象),规则成立时分组默认展开。
|
||||
* 下拉值如 'null' 也是真值,所以按选项判断的分组要写规则对象,不能只写 key。
|
||||
*/
|
||||
function isGroupExpanded(group) {
|
||||
return matchDependsOn(group.expandWhen)
|
||||
}
|
||||
|
||||
/**
|
||||
* Collapse preference for a group.
|
||||
* - expandWhen: rule; when matched, start expanded (channel just enabled → open form)
|
||||
* - defaultCollapsed: static default
|
||||
*/
|
||||
/** 可折叠分组的初始状态:defaultCollapsed 为真时默认收起。 */
|
||||
function resolveGroupCollapsed(group) {
|
||||
if (group.expandWhen) return !isGroupExpanded(group)
|
||||
return !!group.defaultCollapsed
|
||||
}
|
||||
|
||||
/** Stable key; remount when expandWhen flips so newly enabled channels open expanded. */
|
||||
function groupKey(group) {
|
||||
const base = group.key || group.title || 'group'
|
||||
if (group.expandWhen) {
|
||||
return `${base}:${isGroupExpanded(group) ? 'on' : 'off'}`
|
||||
}
|
||||
return base
|
||||
return group.key || group.title || 'group'
|
||||
}
|
||||
|
||||
/** Get field value from formData */
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<div v-if="visible" class="aksrt-secret-clear">
|
||||
<n-button size="small" secondary :type="marked ? 'warning' : 'error'" @click="$emit('toggle')">
|
||||
{{ marked ? '取消清除' : '清除已配置密钥' }}
|
||||
</n-button>
|
||||
<span v-if="marked" class="aksrt-secret-clear__hint">保存后将删除此密钥</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
/**
|
||||
* 已配置密钥的「清除 / 取消清除」操作,input 与 textarea 两种 secret 字段共用。
|
||||
*/
|
||||
defineProps({
|
||||
/** 当前存在已配置密钥('***')或已标记清除时显示 */
|
||||
visible: { type: Boolean, default: false },
|
||||
/** 是否已标记为保存时清除 */
|
||||
marked: { type: Boolean, default: false },
|
||||
})
|
||||
|
||||
defineEmits(['toggle'])
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.aksrt-secret-clear {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.aksrt-secret-clear__hint {
|
||||
color: var(--aksrt-color-warning, #d97706);
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user