feat(projects): 1、增加空白页占位;2、调试已开发功能;
This commit is contained in:
169
src/views/workbench/modules/workbench-activity-panel.vue
Normal file
169
src/views/workbench/modules/workbench-activity-panel.vue
Normal file
@@ -0,0 +1,169 @@
|
||||
<script setup lang="ts">
|
||||
import type { WorkbenchActivityItem } from '../homepage';
|
||||
|
||||
defineOptions({ name: 'WorkbenchActivityPanel' });
|
||||
|
||||
interface Props {
|
||||
items: WorkbenchActivityItem[];
|
||||
}
|
||||
|
||||
defineProps<Props>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ElCard class="workbench-activity card-wrapper" shadow="never">
|
||||
<template #header>
|
||||
<div>
|
||||
<h3 class="workbench-activity__title">最近动态</h3>
|
||||
<p class="workbench-activity__desc">关注与我相关的需求、任务、工单变化与 @ 提醒</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div v-if="items.length" class="workbench-activity__list">
|
||||
<article v-for="item in items" :key="item.id" class="workbench-activity__item">
|
||||
<div class="workbench-activity__rail">
|
||||
<span class="workbench-activity__dot" :class="`workbench-activity__dot--${item.tone}`" />
|
||||
<span class="workbench-activity__line" />
|
||||
</div>
|
||||
|
||||
<div class="workbench-activity__body">
|
||||
<div class="workbench-activity__meta">
|
||||
<span class="workbench-activity__time" :title="item.timeLabel">{{ item.relativeLabel }}</span>
|
||||
<span v-if="item.mentioned" class="workbench-activity__mention">@ 提醒</span>
|
||||
</div>
|
||||
<p class="workbench-activity__sentence">
|
||||
<strong class="workbench-activity__actor">{{ item.actor }}</strong>
|
||||
<span>{{ item.action }}</span>
|
||||
<strong class="workbench-activity__target">{{ item.target }}</strong>
|
||||
</p>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
<ElEmpty v-else description="暂无动态" :image-size="72" />
|
||||
</ElCard>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.workbench-activity {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:deep(.el-card__header) {
|
||||
padding: 16px 18px;
|
||||
border-bottom: 1px solid rgb(226 232 240 / 80%);
|
||||
}
|
||||
|
||||
.workbench-activity__title {
|
||||
margin: 0;
|
||||
color: rgb(15 23 42 / 98%);
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.workbench-activity__desc {
|
||||
margin: 4px 0 0;
|
||||
color: rgb(100 116 139 / 92%);
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.workbench-activity__list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.workbench-activity__item {
|
||||
display: grid;
|
||||
grid-template-columns: 20px minmax(0, 1fr);
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.workbench-activity__rail {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.workbench-activity__dot {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 999px;
|
||||
margin-top: 8px;
|
||||
box-shadow: 0 0 0 4px rgb(255 255 255 / 96%);
|
||||
}
|
||||
|
||||
.workbench-activity__dot--sky {
|
||||
background-color: rgb(14 165 233 / 92%);
|
||||
}
|
||||
|
||||
.workbench-activity__dot--emerald {
|
||||
background-color: rgb(5 150 105 / 92%);
|
||||
}
|
||||
|
||||
.workbench-activity__dot--amber {
|
||||
background-color: rgb(217 119 6 / 92%);
|
||||
}
|
||||
|
||||
.workbench-activity__dot--rose {
|
||||
background-color: rgb(225 29 72 / 92%);
|
||||
}
|
||||
|
||||
.workbench-activity__dot--violet {
|
||||
background-color: rgb(124 58 237 / 92%);
|
||||
}
|
||||
|
||||
.workbench-activity__line {
|
||||
flex: 1;
|
||||
width: 2px;
|
||||
min-height: 28px;
|
||||
margin-top: 4px;
|
||||
background: linear-gradient(180deg, rgb(203 213 225 / 96%), rgb(226 232 240 / 24%));
|
||||
}
|
||||
|
||||
.workbench-activity__item:last-child .workbench-activity__line {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.workbench-activity__body {
|
||||
padding: 6px 14px 14px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.workbench-activity__meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.workbench-activity__time {
|
||||
color: rgb(100 116 139 / 92%);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.workbench-activity__mention {
|
||||
padding: 1px 8px;
|
||||
border-radius: 999px;
|
||||
background-color: rgb(237 233 254 / 96%);
|
||||
color: rgb(109 40 217 / 96%);
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.workbench-activity__sentence {
|
||||
margin: 6px 0 0;
|
||||
color: rgb(71 85 105 / 94%);
|
||||
font-size: 13px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.workbench-activity__actor {
|
||||
color: rgb(15 23 42 / 98%);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.workbench-activity__target {
|
||||
color: rgb(14 116 144 / 96%);
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
318
src/views/workbench/modules/workbench-banner.vue
Normal file
318
src/views/workbench/modules/workbench-banner.vue
Normal file
@@ -0,0 +1,318 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { useAuthStore } from '@/store/modules/auth';
|
||||
import { useRouterPush } from '@/hooks/common/router';
|
||||
import { getGreeting, getTodayLabel } from '../homepage';
|
||||
import type { WorkbenchBannerSummary } from '../homepage';
|
||||
|
||||
defineOptions({ name: 'WorkbenchBanner' });
|
||||
|
||||
interface Props {
|
||||
summary: WorkbenchBannerSummary;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const { routerPushByKey } = useRouterPush();
|
||||
const authStore = useAuthStore();
|
||||
|
||||
const displayName = computed(() => authStore.userInfo.nickname || authStore.userInfo.userName || '同学');
|
||||
const greeting = computed(() => getGreeting());
|
||||
const todayLabel = computed(() => getTodayLabel());
|
||||
|
||||
const rhythmItems = computed(() => [
|
||||
{ label: '本周完成', value: `${props.summary.weekDone} / ${props.summary.weekTotal}`, tone: 'emerald' as const },
|
||||
{ label: '进行中', value: String(props.summary.weekInProgress), tone: 'sky' as const },
|
||||
{ label: '逾期', value: String(props.summary.weekOverdue), tone: 'rose' as const }
|
||||
]);
|
||||
|
||||
function handleCreateRequirement() {
|
||||
routerPushByKey('product_list');
|
||||
}
|
||||
|
||||
function handleCreateTask() {
|
||||
routerPushByKey('project_list');
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="workbench-banner">
|
||||
<div class="workbench-banner__identity">
|
||||
<div class="workbench-banner__title-group">
|
||||
<h1 class="workbench-banner__title">{{ greeting }},{{ displayName }}</h1>
|
||||
<span class="workbench-banner__decor-word">RDMS</span>
|
||||
</div>
|
||||
<p class="workbench-banner__subtitle">{{ todayLabel }}</p>
|
||||
|
||||
<div class="workbench-banner__digest">
|
||||
<div class="workbench-banner__digest-item">
|
||||
<span class="workbench-banner__digest-label">今日待办</span>
|
||||
<strong class="workbench-banner__digest-value">{{ summary.todoCount }}</strong>
|
||||
<span class="workbench-banner__digest-unit">项</span>
|
||||
</div>
|
||||
<span class="workbench-banner__digest-sep">·</span>
|
||||
<div class="workbench-banner__digest-item">
|
||||
<span class="workbench-banner__digest-label">即将到期</span>
|
||||
<strong class="workbench-banner__digest-value workbench-banner__digest-value--warn">
|
||||
{{ summary.upcomingCount }}
|
||||
</strong>
|
||||
<span class="workbench-banner__digest-unit">项</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="workbench-banner__actions">
|
||||
<ElButton type="primary" @click="handleCreateRequirement">
|
||||
<SvgIcon icon="mdi:plus" class="workbench-banner__btn-icon" />
|
||||
<span>新建需求</span>
|
||||
</ElButton>
|
||||
<ElButton @click="handleCreateTask">
|
||||
<SvgIcon icon="mdi:plus" class="workbench-banner__btn-icon" />
|
||||
<span>新建任务</span>
|
||||
</ElButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="workbench-banner__rhythm">
|
||||
<div class="workbench-banner__rhythm-header">
|
||||
<h2 class="workbench-banner__rhythm-title">本周节奏</h2>
|
||||
<span class="workbench-banner__rhythm-rate">完成率 {{ summary.weekCompletionRate }}%</span>
|
||||
</div>
|
||||
<div class="workbench-banner__rhythm-bar">
|
||||
<div class="workbench-banner__rhythm-bar-inner" :style="{ width: `${summary.weekCompletionRate}%` }" />
|
||||
</div>
|
||||
<ul class="workbench-banner__rhythm-list">
|
||||
<li
|
||||
v-for="item in rhythmItems"
|
||||
:key="item.label"
|
||||
class="workbench-banner__rhythm-item"
|
||||
:class="`workbench-banner__rhythm-item--${item.tone}`"
|
||||
>
|
||||
<span class="workbench-banner__rhythm-item-label">{{ item.label }}</span>
|
||||
<strong class="workbench-banner__rhythm-item-value">{{ item.value }}</strong>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.workbench-banner {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.55fr) minmax(280px, 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%));
|
||||
}
|
||||
|
||||
.workbench-banner__identity {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.workbench-banner__title-group {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 14px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.workbench-banner__title {
|
||||
margin: 0;
|
||||
color: rgb(15 23 42 / 98%);
|
||||
font-size: 32px;
|
||||
line-height: 1.15;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
.workbench-banner__decor-word {
|
||||
color: transparent;
|
||||
background: linear-gradient(180deg, rgb(14 116 144 / 92%), rgb(13 148 136 / 60%));
|
||||
background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
font-size: 22px;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0.32em;
|
||||
text-shadow: 0 10px 24px rgb(14 116 144 / 14%);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.workbench-banner__subtitle {
|
||||
margin: 0;
|
||||
color: rgb(100 116 139 / 92%);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.workbench-banner__digest {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 12px;
|
||||
padding: 14px 16px;
|
||||
border: 1px solid rgb(226 232 240 / 88%);
|
||||
border-radius: 18px;
|
||||
background-color: rgb(255 255 255 / 78%);
|
||||
}
|
||||
|
||||
.workbench-banner__digest-item {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.workbench-banner__digest-label {
|
||||
color: rgb(100 116 139 / 92%);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.workbench-banner__digest-value {
|
||||
color: rgb(15 23 42 / 98%);
|
||||
font-size: 24px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.workbench-banner__digest-value--warn {
|
||||
color: rgb(217 119 6 / 94%);
|
||||
}
|
||||
|
||||
.workbench-banner__digest-unit {
|
||||
color: rgb(100 116 139 / 90%);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.workbench-banner__digest-sep {
|
||||
color: rgb(203 213 225 / 96%);
|
||||
font-size: 18px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.workbench-banner__actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.workbench-banner__btn-icon {
|
||||
margin-right: 4px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.workbench-banner__rhythm {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
padding: 18px 20px;
|
||||
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%));
|
||||
}
|
||||
|
||||
.workbench-banner__rhythm-header {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.workbench-banner__rhythm-title {
|
||||
margin: 0;
|
||||
color: rgb(15 23 42 / 98%);
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.workbench-banner__rhythm-rate {
|
||||
color: rgb(5 150 105 / 94%);
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.workbench-banner__rhythm-bar {
|
||||
position: relative;
|
||||
height: 8px;
|
||||
border-radius: 999px;
|
||||
background-color: rgb(226 232 240 / 80%);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.workbench-banner__rhythm-bar-inner {
|
||||
height: 100%;
|
||||
border-radius: 999px;
|
||||
background: linear-gradient(90deg, rgb(14 116 144 / 92%), rgb(16 185 129 / 88%));
|
||||
transition: width 240ms ease;
|
||||
}
|
||||
|
||||
.workbench-banner__rhythm-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 10px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.workbench-banner__rhythm-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
padding: 12px 12px;
|
||||
border-radius: 14px;
|
||||
background-color: rgb(248 250 252 / 96%);
|
||||
}
|
||||
|
||||
.workbench-banner__rhythm-item--emerald {
|
||||
background-color: rgb(236 253 245 / 88%);
|
||||
}
|
||||
|
||||
.workbench-banner__rhythm-item--sky {
|
||||
background-color: rgb(240 249 255 / 88%);
|
||||
}
|
||||
|
||||
.workbench-banner__rhythm-item--rose {
|
||||
background-color: rgb(255 241 242 / 88%);
|
||||
}
|
||||
|
||||
.workbench-banner__rhythm-item-label {
|
||||
color: rgb(100 116 139 / 92%);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.workbench-banner__rhythm-item-value {
|
||||
color: rgb(15 23 42 / 98%);
|
||||
font-size: 20px;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.workbench-banner__rhythm-item--emerald .workbench-banner__rhythm-item-value {
|
||||
color: rgb(5 150 105 / 96%);
|
||||
}
|
||||
|
||||
.workbench-banner__rhythm-item--sky .workbench-banner__rhythm-item-value {
|
||||
color: rgb(14 116 144 / 96%);
|
||||
}
|
||||
|
||||
.workbench-banner__rhythm-item--rose .workbench-banner__rhythm-item-value {
|
||||
color: rgb(225 29 72 / 94%);
|
||||
}
|
||||
|
||||
@media (width <= 1280px) {
|
||||
.workbench-banner {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@media (width <= 768px) {
|
||||
.workbench-banner {
|
||||
padding: 18px;
|
||||
}
|
||||
|
||||
.workbench-banner__title {
|
||||
font-size: 26px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
192
src/views/workbench/modules/workbench-kpi.vue
Normal file
192
src/views/workbench/modules/workbench-kpi.vue
Normal file
@@ -0,0 +1,192 @@
|
||||
<script setup lang="ts">
|
||||
import type { WorkbenchKpiCard } from '../homepage';
|
||||
|
||||
defineOptions({ name: 'WorkbenchKpi' });
|
||||
|
||||
interface Props {
|
||||
cards: WorkbenchKpiCard[];
|
||||
}
|
||||
|
||||
defineProps<Props>();
|
||||
|
||||
function getTrendIcon(trend: WorkbenchKpiCard['trend']) {
|
||||
if (trend === 'up') return 'mdi:arrow-top-right-thin';
|
||||
if (trend === 'down') return 'mdi:arrow-bottom-right-thin';
|
||||
return 'mdi:minus';
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="workbench-kpi">
|
||||
<article
|
||||
v-for="card in cards"
|
||||
:key="card.key"
|
||||
class="workbench-kpi__card"
|
||||
:class="`workbench-kpi__card--${card.tone}`"
|
||||
>
|
||||
<div class="workbench-kpi__card-header">
|
||||
<span class="workbench-kpi__card-label">{{ card.label }}</span>
|
||||
<span class="workbench-kpi__card-icon">
|
||||
<SvgIcon :icon="card.icon" />
|
||||
</span>
|
||||
</div>
|
||||
<strong class="workbench-kpi__card-value">{{ card.value }}</strong>
|
||||
<div class="workbench-kpi__card-trend" :class="`workbench-kpi__card-trend--${card.trend}`">
|
||||
<SvgIcon :icon="getTrendIcon(card.trend)" class="workbench-kpi__card-trend-icon" />
|
||||
<span>{{ card.trendText }}</span>
|
||||
</div>
|
||||
<p class="workbench-kpi__card-hint">{{ card.hint }}</p>
|
||||
</article>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.workbench-kpi {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.workbench-kpi__card {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
padding: 20px;
|
||||
min-height: 148px;
|
||||
border: 1px solid rgb(226 232 240 / 92%);
|
||||
border-radius: 20px;
|
||||
background: linear-gradient(180deg, rgb(255 255 255 / 99%), rgb(248 250 252 / 96%));
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.workbench-kpi__card::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: -40% -30% auto auto;
|
||||
width: 160px;
|
||||
height: 160px;
|
||||
border-radius: 50%;
|
||||
opacity: 0.55;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.workbench-kpi__card--sky::after {
|
||||
background: radial-gradient(circle, rgb(14 165 233 / 22%), transparent 70%);
|
||||
}
|
||||
|
||||
.workbench-kpi__card--emerald::after {
|
||||
background: radial-gradient(circle, rgb(16 185 129 / 22%), transparent 70%);
|
||||
}
|
||||
|
||||
.workbench-kpi__card--amber::after {
|
||||
background: radial-gradient(circle, rgb(245 158 11 / 22%), transparent 70%);
|
||||
}
|
||||
|
||||
.workbench-kpi__card--rose::after {
|
||||
background: radial-gradient(circle, rgb(244 63 94 / 22%), transparent 70%);
|
||||
}
|
||||
|
||||
.workbench-kpi__card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.workbench-kpi__card-label {
|
||||
color: rgb(100 116 139 / 94%);
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.workbench-kpi__card-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 10px;
|
||||
background-color: rgb(255 255 255 / 88%);
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.workbench-kpi__card--sky .workbench-kpi__card-icon {
|
||||
color: rgb(14 116 144 / 94%);
|
||||
}
|
||||
|
||||
.workbench-kpi__card--emerald .workbench-kpi__card-icon {
|
||||
color: rgb(5 150 105 / 94%);
|
||||
}
|
||||
|
||||
.workbench-kpi__card--amber .workbench-kpi__card-icon {
|
||||
color: rgb(217 119 6 / 94%);
|
||||
}
|
||||
|
||||
.workbench-kpi__card--rose .workbench-kpi__card-icon {
|
||||
color: rgb(225 29 72 / 94%);
|
||||
}
|
||||
|
||||
.workbench-kpi__card-value {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
color: rgb(15 23 42 / 98%);
|
||||
font-size: 32px;
|
||||
line-height: 1.05;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
.workbench-kpi__card-trend {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
width: fit-content;
|
||||
padding: 3px 10px;
|
||||
border-radius: 999px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.workbench-kpi__card-trend--up {
|
||||
color: rgb(5 150 105 / 96%);
|
||||
background-color: rgb(236 253 245 / 96%);
|
||||
}
|
||||
|
||||
.workbench-kpi__card-trend--down {
|
||||
color: rgb(225 29 72 / 96%);
|
||||
background-color: rgb(255 241 242 / 96%);
|
||||
}
|
||||
|
||||
.workbench-kpi__card-trend--flat {
|
||||
color: rgb(100 116 139 / 94%);
|
||||
background-color: rgb(241 245 249 / 96%);
|
||||
}
|
||||
|
||||
.workbench-kpi__card-trend-icon {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.workbench-kpi__card-hint {
|
||||
margin: 0;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
color: rgb(100 116 139 / 88%);
|
||||
font-size: 12px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
@media (width <= 1280px) {
|
||||
.workbench-kpi {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (width <= 600px) {
|
||||
.workbench-kpi {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
311
src/views/workbench/modules/workbench-project-grid.vue
Normal file
311
src/views/workbench/modules/workbench-project-grid.vue
Normal file
@@ -0,0 +1,311 @@
|
||||
<script setup lang="ts">
|
||||
import { useRouterPush } from '@/hooks/common/router';
|
||||
import type { WorkbenchProjectItem } from '../homepage';
|
||||
|
||||
defineOptions({ name: 'WorkbenchProjectGrid' });
|
||||
|
||||
interface Props {
|
||||
items: WorkbenchProjectItem[];
|
||||
}
|
||||
|
||||
defineProps<Props>();
|
||||
|
||||
const { routerPushByKey } = useRouterPush();
|
||||
|
||||
function handleEnterProjectList() {
|
||||
routerPushByKey('project_list');
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ElCard class="workbench-project card-wrapper" shadow="never">
|
||||
<template #header>
|
||||
<div class="workbench-project__header">
|
||||
<div>
|
||||
<h3 class="workbench-project__title">我参与的项目</h3>
|
||||
<p class="workbench-project__desc">直接看每个项目的当前进度、我的角色与未完成任务</p>
|
||||
</div>
|
||||
<ElButton type="primary" link @click="handleEnterProjectList">
|
||||
<span>进入项目列表</span>
|
||||
<SvgIcon icon="mdi:arrow-right-thin" class="workbench-project__more-icon" />
|
||||
</ElButton>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div v-if="items.length" class="workbench-project__grid">
|
||||
<article v-for="item in items" :key="item.id" class="workbench-project__card">
|
||||
<div class="workbench-project__card-header">
|
||||
<div class="workbench-project__card-title-group">
|
||||
<h4 class="workbench-project__card-title">{{ item.name }}</h4>
|
||||
<span class="workbench-project__card-code">{{ item.code }}</span>
|
||||
</div>
|
||||
<span class="workbench-project__card-status" :class="`workbench-project__card-status--${item.statusTone}`">
|
||||
{{ item.statusLabel }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="workbench-project__card-role">
|
||||
<span class="workbench-project__card-role-label">我的角色</span>
|
||||
<strong class="workbench-project__card-role-value">{{ item.myRole }}</strong>
|
||||
</div>
|
||||
|
||||
<div class="workbench-project__progress">
|
||||
<div class="workbench-project__progress-header">
|
||||
<span class="workbench-project__progress-label">进度</span>
|
||||
<strong class="workbench-project__progress-value">{{ item.progress }}%</strong>
|
||||
</div>
|
||||
<div class="workbench-project__progress-bar">
|
||||
<div
|
||||
class="workbench-project__progress-bar-inner"
|
||||
:class="`workbench-project__progress-bar-inner--${item.statusTone}`"
|
||||
:style="{ width: `${item.progress}%` }"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="workbench-project__footer">
|
||||
<div class="workbench-project__footer-block">
|
||||
<span class="workbench-project__footer-label">我负责的任务</span>
|
||||
<strong class="workbench-project__footer-value">
|
||||
{{ item.myTaskCount }}
|
||||
<span v-if="item.myPendingTaskCount > 0" class="workbench-project__footer-sub">
|
||||
(待处理 {{ item.myPendingTaskCount }})
|
||||
</span>
|
||||
</strong>
|
||||
</div>
|
||||
<div class="workbench-project__footer-block workbench-project__footer-block--right">
|
||||
<span class="workbench-project__footer-label">最近活动</span>
|
||||
<strong class="workbench-project__footer-value">{{ item.lastActiveLabel }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
<ElEmpty v-else description="暂未参与任何项目" :image-size="72" />
|
||||
</ElCard>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.workbench-project {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:deep(.el-card__header) {
|
||||
padding: 16px 18px;
|
||||
border-bottom: 1px solid rgb(226 232 240 / 80%);
|
||||
}
|
||||
|
||||
.workbench-project__header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.workbench-project__title {
|
||||
margin: 0;
|
||||
color: rgb(15 23 42 / 98%);
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.workbench-project__desc {
|
||||
margin: 4px 0 0;
|
||||
color: rgb(100 116 139 / 92%);
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.workbench-project__more-icon {
|
||||
margin-left: 4px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.workbench-project__grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.workbench-project__card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
padding: 18px;
|
||||
border: 1px solid rgb(226 232 240 / 92%);
|
||||
border-radius: 18px;
|
||||
background: linear-gradient(180deg, rgb(255 255 255 / 99%), rgb(248 250 252 / 96%));
|
||||
transition:
|
||||
border-color 160ms ease,
|
||||
transform 160ms ease;
|
||||
}
|
||||
|
||||
.workbench-project__card:hover {
|
||||
border-color: rgb(14 116 144 / 60%);
|
||||
}
|
||||
|
||||
.workbench-project__card-header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.workbench-project__card-title-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.workbench-project__card-title {
|
||||
margin: 0;
|
||||
color: rgb(15 23 42 / 98%);
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.workbench-project__card-code {
|
||||
color: rgb(100 116 139 / 92%);
|
||||
font-size: 12px;
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
|
||||
.workbench-project__card-status {
|
||||
flex-shrink: 0;
|
||||
padding: 3px 10px;
|
||||
border-radius: 999px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.workbench-project__card-status--emerald {
|
||||
background-color: rgb(220 252 231 / 96%);
|
||||
color: rgb(5 150 105 / 96%);
|
||||
}
|
||||
|
||||
.workbench-project__card-status--sky {
|
||||
background-color: rgb(224 242 254 / 96%);
|
||||
color: rgb(14 116 144 / 96%);
|
||||
}
|
||||
|
||||
.workbench-project__card-status--amber {
|
||||
background-color: rgb(254 243 199 / 96%);
|
||||
color: rgb(180 83 9 / 96%);
|
||||
}
|
||||
|
||||
.workbench-project__card-role {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 10px 12px;
|
||||
border-radius: 12px;
|
||||
background-color: rgb(241 245 249 / 80%);
|
||||
}
|
||||
|
||||
.workbench-project__card-role-label {
|
||||
color: rgb(100 116 139 / 92%);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.workbench-project__card-role-value {
|
||||
color: rgb(15 23 42 / 98%);
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.workbench-project__progress {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.workbench-project__progress-header {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.workbench-project__progress-label {
|
||||
color: rgb(100 116 139 / 92%);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.workbench-project__progress-value {
|
||||
color: rgb(15 23 42 / 98%);
|
||||
font-size: 18px;
|
||||
line-height: 1;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
.workbench-project__progress-bar {
|
||||
height: 6px;
|
||||
border-radius: 999px;
|
||||
background-color: rgb(226 232 240 / 88%);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.workbench-project__progress-bar-inner {
|
||||
height: 100%;
|
||||
border-radius: 999px;
|
||||
transition: width 240ms ease;
|
||||
}
|
||||
|
||||
.workbench-project__progress-bar-inner--emerald {
|
||||
background: linear-gradient(90deg, rgb(5 150 105 / 92%), rgb(16 185 129 / 86%));
|
||||
}
|
||||
|
||||
.workbench-project__progress-bar-inner--sky {
|
||||
background: linear-gradient(90deg, rgb(14 116 144 / 92%), rgb(14 165 233 / 86%));
|
||||
}
|
||||
|
||||
.workbench-project__progress-bar-inner--amber {
|
||||
background: linear-gradient(90deg, rgb(217 119 6 / 92%), rgb(245 158 11 / 86%));
|
||||
}
|
||||
|
||||
.workbench-project__footer {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.workbench-project__footer-block {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.workbench-project__footer-block--right {
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.workbench-project__footer-label {
|
||||
color: rgb(100 116 139 / 92%);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.workbench-project__footer-value {
|
||||
color: rgb(15 23 42 / 98%);
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.workbench-project__footer-sub {
|
||||
color: rgb(190 18 60 / 94%);
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
@media (width <= 1280px) {
|
||||
.workbench-project__grid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (width <= 600px) {
|
||||
.workbench-project__grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
347
src/views/workbench/modules/workbench-todo-panel.vue
Normal file
347
src/views/workbench/modules/workbench-todo-panel.vue
Normal file
@@ -0,0 +1,347 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue';
|
||||
import type { RouteKey } from '@elegant-router/types';
|
||||
import { useRouterPush } from '@/hooks/common/router';
|
||||
import { filterWorkbenchTodoItems } from '../homepage';
|
||||
import type { WorkbenchTodoItem, WorkbenchTodoTimeBucket } from '../homepage';
|
||||
|
||||
defineOptions({ name: 'WorkbenchTodoPanel' });
|
||||
|
||||
interface Props {
|
||||
items: WorkbenchTodoItem[];
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const { routerPushByKey } = useRouterPush();
|
||||
|
||||
const activeBucket = ref<WorkbenchTodoTimeBucket>('all');
|
||||
|
||||
const buckets: Array<{ key: WorkbenchTodoTimeBucket; label: string }> = [
|
||||
{ key: 'all', label: '全部' },
|
||||
{ key: 'today', label: '今日' },
|
||||
{ key: 'week', label: '本周' },
|
||||
{ key: 'overdue', label: '逾期' }
|
||||
];
|
||||
|
||||
const bucketCounts = computed(() => ({
|
||||
all: props.items.length,
|
||||
today: filterWorkbenchTodoItems(props.items, 'today').length,
|
||||
week: filterWorkbenchTodoItems(props.items, 'week').length,
|
||||
overdue: filterWorkbenchTodoItems(props.items, 'overdue').length
|
||||
}));
|
||||
|
||||
const filteredItems = computed(() => filterWorkbenchTodoItems(props.items, activeBucket.value));
|
||||
|
||||
function handleClickItem(item: WorkbenchTodoItem) {
|
||||
if (!item.routeKey) return;
|
||||
routerPushByKey(item.routeKey as RouteKey);
|
||||
}
|
||||
|
||||
function getDeadlineToneClass(item: WorkbenchTodoItem) {
|
||||
if (item.overdue || (item.remainingDays !== null && item.remainingDays < 0)) {
|
||||
return 'workbench-todo__deadline--rose';
|
||||
}
|
||||
if (item.remainingDays === 0) return 'workbench-todo__deadline--amber';
|
||||
return 'workbench-todo__deadline--slate';
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ElCard class="workbench-todo card-wrapper" shadow="never">
|
||||
<template #header>
|
||||
<div class="workbench-todo__header">
|
||||
<div class="workbench-todo__title-group">
|
||||
<h3 class="workbench-todo__title">我的待办</h3>
|
||||
<p class="workbench-todo__desc">需要我处理的需求评审、任务、工单与 @ 提醒</p>
|
||||
</div>
|
||||
<div class="workbench-todo__tabs">
|
||||
<button
|
||||
v-for="bucket in buckets"
|
||||
:key="bucket.key"
|
||||
type="button"
|
||||
class="workbench-todo__tab"
|
||||
:class="{ 'workbench-todo__tab--active': activeBucket === bucket.key }"
|
||||
@click="activeBucket = bucket.key"
|
||||
>
|
||||
<span>{{ bucket.label }}</span>
|
||||
<span class="workbench-todo__tab-count">{{ bucketCounts[bucket.key] }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div v-if="filteredItems.length" class="workbench-todo__list">
|
||||
<article
|
||||
v-for="item in filteredItems"
|
||||
:key="item.id"
|
||||
class="workbench-todo__item"
|
||||
:class="{ 'workbench-todo__item--clickable': Boolean(item.routeKey) }"
|
||||
@click="handleClickItem(item)"
|
||||
>
|
||||
<div class="workbench-todo__leading">
|
||||
<span class="workbench-todo__category" :class="`workbench-todo__category--${item.categoryTone}`">
|
||||
{{ item.categoryLabel }}
|
||||
</span>
|
||||
<span v-if="item.highPriority" class="workbench-todo__priority">高</span>
|
||||
</div>
|
||||
|
||||
<div class="workbench-todo__body">
|
||||
<h4 class="workbench-todo__item-title">{{ item.title }}</h4>
|
||||
<div class="workbench-todo__meta">
|
||||
<span class="workbench-todo__source">{{ item.source }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="workbench-todo__trailing">
|
||||
<span class="workbench-todo__deadline" :class="getDeadlineToneClass(item)">
|
||||
{{ item.deadlineLabel }}
|
||||
</span>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
<ElEmpty v-else description="当前筛选下暂无待办" :image-size="72" />
|
||||
</ElCard>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.workbench-todo {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:deep(.el-card__header) {
|
||||
padding: 16px 18px;
|
||||
border-bottom: 1px solid rgb(226 232 240 / 80%);
|
||||
}
|
||||
|
||||
.workbench-todo__header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.workbench-todo__title-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.workbench-todo__title {
|
||||
margin: 0;
|
||||
color: rgb(15 23 42 / 98%);
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.workbench-todo__desc {
|
||||
margin: 0;
|
||||
color: rgb(100 116 139 / 92%);
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.workbench-todo__tabs {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.workbench-todo__tab {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 6px 12px;
|
||||
border: 1px solid rgb(226 232 240 / 92%);
|
||||
border-radius: 999px;
|
||||
background-color: rgb(255 255 255 / 96%);
|
||||
color: rgb(71 85 105 / 94%);
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
transition: all 160ms ease;
|
||||
}
|
||||
|
||||
.workbench-todo__tab:hover {
|
||||
border-color: rgb(14 116 144 / 64%);
|
||||
color: rgb(14 116 144 / 96%);
|
||||
}
|
||||
|
||||
.workbench-todo__tab--active {
|
||||
border-color: rgb(14 116 144 / 92%);
|
||||
background-color: rgb(14 116 144 / 96%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.workbench-todo__tab--active:hover {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.workbench-todo__tab-count {
|
||||
padding: 1px 6px;
|
||||
border-radius: 999px;
|
||||
background-color: rgb(241 245 249 / 96%);
|
||||
color: rgb(71 85 105 / 94%);
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.workbench-todo__tab--active .workbench-todo__tab-count {
|
||||
background-color: rgb(255 255 255 / 22%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.workbench-todo__list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.workbench-todo__item {
|
||||
display: grid;
|
||||
grid-template-columns: auto minmax(0, 1fr) auto;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
padding: 14px 16px;
|
||||
border: 1px solid rgb(226 232 240 / 90%);
|
||||
border-radius: 16px;
|
||||
background-color: rgb(255 255 255 / 98%);
|
||||
transition:
|
||||
border-color 160ms ease,
|
||||
background-color 160ms ease;
|
||||
}
|
||||
|
||||
.workbench-todo__item--clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.workbench-todo__item--clickable:hover {
|
||||
border-color: rgb(14 116 144 / 60%);
|
||||
background-color: rgb(240 253 250 / 84%);
|
||||
}
|
||||
|
||||
.workbench-todo__leading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.workbench-todo__category {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 3px 10px;
|
||||
border-radius: 999px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.workbench-todo__category--sky {
|
||||
background-color: rgb(224 242 254 / 96%);
|
||||
color: rgb(14 116 144 / 96%);
|
||||
}
|
||||
|
||||
.workbench-todo__category--emerald {
|
||||
background-color: rgb(220 252 231 / 96%);
|
||||
color: rgb(5 150 105 / 96%);
|
||||
}
|
||||
|
||||
.workbench-todo__category--amber {
|
||||
background-color: rgb(254 243 199 / 96%);
|
||||
color: rgb(180 83 9 / 96%);
|
||||
}
|
||||
|
||||
.workbench-todo__category--rose {
|
||||
background-color: rgb(255 228 230 / 96%);
|
||||
color: rgb(190 18 60 / 96%);
|
||||
}
|
||||
|
||||
.workbench-todo__category--violet {
|
||||
background-color: rgb(237 233 254 / 96%);
|
||||
color: rgb(109 40 217 / 96%);
|
||||
}
|
||||
|
||||
.workbench-todo__priority {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 6px;
|
||||
background-color: rgb(254 226 226 / 96%);
|
||||
color: rgb(220 38 38 / 96%);
|
||||
font-size: 11px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.workbench-todo__body {
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.workbench-todo__item-title {
|
||||
margin: 0;
|
||||
color: rgb(15 23 42 / 98%);
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 1.5;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.workbench-todo__meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.workbench-todo__source {
|
||||
color: rgb(100 116 139 / 92%);
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.workbench-todo__trailing {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.workbench-todo__deadline {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 4px 10px;
|
||||
border-radius: 999px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.workbench-todo__deadline--slate {
|
||||
background-color: rgb(241 245 249 / 96%);
|
||||
color: rgb(71 85 105 / 94%);
|
||||
}
|
||||
|
||||
.workbench-todo__deadline--amber {
|
||||
background-color: rgb(254 243 199 / 96%);
|
||||
color: rgb(180 83 9 / 96%);
|
||||
}
|
||||
|
||||
.workbench-todo__deadline--rose {
|
||||
background-color: rgb(255 228 230 / 96%);
|
||||
color: rgb(190 18 60 / 96%);
|
||||
}
|
||||
|
||||
@media (width <= 600px) {
|
||||
.workbench-todo__item {
|
||||
grid-template-columns: auto minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.workbench-todo__trailing {
|
||||
grid-column: 1 / -1;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user