312 lines
7.6 KiB
Vue
312 lines
7.6 KiB
Vue
|
|
<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>
|