feat(projects): 1、增加空白页占位;2、调试已开发功能;

This commit is contained in:
2026-05-14 09:05:08 +08:00
parent f634d21d2a
commit ddd05f8c02
58 changed files with 7392 additions and 1325 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,767 @@
<script setup lang="ts">
import { computed, ref, watch } from 'vue';
import { RDMS_OBJECT_DIRECTION_DICT_CODE } from '@/constants/dict';
import { fetchGetProduct, fetchGetProductMembers, fetchGetProductSettings } from '@/service/api';
import { useDict } from '@/hooks/business/dict';
import { useCurrentProduct } from '../shared/use-current-product';
import ProductActivityTimelinePanel from './modules/product-activity-timeline-panel.vue';
import {
buildProductHomepageBanner,
buildRequirementPoolRecentChanges,
buildRequirementPoolSummary,
getProductHomepageExtensionModules
} from './homepage';
import { productHomepageExtensionMock, productRequirementPoolMock } from './mock';
defineOptions({ name: 'ProductDashboard' });
const { currentObjectId } = useCurrentProduct();
const { getLabel: getDirectionDictLabel } = useDict(RDMS_OBJECT_DIRECTION_DICT_CODE);
const pageLoading = ref(false);
const productDetail = ref<Api.Product.Product | null>(null);
const settings = ref<Api.Product.ProductSettings | null>(null);
const members = ref<Api.Product.ProductMember[]>([]);
const latestActivityTime = ref('');
const requirementPoolSummary = computed(() => buildRequirementPoolSummary(productRequirementPoolMock.summary));
const requirementPoolRecentChanges = computed(() =>
buildRequirementPoolRecentChanges(productRequirementPoolMock.recentChanges)
);
const homepageBanner = computed(() =>
buildProductHomepageBanner({
product: productDetail.value,
settings: settings.value,
members: members.value,
requirementSummary: requirementPoolSummary.value,
latestActivityTime: latestActivityTime.value
})
);
const extensionModules = computed(() => getProductHomepageExtensionModules(productHomepageExtensionMock));
const directionLabel = computed(() => getDirectionDictLabel(homepageBanner.value.identity.directionCode, '--'));
const bannerFacts = computed(() => {
const [managerFact, roleFact] = homepageBanner.value.identity.facts;
return [
{
label: '产品方向',
value: directionLabel.value,
fullWidth: false
},
{
label: managerFact?.label || '产品经理',
value: managerFact?.value || '--',
fullWidth: false
},
{
label: roleFact?.label || '角色摘要',
value: roleFact?.value || '--',
fullWidth: true
}
];
});
const bannerStatusClass = computed(() => {
const statusCode = homepageBanner.value.identity.statusCode;
return statusCode ? `product-homepage-banner--${statusCode}` : 'product-homepage-banner--default';
});
const bannerStatusWordClass = computed(() => {
const statusCode = homepageBanner.value.identity.statusCode;
return statusCode
? `product-homepage-banner__status-word--${statusCode}`
: 'product-homepage-banner__status-word--default';
});
function handleLatestActivityTimeChange(value: string) {
latestActivityTime.value = value;
}
async function loadDashboardData(objectId: string) {
pageLoading.value = true;
try {
const [productResult, settingsResult, membersResult] = await Promise.all([
fetchGetProduct(objectId),
fetchGetProductSettings(objectId),
fetchGetProductMembers(objectId)
]);
productDetail.value = productResult.error ? null : productResult.data || null;
settings.value = settingsResult.error ? null : settingsResult.data || null;
members.value = membersResult.error ? [] : membersResult.data || [];
} finally {
pageLoading.value = false;
}
}
watch(
() => currentObjectId.value,
async objectId => {
if (!objectId) {
productDetail.value = null;
settings.value = null;
members.value = [];
latestActivityTime.value = '';
return;
}
await loadDashboardData(objectId);
},
{ immediate: true }
);
</script>
<template>
<div v-loading="pageLoading" class="product-homepage">
<section class="product-homepage-banner" :class="bannerStatusClass">
<div class="product-homepage-banner__identity">
<div class="product-homepage-banner__title-group">
<div class="product-homepage-banner__title-main min-w-0">
<div class="product-homepage-banner__title-row">
<h1 class="product-homepage-banner__title">{{ homepageBanner.identity.name }}</h1>
<span class="product-homepage-banner__status-word" :class="bannerStatusWordClass">
{{ homepageBanner.identity.statusLabel }}
</span>
</div>
<div class="product-homepage-banner__subtitle">
<span class="product-homepage-banner__code">编号 {{ homepageBanner.identity.code }}</span>
<p v-if="homepageBanner.identity.description" class="product-homepage-banner__description">
{{ homepageBanner.identity.description }}
</p>
</div>
</div>
</div>
<div class="product-homepage-banner__facts">
<div
v-for="item in bannerFacts"
:key="item.label"
class="product-homepage-banner__fact"
:class="{ 'product-homepage-banner__fact--full': item.fullWidth }"
>
<span class="product-homepage-banner__fact-label">{{ item.label }}</span>
<strong class="product-homepage-banner__fact-value">{{ item.value }}</strong>
</div>
</div>
</div>
<div class="product-homepage-banner__metrics">
<article v-for="item in homepageBanner.metrics" :key="item.label" class="product-homepage-banner__metric">
<span class="product-homepage-banner__metric-label">{{ item.label }}</span>
<strong class="product-homepage-banner__metric-value">{{ item.value }}</strong>
</article>
</div>
</section>
<section class="product-homepage-main">
<ProductActivityTimelinePanel
:product-id="currentObjectId || ''"
@latest-time-change="handleLatestActivityTimeChange"
/>
<div class="product-homepage-main__aside">
<ElCard class="product-homepage-panel card-wrapper">
<template #header>
<div>
<h3 class="product-homepage-panel__title">需求池管理概览</h3>
<p class="product-homepage-panel__desc">先看需求池现在的总体规模状态结构和待处理压力</p>
</div>
</template>
<div class="product-homepage-requirement-summary">
<div class="product-homepage-requirement-summary__metrics">
<article
v-for="item in requirementPoolSummary.metrics"
:key="item.label"
class="product-homepage-requirement-summary__metric"
>
<span class="product-homepage-requirement-summary__metric-label">{{ item.label }}</span>
<strong class="product-homepage-requirement-summary__metric-value">{{ item.value }}</strong>
</article>
</div>
<div class="product-homepage-requirement-summary__distribution">
<div
v-for="item in requirementPoolSummary.distribution"
:key="item.label"
class="product-homepage-requirement-summary__distribution-item"
>
<span>{{ item.label }}</span>
<strong>{{ item.value }}</strong>
</div>
</div>
</div>
</ElCard>
<ElCard class="product-homepage-panel card-wrapper">
<template #header>
<div>
<h3 class="product-homepage-panel__title">需求池最近变化</h3>
<p class="product-homepage-panel__desc">承接需求新增状态流转和关闭情况和产品动态时间线分开表达</p>
</div>
</template>
<div v-if="requirementPoolRecentChanges.length" class="product-homepage-requirement-changes">
<article
v-for="item in requirementPoolRecentChanges"
:key="item.id"
class="product-homepage-requirement-changes__item"
>
<div class="product-homepage-requirement-changes__meta">
<ElTag type="info" effect="plain" size="small">{{ item.actionLabel }}</ElTag>
<span class="product-homepage-requirement-changes__time">{{ item.time }}</span>
</div>
<strong class="product-homepage-requirement-changes__title">{{ item.title }}</strong>
<p class="product-homepage-requirement-changes__status">当前状态{{ item.statusLabel }}</p>
</article>
</div>
<ElEmpty v-else description="当前暂无需求池最近变化" :image-size="72" />
</ElCard>
</div>
</section>
<section class="product-homepage-extension">
<ElCard v-for="module in extensionModules" :key="module.key" class="product-homepage-panel card-wrapper">
<template #header>
<div>
<h3 class="product-homepage-panel__title">{{ module.title }}</h3>
<p class="product-homepage-panel__desc">{{ module.description }}</p>
</div>
</template>
<div class="product-homepage-extension__list">
<div v-for="item in module.items" :key="item" class="product-homepage-extension__item">
<span class="product-homepage-extension__dot" />
<span>{{ item }}</span>
</div>
</div>
</ElCard>
</section>
</div>
</template>
<style scoped>
.product-homepage {
display: flex;
flex-direction: column;
gap: 16px;
}
.product-homepage-banner {
display: grid;
grid-template-columns: minmax(0, 1.55fr) minmax(320px, 1fr);
gap: 16px;
padding: 24px;
border: 1px solid rgb(226 232 240 / 92%);
border-radius: 24px;
background:
radial-gradient(circle at top left, rgb(14 116 144 / 14%), transparent 34%),
radial-gradient(circle at bottom right, rgb(15 118 110 / 10%), transparent 28%),
linear-gradient(135deg, rgb(255 255 255 / 99%), rgb(248 250 252 / 98%));
}
.product-homepage-banner--default {
border-color: rgb(226 232 240 / 92%);
background:
radial-gradient(circle at top left, rgb(14 116 144 / 14%), transparent 34%),
radial-gradient(circle at bottom right, rgb(15 118 110 / 10%), transparent 28%),
linear-gradient(135deg, rgb(255 255 255 / 99%), rgb(248 250 252 / 98%));
}
.product-homepage-banner--active {
border-color: rgb(167 243 208 / 88%);
background:
radial-gradient(circle at top left, rgb(5 150 105 / 16%), transparent 32%),
radial-gradient(circle at bottom right, rgb(16 185 129 / 14%), transparent 26%),
linear-gradient(135deg, rgb(236 253 245 / 99%), rgb(255 255 255 / 98%));
}
.product-homepage-banner--paused {
border-color: rgb(253 230 138 / 90%);
background:
radial-gradient(circle at top left, rgb(245 158 11 / 18%), transparent 32%),
radial-gradient(circle at bottom right, rgb(251 191 36 / 16%), transparent 24%),
linear-gradient(135deg, rgb(255 251 235 / 99%), rgb(255 255 255 / 98%));
}
.product-homepage-banner--archived {
border-color: rgb(203 213 225 / 92%);
background:
radial-gradient(circle at top left, rgb(100 116 139 / 14%), transparent 34%),
radial-gradient(circle at bottom right, rgb(148 163 184 / 10%), transparent 26%),
linear-gradient(135deg, rgb(248 250 252 / 99%), rgb(255 255 255 / 98%));
}
.product-homepage-banner--abandoned {
border-color: rgb(254 205 211 / 92%);
background:
radial-gradient(circle at top left, rgb(244 63 94 / 16%), transparent 32%),
radial-gradient(circle at bottom right, rgb(251 113 133 / 14%), transparent 24%),
linear-gradient(135deg, rgb(255 241 242 / 99%), rgb(255 255 255 / 98%));
}
.product-homepage-banner__identity {
display: flex;
min-width: 0;
flex-direction: column;
gap: 16px;
}
.product-homepage-banner__title-group {
display: flex;
align-items: flex-start;
gap: 12px;
}
.product-homepage-banner__title-main {
display: flex;
min-width: 0;
flex: 1;
flex-direction: column;
gap: 12px;
}
.product-homepage-banner__title-row {
display: flex;
min-width: 0;
align-items: baseline;
gap: 14px;
}
.product-homepage-banner__code {
margin: 0;
color: rgb(14 116 144 / 92%);
font-size: 13px;
font-weight: 600;
white-space: nowrap;
}
.product-homepage-banner__title {
margin: 0;
color: rgb(15 23 42 / 98%);
font-size: 34px;
line-height: 1.15;
letter-spacing: -0.03em;
}
.product-homepage-banner__subtitle {
display: flex;
min-width: 0;
flex-wrap: wrap;
align-items: baseline;
gap: 10px 14px;
}
.product-homepage-banner__description {
margin: 0;
min-width: 0;
color: rgb(71 85 105 / 94%);
font-size: 14px;
line-height: 1.8;
}
.product-homepage-banner__status-word {
flex-shrink: 0;
font-size: 26px;
font-weight: 800;
line-height: 1;
letter-spacing: 0.18em;
text-transform: uppercase;
user-select: none;
}
.product-homepage-banner__status-word--default {
color: rgb(148 163 184 / 48%);
}
.product-homepage-banner__status-word--active {
color: transparent;
background: linear-gradient(180deg, rgb(5 150 105 / 94%), rgb(16 185 129 / 70%));
background-clip: text;
-webkit-text-fill-color: transparent;
text-shadow: 0 10px 24px rgb(5 150 105 / 16%);
}
.product-homepage-banner__status-word--paused {
color: transparent;
background: linear-gradient(180deg, rgb(217 119 6 / 94%), rgb(245 158 11 / 70%));
background-clip: text;
-webkit-text-fill-color: transparent;
text-shadow: 0 10px 24px rgb(245 158 11 / 16%);
}
.product-homepage-banner__status-word--archived {
color: transparent;
background: linear-gradient(180deg, rgb(71 85 105 / 92%), rgb(148 163 184 / 64%));
background-clip: text;
-webkit-text-fill-color: transparent;
text-shadow: 0 10px 24px rgb(100 116 139 / 14%);
}
.product-homepage-banner__status-word--abandoned {
color: transparent;
background: linear-gradient(180deg, rgb(225 29 72 / 94%), rgb(251 113 133 / 68%));
background-clip: text;
-webkit-text-fill-color: transparent;
text-shadow: 0 10px 24px rgb(225 29 72 / 16%);
}
.product-homepage-banner__facts {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 12px;
}
.product-homepage-banner__fact {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
min-height: 58px;
padding: 14px 16px;
border: 1px solid rgb(226 232 240 / 88%);
border-radius: 18px;
background-color: rgb(255 255 255 / 78%);
}
.product-homepage-banner__fact--full {
grid-column: 1 / -1;
align-items: flex-start;
}
.product-homepage-banner__fact-label {
color: rgb(100 116 139 / 94%);
font-size: 13px;
white-space: nowrap;
}
.product-homepage-banner__fact-value {
color: rgb(15 23 42 / 96%);
font-size: 15px;
line-height: 1.6;
text-align: right;
}
.product-homepage-banner__fact--full .product-homepage-banner__fact-value {
max-width: 72%;
text-align: left;
}
.product-homepage-banner__metrics {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 12px;
}
.product-homepage-banner__metric {
display: flex;
min-height: 112px;
flex-direction: column;
justify-content: center;
gap: 16px;
padding: 18px;
border: 1px solid rgb(226 232 240 / 88%);
border-radius: 20px;
background: linear-gradient(180deg, rgb(255 255 255 / 99%), rgb(241 245 249 / 98%));
}
.product-homepage-banner__metric-label {
color: rgb(100 116 139 / 92%);
font-size: 12px;
}
.product-homepage-banner__metric-value {
color: rgb(15 23 42 / 98%);
font-size: 28px;
line-height: 1.1;
letter-spacing: -0.02em;
word-break: break-word;
}
.product-homepage-main {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 16px;
}
.product-homepage-main__aside {
display: flex;
min-width: 0;
flex-direction: column;
gap: 16px;
}
.product-homepage-panel {
overflow: hidden;
}
.product-homepage-panel__title {
margin: 0;
color: rgb(15 23 42 / 98%);
font-size: 16px;
font-weight: 700;
}
.product-homepage-panel__desc {
margin: 4px 0 0;
color: rgb(100 116 139 / 92%);
font-size: 13px;
line-height: 1.7;
}
.product-homepage-timeline {
display: flex;
flex-direction: column;
gap: 10px;
}
.product-homepage-timeline__item {
display: grid;
grid-template-columns: 20px minmax(0, 1fr);
gap: 12px;
}
.product-homepage-timeline__rail {
display: flex;
flex-direction: column;
align-items: center;
}
.product-homepage-timeline__dot {
width: 12px;
height: 12px;
border-radius: 999px;
margin-top: 6px;
box-shadow: 0 0 0 4px rgb(255 255 255 / 96%);
}
.product-homepage-timeline__dot--sky {
background-color: rgb(14 165 233 / 88%);
}
.product-homepage-timeline__dot--emerald {
background-color: rgb(5 150 105 / 88%);
}
.product-homepage-timeline__dot--amber {
background-color: rgb(217 119 6 / 88%);
}
.product-homepage-timeline__dot--rose {
background-color: rgb(225 29 72 / 88%);
}
.product-homepage-timeline__dot--slate {
background-color: rgb(100 116 139 / 88%);
}
.product-homepage-timeline__line {
flex: 1;
width: 2px;
min-height: 30px;
margin-top: 4px;
background: linear-gradient(180deg, rgb(203 213 225 / 96%), rgb(226 232 240 / 28%));
}
.product-homepage-timeline__item:last-child .product-homepage-timeline__line {
opacity: 0;
}
.product-homepage-timeline__content {
padding: 12px 14px;
border: 1px solid rgb(226 232 240 / 92%);
border-radius: 16px;
background-color: rgb(255 255 255 / 98%);
}
.product-homepage-timeline__meta {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 8px;
}
.product-homepage-timeline__time {
color: rgb(100 116 139 / 90%);
font-size: 12px;
}
.product-homepage-timeline__sentence {
margin: 6px 0 0;
color: rgb(71 85 105 / 94%);
font-size: 13px;
line-height: 1.65;
}
.product-homepage-timeline__headline {
margin-right: 6px;
color: rgb(15 23 42 / 98%);
font-weight: 600;
}
.product-homepage-requirement-summary {
display: flex;
flex-direction: column;
gap: 16px;
}
.product-homepage-requirement-summary__metrics {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 12px;
}
.product-homepage-requirement-summary__metric {
display: flex;
min-height: 100px;
flex-direction: column;
justify-content: center;
gap: 14px;
padding: 14px 16px;
border-radius: 18px;
background: linear-gradient(180deg, rgb(248 250 252 / 98%), rgb(241 245 249 / 94%));
}
.product-homepage-requirement-summary__metric-label {
color: rgb(100 116 139 / 92%);
font-size: 12px;
}
.product-homepage-requirement-summary__metric-value {
color: rgb(15 23 42 / 98%);
font-size: 24px;
line-height: 1.1;
}
.product-homepage-requirement-summary__distribution {
display: flex;
flex-direction: column;
gap: 10px;
}
.product-homepage-requirement-summary__distribution-item {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 13px 14px;
border: 1px solid rgb(226 232 240 / 88%);
border-radius: 14px;
background-color: rgb(255 255 255 / 96%);
color: rgb(51 65 85 / 95%);
font-size: 14px;
}
.product-homepage-requirement-summary__distribution-item strong {
color: rgb(15 23 42 / 98%);
font-size: 18px;
}
.product-homepage-requirement-changes {
display: flex;
flex-direction: column;
gap: 12px;
}
.product-homepage-requirement-changes__item {
padding: 14px 16px;
border: 1px solid rgb(226 232 240 / 90%);
border-radius: 18px;
background-color: rgb(255 255 255 / 98%);
}
.product-homepage-requirement-changes__meta {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 10px;
}
.product-homepage-requirement-changes__time {
color: rgb(100 116 139 / 90%);
font-size: 12px;
}
.product-homepage-requirement-changes__title {
display: block;
margin-top: 10px;
color: rgb(15 23 42 / 98%);
font-size: 15px;
line-height: 1.65;
}
.product-homepage-requirement-changes__status {
margin: 8px 0 0;
color: rgb(71 85 105 / 94%);
font-size: 13px;
line-height: 1.7;
}
.product-homepage-extension {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 16px;
}
.product-homepage-extension__list {
display: flex;
flex-direction: column;
gap: 10px;
}
.product-homepage-extension__item {
display: flex;
align-items: center;
gap: 10px;
padding: 13px 14px;
border-radius: 16px;
background-color: rgb(248 250 252 / 96%);
color: rgb(51 65 85 / 95%);
font-size: 14px;
line-height: 1.7;
}
.product-homepage-extension__dot {
width: 8px;
height: 8px;
border-radius: 999px;
background-color: rgb(14 116 144 / 88%);
flex-shrink: 0;
}
@media (width <= 1280px) {
.product-homepage-banner,
.product-homepage-main,
.product-homepage-extension {
grid-template-columns: 1fr;
}
}
@media (width <= 768px) {
.product-homepage-banner {
padding: 18px;
}
.product-homepage-banner__title-row {
flex-wrap: wrap;
}
.product-homepage-banner__title {
font-size: 28px;
}
.product-homepage-banner__status-word {
font-size: 22px;
}
.product-homepage-banner__facts,
.product-homepage-banner__metrics,
.product-homepage-requirement-summary__metrics {
grid-template-columns: 1fr;
}
}
</style>

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import { ref, watch } from 'vue';
import { computed, ref, watch } from 'vue';
import { fetchGetProductActivityTimelinePage } from '@/service/api';
import {
DEFAULT_PRODUCT_ACTIVITY_PAGE_SIZE,
@@ -13,6 +13,7 @@ defineOptions({ name: 'ProductActivityTimelinePanel' });
interface Props {
productId: string;
maxItems?: number;
}
interface Emits {
@@ -27,6 +28,11 @@ const loading = ref(false);
const loadError = ref(false);
const items = ref<ReturnType<typeof buildProductActivityDisplayItems>>([]);
const displayItems = computed(() => {
if (!props.maxItems || props.maxItems <= 0) return items.value;
return items.value.slice(0, props.maxItems);
});
async function loadRecentActivities() {
if (!props.productId) {
items.value = [];
@@ -79,11 +85,17 @@ watch(
</script>
<template>
<ElCard class="product-activity-panel card-wrapper">
<ElCard class="product-activity-panel card-wrapper" shadow="never">
<template #header>
<div class="product-activity-panel__header">
<div>
<h3 class="product-activity-panel__title">产品动态时间线</h3>
<div class="product-activity-panel__header-main">
<span class="product-activity-panel__header-icon">
<SvgIcon icon="mdi:timeline-clock-outline" />
</span>
<div class="product-activity-panel__header-text">
<h3 class="product-activity-panel__title">产品动态时间线</h3>
<p class="product-activity-panel__desc">对象状态与团队的最近活动</p>
</div>
</div>
<ElButton text type="primary" :disabled="!productId" @click="openDialog">更多</ElButton>
@@ -96,8 +108,8 @@ watch(
<ElButton type="primary" plain @click="loadRecentActivities">重新加载</ElButton>
</div>
<div v-else-if="items.length" class="product-activity-panel__timeline">
<article v-for="item in items" :key="item.id" class="product-activity-panel__item">
<div v-else-if="displayItems.length" class="product-activity-panel__timeline">
<article v-for="item in displayItems" :key="item.id" class="product-activity-panel__item">
<div class="product-activity-panel__rail">
<span class="product-activity-panel__dot" :class="`product-activity-panel__dot--${item.tone}`" />
<span class="product-activity-panel__line" />
@@ -139,11 +151,36 @@ watch(
.product-activity-panel__header {
display: flex;
align-items: flex-start;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.product-activity-panel__header-main {
display: flex;
align-items: center;
gap: 12px;
min-width: 0;
}
.product-activity-panel__header-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
border-radius: 12px;
font-size: 19px;
color: #fff;
flex-shrink: 0;
background: linear-gradient(135deg, #38bdf8, #0284c7);
box-shadow: 0 6px 14px -8px rgb(2 132 199 / 55%);
}
.product-activity-panel__header-text {
min-width: 0;
}
.product-activity-panel__title {
margin: 0;
color: rgb(15 23 42 / 98%);
@@ -152,19 +189,19 @@ watch(
}
.product-activity-panel__desc {
margin: 4px 0 0;
margin: 3px 0 0;
color: rgb(100 116 139 / 92%);
font-size: 13px;
line-height: 1.7;
font-size: 12px;
line-height: 1.6;
}
.product-activity-panel__body {
min-height: 520px;
min-height: auto;
}
.product-activity-panel__state {
display: flex;
min-height: 420px;
min-height: 200px;
flex-direction: column;
align-items: center;
justify-content: center;
@@ -273,10 +310,6 @@ watch(
}
@media (width <= 768px) {
.product-activity-panel__body {
min-height: auto;
}
.product-activity-panel__header {
flex-direction: column;
align-items: stretch;