feat(admin): ConfigCard 视觉基线卡壳

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-10-14 17:10:02 +08:00
parent 6881ccc3cd
commit 2e00db16b9
@@ -0,0 +1,146 @@
<!--
视觉基线卡壳(spec §4):三行卡头(图标+任务名 → 一句话说明 → 组级信息条)
+ 底部操作条(左 回滚链接 / 右 备注选填+保存——均 slot)+ 底部危险行。
只管布局与样式,不含任何业务逻辑。
-->
<template>
<ElCard shadow="never" class="config-card">
<template #header>
<div class="cc-header">
<div class="cc-title-row">
<ArtSvgIcon v-if="icon" :icon="icon" class="cc-icon" />
<span class="cc-title">{{ title }}</span>
</div>
<p v-if="subtitle" class="cc-subtitle">{{ subtitle }}</p>
<div v-if="info || $slots.info" class="cc-info">
<slot name="info">
<ArtSvgIcon icon="solar:info-circle-line-duotone" class="cc-info-icon" />
<span>{{ info }}</span>
</slot>
</div>
</div>
</template>
<slot />
<div v-if="$slots.footer" class="cc-footer">
<div class="cc-footer-left">
<slot name="footer-left" />
</div>
<div class="cc-footer-right">
<slot name="footer-right" />
</div>
</div>
<div v-if="dangerLabel || $slots.danger" class="cc-danger">
<span class="cc-danger-text">{{ dangerLabel }}</span>
<slot name="danger" />
</div>
</ElCard>
</template>
<script setup lang="ts">
defineOptions({ name: 'ConfigCard' })
defineProps<{
/** ArtSvgIcon 图标名(如 solar:palette-round-line-duotone) */
icon?: string
/** 任务名(品牌与外观 / AI 话术 …) */
title: string
/** 一句话「这是干嘛的」 */
subtitle?: string
/** 组级信息条文案(整组保存 · 立即生效 · 可回滚;带 • 字段留空=默认) */
info?: string
/** 危险行文案(有值才渲染危险行;按钮走 #danger slot) */
dangerLabel?: string
}>()
</script>
<style scoped lang="scss">
.config-card {
:deep(.el-card__header) {
padding: 14px 20px 10px;
}
:deep(.el-card__body) {
padding: 14px 20px;
}
}
.cc-header {
.cc-title-row {
display: flex;
align-items: center;
gap: 8px;
.cc-icon {
font-size: 18px;
color: var(--art-main-color, var(--el-color-primary));
}
.cc-title {
font-weight: 600;
font-size: 15px;
color: var(--art-text-color, var(--el-text-color-primary));
}
}
.cc-subtitle {
margin: 2px 0 0;
font-size: 12px;
color: var(--art-gray-500, var(--el-text-color-secondary));
}
.cc-info {
display: flex;
align-items: flex-start;
gap: 6px;
margin-top: 8px;
padding: 7px 12px;
border-radius: 6px;
background: var(--el-fill-color-lighter);
font-size: 12px;
line-height: 1.6;
color: var(--art-gray-500, var(--el-text-color-secondary));
.cc-info-icon {
flex-shrink: 0;
font-size: 14px;
color: var(--art-gray-400, var(--el-text-color-placeholder));
}
}
}
.cc-footer {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-top: 14px;
padding-top: 12px;
border-top: 1px dashed var(--el-border-color-lighter);
.cc-footer-right {
display: flex;
align-items: center;
gap: 8px;
}
}
.cc-danger {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-top: 10px;
padding: 7px 12px;
border: 1px solid var(--el-color-danger-light-7);
border-radius: 6px;
background: var(--el-color-danger-light-9);
.cc-danger-text {
font-size: 12px;
color: var(--el-color-danger);
}
}
</style>