394 lines
11 KiB
Vue
394 lines
11 KiB
Vue
|
|
<script setup lang="ts">
|
||
|
|
import { computed } from 'vue';
|
||
|
|
import { getProductStatusLabel } from '../../shared/product-master-data';
|
||
|
|
import { getProductLifecycleActionCardMeta, getProductLifecycleStatusSummary } from '../shared';
|
||
|
|
|
||
|
|
defineOptions({ name: 'SettingLifecyclePanel' });
|
||
|
|
|
||
|
|
interface Props {
|
||
|
|
lifecycle: Api.Product.ProductLifecycleInfo | null;
|
||
|
|
}
|
||
|
|
|
||
|
|
interface Emits {
|
||
|
|
(e: 'action', action: Api.Product.ProductLifecycleAction): void;
|
||
|
|
}
|
||
|
|
|
||
|
|
const props = defineProps<Props>();
|
||
|
|
const emit = defineEmits<Emits>();
|
||
|
|
|
||
|
|
const statusSummary = computed(() => {
|
||
|
|
if (!props.lifecycle) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
return getProductLifecycleStatusSummary(props.lifecycle.statusCode);
|
||
|
|
});
|
||
|
|
|
||
|
|
const actionCards = computed(() =>
|
||
|
|
(props.lifecycle?.availableActions || []).map(action => ({
|
||
|
|
...action,
|
||
|
|
...getProductLifecycleActionCardMeta(action.actionCode)
|
||
|
|
}))
|
||
|
|
);
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<template>
|
||
|
|
<ElCard class="card-wrapper">
|
||
|
|
<template #header>
|
||
|
|
<div>
|
||
|
|
<h3 class="text-16px text-[#0f172a] font-700">生命周期管理</h3>
|
||
|
|
</div>
|
||
|
|
</template>
|
||
|
|
|
||
|
|
<template v-if="lifecycle">
|
||
|
|
<div class="setting-lifecycle-panel__layout">
|
||
|
|
<section
|
||
|
|
class="setting-lifecycle-panel__hero"
|
||
|
|
:class="[`setting-lifecycle-panel__hero--${statusSummary?.tone || 'slate'}`]"
|
||
|
|
>
|
||
|
|
<div class="setting-lifecycle-panel__hero-top">
|
||
|
|
<div class="setting-lifecycle-panel__hero-main">
|
||
|
|
<div class="setting-lifecycle-panel__hero-status-row">
|
||
|
|
<span class="setting-lifecycle-panel__hero-status-label">当前状态</span>
|
||
|
|
<span class="setting-lifecycle-panel__hero-status-chip">
|
||
|
|
{{ getProductStatusLabel(lifecycle.statusCode) }}
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
<h4 class="setting-lifecycle-panel__hero-title">{{ statusSummary?.caption }}</h4>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<p class="setting-lifecycle-panel__hero-desc">
|
||
|
|
{{ statusSummary?.description }}
|
||
|
|
</p>
|
||
|
|
|
||
|
|
<div class="setting-lifecycle-panel__reason-card">
|
||
|
|
<span class="setting-lifecycle-panel__reason-label">最近状态原因</span>
|
||
|
|
<strong class="setting-lifecycle-panel__reason-value">
|
||
|
|
{{ lifecycle.lastStatusReason || '当前没有记录状态原因。' }}
|
||
|
|
</strong>
|
||
|
|
</div>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<section class="setting-lifecycle-panel__action-panel">
|
||
|
|
<div class="setting-lifecycle-panel__action-head">
|
||
|
|
<h4 class="setting-lifecycle-panel__action-title">可执行动作</h4>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div v-if="actionCards.length > 0" class="setting-lifecycle-panel__action-grid">
|
||
|
|
<button
|
||
|
|
v-for="action in actionCards"
|
||
|
|
:key="action.actionCode"
|
||
|
|
type="button"
|
||
|
|
class="setting-lifecycle-panel__action-card"
|
||
|
|
:class="[`setting-lifecycle-panel__action-card--${action.tone}`]"
|
||
|
|
@click="emit('action', action)"
|
||
|
|
>
|
||
|
|
<div class="setting-lifecycle-panel__action-card-top">
|
||
|
|
<span class="setting-lifecycle-panel__action-dot" aria-hidden="true"></span>
|
||
|
|
<strong class="setting-lifecycle-panel__action-name">{{ action.actionName }}</strong>
|
||
|
|
</div>
|
||
|
|
<p class="setting-lifecycle-panel__action-desc">{{ action.description }}</p>
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
<div v-else class="setting-lifecycle-panel__empty-tip">当前状态下暂无可执行生命周期动作。</div>
|
||
|
|
</section>
|
||
|
|
</div>
|
||
|
|
</template>
|
||
|
|
|
||
|
|
<ElEmpty v-else description="未获取到生命周期信息" />
|
||
|
|
</ElCard>
|
||
|
|
</template>
|
||
|
|
|
||
|
|
<style scoped>
|
||
|
|
.setting-lifecycle-panel__layout {
|
||
|
|
display: grid;
|
||
|
|
grid-template-columns: minmax(0, 1.1fr) minmax(320px, 0.9fr);
|
||
|
|
gap: 16px;
|
||
|
|
align-items: start;
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__hero,
|
||
|
|
.setting-lifecycle-panel__action-panel {
|
||
|
|
display: flex;
|
||
|
|
flex-direction: column;
|
||
|
|
gap: 14px;
|
||
|
|
min-height: 100%;
|
||
|
|
padding: 18px;
|
||
|
|
border: 1px solid rgb(226 232 240 / 92%);
|
||
|
|
border-radius: 20px;
|
||
|
|
background-color: rgb(248 250 252 / 96%);
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__hero {
|
||
|
|
overflow: hidden;
|
||
|
|
background:
|
||
|
|
radial-gradient(circle at top left, rgb(15 118 110 / 10%), transparent 34%),
|
||
|
|
linear-gradient(180deg, rgb(255 255 255 / 99%), rgb(248 250 252 / 97%));
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__hero--emerald {
|
||
|
|
border-color: rgb(16 185 129 / 22%);
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__hero--amber {
|
||
|
|
border-color: rgb(245 158 11 / 22%);
|
||
|
|
background:
|
||
|
|
radial-gradient(circle at top left, rgb(245 158 11 / 10%), transparent 34%),
|
||
|
|
linear-gradient(180deg, rgb(255 255 255 / 99%), rgb(255 251 235 / 97%));
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__hero--slate {
|
||
|
|
border-color: rgb(100 116 139 / 22%);
|
||
|
|
background:
|
||
|
|
radial-gradient(circle at top left, rgb(100 116 139 / 10%), transparent 34%),
|
||
|
|
linear-gradient(180deg, rgb(255 255 255 / 99%), rgb(248 250 252 / 97%));
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__hero--rose {
|
||
|
|
border-color: rgb(244 63 94 / 22%);
|
||
|
|
background:
|
||
|
|
radial-gradient(circle at top left, rgb(244 63 94 / 10%), transparent 34%),
|
||
|
|
linear-gradient(180deg, rgb(255 255 255 / 99%), rgb(255 241 242 / 97%));
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__hero-top,
|
||
|
|
.setting-lifecycle-panel__action-head {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
gap: 16px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__hero-main {
|
||
|
|
display: flex;
|
||
|
|
flex-direction: column;
|
||
|
|
gap: 10px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__hero-status-row {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
gap: 10px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__hero-status-label {
|
||
|
|
color: rgb(71 85 105 / 94%);
|
||
|
|
font-size: 12px;
|
||
|
|
font-weight: 600;
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__hero-status-chip {
|
||
|
|
display: inline-flex;
|
||
|
|
align-items: center;
|
||
|
|
padding: 4px 12px;
|
||
|
|
border: 1px solid transparent;
|
||
|
|
border-radius: 999px;
|
||
|
|
font-size: 13px;
|
||
|
|
font-weight: 700;
|
||
|
|
line-height: 1.2;
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__hero-title {
|
||
|
|
color: rgb(15 23 42 / 96%);
|
||
|
|
font-size: 22px;
|
||
|
|
font-weight: 700;
|
||
|
|
line-height: 1.25;
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__action-title {
|
||
|
|
color: rgb(15 23 42 / 96%);
|
||
|
|
font-size: 18px;
|
||
|
|
font-weight: 700;
|
||
|
|
line-height: 1.25;
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__hero-desc {
|
||
|
|
max-width: 560px;
|
||
|
|
color: rgb(71 85 105 / 94%);
|
||
|
|
font-size: 14px;
|
||
|
|
line-height: 1.7;
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__reason-card {
|
||
|
|
display: flex;
|
||
|
|
flex-direction: column;
|
||
|
|
gap: 6px;
|
||
|
|
padding: 14px 16px;
|
||
|
|
border: 1px solid rgb(226 232 240 / 88%);
|
||
|
|
border-radius: 16px;
|
||
|
|
background-color: rgb(255 255 255 / 82%);
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__reason-label {
|
||
|
|
color: rgb(100 116 139 / 92%);
|
||
|
|
font-size: 12px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__reason-value {
|
||
|
|
color: rgb(15 23 42 / 94%);
|
||
|
|
font-size: 15px;
|
||
|
|
line-height: 1.7;
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__action-panel {
|
||
|
|
background:
|
||
|
|
radial-gradient(circle at top right, rgb(59 130 246 / 7%), transparent 32%),
|
||
|
|
linear-gradient(180deg, rgb(255 255 255 / 99%), rgb(248 250 252 / 97%));
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__action-grid {
|
||
|
|
display: grid;
|
||
|
|
grid-template-columns: repeat(1, minmax(0, 1fr));
|
||
|
|
gap: 10px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__action-card {
|
||
|
|
display: flex;
|
||
|
|
flex-direction: column;
|
||
|
|
gap: 8px;
|
||
|
|
width: 100%;
|
||
|
|
padding: 14px 16px;
|
||
|
|
border: 1px solid rgb(226 232 240 / 92%);
|
||
|
|
border-radius: 18px;
|
||
|
|
background-color: rgb(255 255 255 / 96%);
|
||
|
|
text-align: left;
|
||
|
|
transition:
|
||
|
|
transform 0.2s ease,
|
||
|
|
border-color 0.2s ease,
|
||
|
|
box-shadow 0.2s ease;
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__action-card:hover {
|
||
|
|
transform: translateY(-1px);
|
||
|
|
box-shadow: 0 10px 22px rgb(15 23 42 / 6%);
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__action-card-top {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
gap: 10px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__action-dot {
|
||
|
|
width: 10px;
|
||
|
|
height: 10px;
|
||
|
|
border-radius: 999px;
|
||
|
|
background-color: currentcolor;
|
||
|
|
flex: 0 0 auto;
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__action-name {
|
||
|
|
color: rgb(15 23 42 / 96%);
|
||
|
|
font-size: 16px;
|
||
|
|
font-weight: 700;
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__action-desc {
|
||
|
|
color: rgb(71 85 105 / 94%);
|
||
|
|
font-size: 13px;
|
||
|
|
line-height: 1.6;
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__empty-tip {
|
||
|
|
padding: 18px 16px;
|
||
|
|
border: 1px dashed rgb(203 213 225 / 92%);
|
||
|
|
border-radius: 16px;
|
||
|
|
color: rgb(100 116 139 / 92%);
|
||
|
|
font-size: 13px;
|
||
|
|
line-height: 1.7;
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__hero--emerald .setting-lifecycle-panel__hero-status-chip {
|
||
|
|
border-color: rgb(16 185 129 / 24%);
|
||
|
|
background-color: rgb(236 253 245 / 90%);
|
||
|
|
color: rgb(4 120 87 / 96%);
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__hero--amber .setting-lifecycle-panel__hero-status-chip {
|
||
|
|
border-color: rgb(245 158 11 / 24%);
|
||
|
|
background-color: rgb(255 247 237 / 94%);
|
||
|
|
color: rgb(180 83 9 / 96%);
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__hero--slate .setting-lifecycle-panel__hero-status-chip {
|
||
|
|
border-color: rgb(148 163 184 / 28%);
|
||
|
|
background-color: rgb(241 245 249 / 94%);
|
||
|
|
color: rgb(71 85 105 / 96%);
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__hero--rose .setting-lifecycle-panel__hero-status-chip {
|
||
|
|
border-color: rgb(244 63 94 / 24%);
|
||
|
|
background-color: rgb(255 241 242 / 94%);
|
||
|
|
color: rgb(190 24 93 / 96%);
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__action-card--emerald {
|
||
|
|
border-color: rgb(16 185 129 / 22%);
|
||
|
|
background: linear-gradient(90deg, rgb(236 253 245 / 90%), rgb(255 255 255 / 96%) 26%);
|
||
|
|
color: rgb(4 120 87 / 96%);
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__action-card--amber {
|
||
|
|
border-color: rgb(245 158 11 / 22%);
|
||
|
|
background: linear-gradient(90deg, rgb(255 247 237 / 92%), rgb(255 255 255 / 96%) 26%);
|
||
|
|
color: rgb(180 83 9 / 96%);
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__action-card--slate {
|
||
|
|
border-color: rgb(148 163 184 / 26%);
|
||
|
|
background: linear-gradient(90deg, rgb(241 245 249 / 92%), rgb(255 255 255 / 96%) 26%);
|
||
|
|
color: rgb(71 85 105 / 96%);
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__action-card--rose {
|
||
|
|
border-color: rgb(244 63 94 / 22%);
|
||
|
|
background: linear-gradient(90deg, rgb(255 241 242 / 92%), rgb(255 255 255 / 96%) 26%);
|
||
|
|
color: rgb(190 24 93 / 96%);
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__action-card--emerald:hover {
|
||
|
|
box-shadow: 0 10px 22px rgb(16 185 129 / 12%);
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__action-card--amber:hover {
|
||
|
|
box-shadow: 0 10px 22px rgb(245 158 11 / 12%);
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__action-card--slate:hover {
|
||
|
|
box-shadow: 0 10px 22px rgb(100 116 139 / 10%);
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__action-card--rose:hover {
|
||
|
|
box-shadow: 0 10px 22px rgb(244 63 94 / 12%);
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__action-card--emerald .setting-lifecycle-panel__action-name {
|
||
|
|
color: rgb(6 95 70 / 96%);
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__action-card--amber .setting-lifecycle-panel__action-name {
|
||
|
|
color: rgb(146 64 14 / 96%);
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__action-card--slate .setting-lifecycle-panel__action-name {
|
||
|
|
color: rgb(51 65 85 / 96%);
|
||
|
|
}
|
||
|
|
|
||
|
|
.setting-lifecycle-panel__action-card--rose .setting-lifecycle-panel__action-name {
|
||
|
|
color: rgb(159 18 57 / 96%);
|
||
|
|
}
|
||
|
|
|
||
|
|
@media (width <= 1280px) {
|
||
|
|
.setting-lifecycle-panel__layout {
|
||
|
|
grid-template-columns: 1fr;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
@media (width <= 640px) {
|
||
|
|
.setting-lifecycle-panel__hero-top,
|
||
|
|
.setting-lifecycle-panel__action-head {
|
||
|
|
align-items: flex-start;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
</style>
|