Files
cn-rdms-web/src/views/workbench/modules/workbench-module-card.vue

137 lines
3.3 KiB
Vue
Raw Normal View History

<script setup lang="ts">
import { inject } from 'vue';
defineOptions({ name: 'WorkbenchModuleCard' });
// 由 src/views/workbench/index.vue provide 的"进入编辑模式"动作;
// 卡片正常态的"编辑布局"按钮直接调它,避免每个 widget 都透传 emit
const enterEditing = inject<(() => void) | null>('workbenchEnterEditing', null);
interface Props {
title: string;
icon?: string;
badgeCount?: number;
editing?: boolean;
hasSettings?: boolean;
}
withDefaults(defineProps<Props>(), {
icon: undefined,
badgeCount: undefined,
editing: false,
hasSettings: false
});
const emit = defineEmits<{
(e: 'hide'): void;
(e: 'open-settings'): void;
(e: 'refresh'): void;
}>();
</script>
<template>
<section class="module-card" :class="{ 'is-editing': editing }">
<header class="module-card__head">
<span v-if="editing" class="module-drag-handle" title="拖动调整位置">
<SvgIcon icon="mdi:drag-vertical" />
</span>
<SvgIcon v-if="icon" class="module-card__icon" :icon="icon" />
<span class="module-card__title">{{ title }}</span>
<span v-if="badgeCount != null" class="module-card__badge">{{ badgeCount }}</span>
<div class="module-card__actions">
<ElButton v-if="editing && hasSettings" link size="small" title="模块设置" @click="emit('open-settings')">
<SvgIcon icon="mdi:cog-outline" />
</ElButton>
<ElButton v-if="!editing" link size="small" title="刷新" @click="emit('refresh')">
<SvgIcon icon="mdi:refresh" />
</ElButton>
<ElButton v-if="!editing && enterEditing" link size="small" title="编辑工作台布局" @click="enterEditing()">
<SvgIcon icon="mdi:view-dashboard-edit-outline" />
</ElButton>
<ElButton v-if="editing" link size="small" title="隐藏此模块" type="danger" @click="emit('hide')">
<SvgIcon icon="mdi:close" />
</ElButton>
</div>
</header>
<div class="module-card__body">
<slot />
</div>
</section>
</template>
<style scoped>
.module-card {
background: var(--el-bg-color);
border: 1px solid var(--el-border-color-lighter);
border-radius: 10px;
height: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
transition:
border-color 120ms,
box-shadow 120ms;
}
.module-card.is-editing {
border-style: dashed;
border-color: var(--el-color-primary-light-5);
}
.module-card__head {
display: flex;
align-items: center;
gap: 8px;
padding: 10px 14px;
border-bottom: 1px solid var(--el-border-color-lighter);
background: var(--el-fill-color-blank);
}
.module-drag-handle {
cursor: grab;
color: var(--el-text-color-secondary);
display: inline-flex;
align-items: center;
}
.module-drag-handle:active {
cursor: grabbing;
}
.module-card__icon {
color: var(--el-color-primary);
font-size: 16px;
}
.module-card__title {
font-weight: 600;
font-size: 14px;
flex: 1;
}
.module-card__badge {
background: var(--el-fill-color);
color: var(--el-text-color-secondary);
padding: 1px 8px;
border-radius: 999px;
font-size: 12px;
}
.module-card__actions {
display: inline-flex;
align-items: center;
gap: 2px;
}
.module-card__body {
flex: 1;
min-height: 0;
padding: 14px;
overflow: auto;
display: flex;
flex-direction: column;
}
</style>