feat(aihr): 重构运营后台与租户知识治理
- 重构运营总览、侧栏和内容运营工作台\n- 增加大喇叭、成果审核与运营统计链路\n- 补齐租户、知识空间、分类和知识维护闭环\n- 加固标签页租户上下文、停用租户写保护与迁移预检
This commit is contained in:
@@ -0,0 +1,301 @@
|
||||
<template>
|
||||
<div class="work-report-page">
|
||||
<section class="page-head">
|
||||
<div>
|
||||
<h1>成果投稿审核</h1>
|
||||
<p>审核员工提交的案例、视频、SOP 和知识素材;通过后仍需在对应业务库完成最终入库。</p>
|
||||
</div>
|
||||
<el-button :icon="Refresh" :loading="loading" @click="loadReports">刷新</el-button>
|
||||
</section>
|
||||
|
||||
<el-alert v-if="loadError" class="data-alert" type="warning" :closable="false" show-icon title="待审核投稿暂不可读取,页面未展示本地演示数据。" />
|
||||
|
||||
<section class="table-card">
|
||||
<div class="section-title">
|
||||
<div>
|
||||
<h2>待审核投稿</h2>
|
||||
<p>当前仅加载状态为 PENDING 的投稿。</p>
|
||||
</div>
|
||||
<el-tag type="warning">{{ reports.length }} 条</el-tag>
|
||||
</div>
|
||||
<el-table v-loading="loading" :data="reports" size="small" empty-text="暂无待审核投稿">
|
||||
<el-table-column prop="typeLabel" label="类型" width="120">
|
||||
<template #default="{ row }">{{ row.typeLabel || row.type }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="title" label="标题" min-width="220" show-overflow-tooltip />
|
||||
<el-table-column prop="submitterName" label="投稿人" width="120" />
|
||||
<el-table-column prop="attachmentName" label="附件" min-width="180" show-overflow-tooltip>
|
||||
<template #default="{ row }">{{ row.attachmentName || '无附件' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createdAt" label="提交时间" width="180" />
|
||||
<el-table-column label="操作" width="100" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="danger" @click="openReview(row)">审核</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</section>
|
||||
|
||||
<el-drawer v-model="drawerVisible" :title="selectedReport?.title || '成果投稿审核'" size="560px">
|
||||
<template v-if="selectedReport">
|
||||
<div class="report-meta">
|
||||
<el-tag>{{ selectedReport.typeLabel || selectedReport.type }}</el-tag>
|
||||
<span>{{ selectedReport.submitterName }} · {{ selectedReport.createdAt }}</span>
|
||||
</div>
|
||||
<h2>{{ selectedReport.title }}</h2>
|
||||
<p class="report-content">{{ selectedReport.content }}</p>
|
||||
<div v-if="selectedReport.attachmentName" class="attachment-name">
|
||||
<span>附件:{{ selectedReport.attachmentName }}</span>
|
||||
<el-button
|
||||
v-if="selectedReport.attachmentAvailable && selectedReport.attachmentUrl"
|
||||
link
|
||||
type="primary"
|
||||
:loading="attachmentLoading"
|
||||
@click="viewAttachment"
|
||||
>
|
||||
受控查看
|
||||
</el-button>
|
||||
<span v-else class="attachment-unavailable">附件暂不可读取</span>
|
||||
</div>
|
||||
<el-alert
|
||||
v-if="requiresAttachmentReview && !attachmentViewed"
|
||||
class="attachment-alert"
|
||||
type="warning"
|
||||
:closable="false"
|
||||
show-icon
|
||||
title="请先通过受控查看读取投稿附件,再审核通过。"
|
||||
/>
|
||||
<el-form label-position="top" class="review-form">
|
||||
<el-form-item label="审核意见">
|
||||
<el-input v-model="reviewNote" type="textarea" :rows="4" maxlength="500" show-word-limit placeholder="通过可选填;驳回请说明原因" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="review-actions">
|
||||
<el-button type="danger" plain :loading="reviewing" @click="submitReview('REJECT')">驳回</el-button>
|
||||
<el-button type="danger" :loading="reviewing" :disabled="requiresAttachmentReview && !attachmentViewed" @click="submitReview('APPROVE')">
|
||||
审核通过
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="OperationsWorkReportReview" lang="ts">
|
||||
import { Refresh } from '@element-plus/icons-vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { getWorkReportAttachment, listWorkReports, reviewWorkReport, type WorkReport } from '@/api/aihr/work-report';
|
||||
|
||||
const reports = ref<WorkReport[]>([]);
|
||||
const selectedReport = ref<WorkReport>();
|
||||
const loading = ref(false);
|
||||
const reviewing = ref(false);
|
||||
const loadError = ref(false);
|
||||
const drawerVisible = ref(false);
|
||||
const reviewNote = ref('');
|
||||
const attachmentLoading = ref(false);
|
||||
const attachmentViewed = ref(false);
|
||||
const requiresAttachmentReview = computed(() => Boolean(selectedReport.value?.attachmentName) || selectedReport.value?.type === 'VIDEO');
|
||||
|
||||
const loadReports = async () => {
|
||||
loading.value = true;
|
||||
loadError.value = false;
|
||||
try {
|
||||
const response = await listWorkReports('PENDING');
|
||||
reports.value = response.data ?? [];
|
||||
} catch {
|
||||
reports.value = [];
|
||||
loadError.value = true;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const openReview = (report: WorkReport) => {
|
||||
selectedReport.value = report;
|
||||
reviewNote.value = '';
|
||||
attachmentViewed.value = false;
|
||||
drawerVisible.value = true;
|
||||
};
|
||||
|
||||
const viewAttachment = async () => {
|
||||
const report = selectedReport.value;
|
||||
if (!report?.attachmentAvailable || !report.attachmentUrl) {
|
||||
ElMessage.warning('该附件暂不可读取');
|
||||
return;
|
||||
}
|
||||
attachmentLoading.value = true;
|
||||
try {
|
||||
const blob = await getWorkReportAttachment(report.attachmentUrl);
|
||||
if (!blob.size) throw new Error('empty attachment');
|
||||
attachmentViewed.value = true;
|
||||
const objectUrl = URL.createObjectURL(blob);
|
||||
const anchor = document.createElement('a');
|
||||
anchor.href = objectUrl;
|
||||
anchor.target = '_blank';
|
||||
anchor.rel = 'noopener noreferrer';
|
||||
document.body.appendChild(anchor);
|
||||
anchor.click();
|
||||
anchor.remove();
|
||||
window.setTimeout(() => URL.revokeObjectURL(objectUrl), 60_000);
|
||||
} catch {
|
||||
ElMessage.error('附件读取失败,请稍后重试');
|
||||
} finally {
|
||||
attachmentLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const submitReview = async (decision: 'APPROVE' | 'REJECT') => {
|
||||
if (!selectedReport.value) return;
|
||||
const note = reviewNote.value.trim();
|
||||
if (decision === 'REJECT' && !note) {
|
||||
ElMessage.warning('驳回时请填写审核意见');
|
||||
return;
|
||||
}
|
||||
reviewing.value = true;
|
||||
try {
|
||||
await reviewWorkReport(selectedReport.value.id, { decision, note: note || undefined });
|
||||
drawerVisible.value = false;
|
||||
ElMessage.success(decision === 'APPROVE' ? '投稿已审核通过' : '投稿已驳回');
|
||||
await loadReports();
|
||||
} catch {
|
||||
ElMessage.error('审核提交失败,请重试');
|
||||
} finally {
|
||||
reviewing.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(loadReports);
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.work-report-page {
|
||||
min-height: calc(100vh - 84px);
|
||||
padding: 24px 28px 40px;
|
||||
background: #f4f6fa;
|
||||
color: #172033;
|
||||
}
|
||||
|
||||
.page-head,
|
||||
.section-title,
|
||||
.report-meta,
|
||||
.review-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.page-head,
|
||||
.section-title {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.page-head {
|
||||
align-items: flex-start;
|
||||
margin-bottom: 18px;
|
||||
|
||||
h1,
|
||||
h2,
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 6px;
|
||||
color: #858991;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
.section-title p {
|
||||
margin-top: 6px;
|
||||
color: #858991;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.data-alert {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.table-card {
|
||||
padding: 18px;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
|
||||
.section-title {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
:deep(.el-table th.el-table__cell) {
|
||||
color: #667085;
|
||||
font-weight: 500;
|
||||
background: #fafbfc;
|
||||
}
|
||||
}
|
||||
|
||||
.report-meta,
|
||||
.review-actions {
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.report-meta {
|
||||
margin-bottom: 20px;
|
||||
color: #667085;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.report-content {
|
||||
min-height: 120px;
|
||||
color: #344054;
|
||||
line-height: 1.75;
|
||||
white-space: pre-wrap;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.attachment-name {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 10px 12px;
|
||||
border-radius: 6px;
|
||||
background: #f7f8fa;
|
||||
color: #667085;
|
||||
}
|
||||
|
||||
.attachment-unavailable {
|
||||
color: #c0781a;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.attachment-alert {
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
||||
.review-form {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.review-actions {
|
||||
justify-content: flex-end;
|
||||
margin-top: 22px;
|
||||
}
|
||||
|
||||
@media (max-width: 760px) {
|
||||
.work-report-page {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.page-head {
|
||||
gap: 12px;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,422 @@
|
||||
<template>
|
||||
<div class="work-results-page">
|
||||
<section class="page-head">
|
||||
<div>
|
||||
<h1>项目工作成果</h1>
|
||||
<p>按项目和自然日汇总员工已确认的工作助手记录,主管范围由服务端按当前身份复核。</p>
|
||||
</div>
|
||||
<el-button :icon="Refresh" :loading="projectsLoading || resultLoading" @click="refresh">刷新</el-button>
|
||||
</section>
|
||||
|
||||
<el-alert
|
||||
v-if="projectsError"
|
||||
class="data-alert"
|
||||
type="warning"
|
||||
:closable="false"
|
||||
show-icon
|
||||
title="项目列表暂未返回,无法查询团队工作成果。请完成项目权限配置后刷新。"
|
||||
/>
|
||||
<el-alert
|
||||
v-else-if="resultError"
|
||||
class="data-alert"
|
||||
type="warning"
|
||||
:closable="false"
|
||||
show-icon
|
||||
title="所选项目的工作成果暂不可读取,请刷新后重试。"
|
||||
/>
|
||||
|
||||
<section class="filter-card">
|
||||
<el-select
|
||||
v-model="projectCode"
|
||||
class="project-select"
|
||||
filterable
|
||||
placeholder="请选择项目"
|
||||
:loading="projectsLoading"
|
||||
:disabled="!projects.length"
|
||||
@change="resetAndLoad"
|
||||
>
|
||||
<el-option v-for="project in projects" :key="project.projectCode" :label="project.projectName" :value="project.projectCode" />
|
||||
</el-select>
|
||||
<el-date-picker v-model="workDate" type="date" value-format="YYYY-MM-DD" placeholder="工作日期" :disabled-date="disableFutureDate" />
|
||||
<el-button type="danger" :loading="resultLoading" :disabled="!projectCode" @click="resetAndLoad">查询</el-button>
|
||||
</section>
|
||||
|
||||
<el-empty v-if="!projectsLoading && !projects.length" class="empty-state" description="暂无可用项目;页面不会使用默认或猜测的项目编码。" />
|
||||
|
||||
<template v-else-if="projectResult">
|
||||
<section class="metric-grid">
|
||||
<div class="metric-card">
|
||||
<span>贡献员工</span>
|
||||
<strong>{{ projectResult.contributorCount }}</strong>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
<span>已汇总成果</span>
|
||||
<strong>{{ projectResult.resultCount }}</strong>
|
||||
</div>
|
||||
<div class="metric-card warning">
|
||||
<span>待跟进</span>
|
||||
<strong>{{ projectResult.pendingCount }}</strong>
|
||||
</div>
|
||||
<div class="metric-card danger">
|
||||
<span>高优先级</span>
|
||||
<strong>{{ projectResult.highPriorityCount }}</strong>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="table-card">
|
||||
<div class="section-title">
|
||||
<div>
|
||||
<h2>{{ projectResult.projectName }}</h2>
|
||||
<p>
|
||||
{{ projectResult.workDate }} · 第 {{ projectResult.pageNum }} 页展示 {{ projectResult.members.length }} /
|
||||
{{ projectResult.contributorCount }} 位员工的已确认记录
|
||||
</p>
|
||||
</div>
|
||||
<el-tag type="info">{{ projectResult.projectCode }}</el-tag>
|
||||
</div>
|
||||
<el-table :data="projectResult.members" size="small" empty-text="该日期暂无已确认工作成果">
|
||||
<el-table-column type="expand" width="48">
|
||||
<template #default="{ row }">
|
||||
<div class="member-details">
|
||||
<div v-for="section in resultSections(row.sections)" :key="section.key" class="result-section">
|
||||
<strong>{{ sectionLabel(section.key) }}({{ section.items.length }})</strong>
|
||||
<div v-if="section.items.length" class="result-list">
|
||||
<div v-for="item in section.items" :key="item.captureId" class="result-item">
|
||||
<span>{{ item.summary }}</span>
|
||||
<small>{{ item.occurredAt }} · {{ item.category || item.suggestedType }}</small>
|
||||
</div>
|
||||
</div>
|
||||
<span v-else class="muted">暂无记录</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="ownerName" label="员工" min-width="130" />
|
||||
<el-table-column prop="total" label="成果数" width="100" />
|
||||
<el-table-column label="待跟进" width="110">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.pending ? 'warning' : 'success'" size="small">{{ row.pending }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="version" label="版本" width="88" />
|
||||
<el-table-column prop="generatedAt" label="汇总时间" min-width="180" />
|
||||
</el-table>
|
||||
<el-pagination
|
||||
v-if="projectResult.contributorCount > projectResult.pageSize"
|
||||
class="member-pagination"
|
||||
background
|
||||
layout="prev, pager, next"
|
||||
:current-page="projectResult.pageNum"
|
||||
:page-size="projectResult.pageSize"
|
||||
:total="projectResult.contributorCount"
|
||||
@current-change="changePage"
|
||||
/>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<el-empty v-else-if="!projectsLoading && projectCode && !resultLoading" class="empty-state" description="请选择日期后查询项目工作成果。" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="OperationsWorkResults" lang="ts">
|
||||
import { Refresh } from '@element-plus/icons-vue';
|
||||
import { onMounted, ref, watch } from 'vue';
|
||||
import {
|
||||
getProjectWorkResult,
|
||||
listWorkResultProjects,
|
||||
type ProjectWorkResult,
|
||||
type WorkResultItem,
|
||||
type WorkResultProject
|
||||
} from '@/api/aihr/work-results';
|
||||
|
||||
const localDate = () => {
|
||||
const date = new Date();
|
||||
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
|
||||
};
|
||||
|
||||
const projects = ref<WorkResultProject[]>([]);
|
||||
const projectCode = ref('');
|
||||
const workDate = ref(localDate());
|
||||
const projectResult = ref<ProjectWorkResult>();
|
||||
const pageNum = ref(1);
|
||||
const pageSize = 20;
|
||||
const projectsLoading = ref(false);
|
||||
const resultLoading = ref(false);
|
||||
const projectsError = ref(false);
|
||||
const resultError = ref(false);
|
||||
|
||||
const disableFutureDate = (date: Date) => date.getTime() > Date.now();
|
||||
|
||||
const sectionLabels: Record<string, string> = {
|
||||
completed: '已完成',
|
||||
inspection: '巡检',
|
||||
services: '服务记录',
|
||||
leads: '服务线索',
|
||||
attendance: '考勤',
|
||||
pending: '待跟进'
|
||||
};
|
||||
const sectionLabel = (section: string) => sectionLabels[section] || section;
|
||||
const resultSections = (sections: Record<string, WorkResultItem[]>) => Object.entries(sections).map(([key, items]) => ({ key, items }));
|
||||
|
||||
const loadProjects = async () => {
|
||||
projectsLoading.value = true;
|
||||
projectsError.value = false;
|
||||
try {
|
||||
const response = await listWorkResultProjects();
|
||||
projects.value = response.data ?? [];
|
||||
if (!projects.value.some((project) => project.projectCode === projectCode.value)) {
|
||||
projectCode.value = projects.value[0]?.projectCode || '';
|
||||
}
|
||||
} catch {
|
||||
projects.value = [];
|
||||
projectCode.value = '';
|
||||
projectResult.value = undefined;
|
||||
projectsError.value = true;
|
||||
} finally {
|
||||
projectsLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const loadProjectResult = async () => {
|
||||
if (!projectCode.value) return;
|
||||
resultLoading.value = true;
|
||||
resultError.value = false;
|
||||
try {
|
||||
const response = await getProjectWorkResult({
|
||||
projectCode: projectCode.value,
|
||||
workDate: workDate.value,
|
||||
pageNum: pageNum.value,
|
||||
pageSize
|
||||
});
|
||||
projectResult.value = response.data;
|
||||
} catch {
|
||||
projectResult.value = undefined;
|
||||
resultError.value = true;
|
||||
} finally {
|
||||
resultLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const resetAndLoad = () => {
|
||||
pageNum.value = 1;
|
||||
return loadProjectResult();
|
||||
};
|
||||
|
||||
const changePage = (page: number) => {
|
||||
pageNum.value = page;
|
||||
return loadProjectResult();
|
||||
};
|
||||
|
||||
const refresh = async () => {
|
||||
await loadProjects();
|
||||
if (projectCode.value) await loadProjectResult();
|
||||
};
|
||||
|
||||
watch(workDate, () => {
|
||||
projectResult.value = undefined;
|
||||
pageNum.value = 1;
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
await loadProjects();
|
||||
if (projectCode.value) await loadProjectResult();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.work-results-page {
|
||||
min-height: calc(100vh - 84px);
|
||||
padding: 24px 28px 40px;
|
||||
background: #f4f6fa;
|
||||
color: #172033;
|
||||
}
|
||||
|
||||
.page-head,
|
||||
.filter-card,
|
||||
.section-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.page-head,
|
||||
.section-title {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.page-head {
|
||||
align-items: flex-start;
|
||||
margin-bottom: 18px;
|
||||
|
||||
h1,
|
||||
p,
|
||||
h2 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 6px;
|
||||
color: #858991;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
.section-title p,
|
||||
.muted {
|
||||
margin-top: 6px;
|
||||
color: #858991;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.data-alert {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.filter-card,
|
||||
.table-card {
|
||||
padding: 18px;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.filter-card {
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.project-select {
|
||||
min-width: 280px;
|
||||
}
|
||||
|
||||
.metric-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.member-pagination {
|
||||
justify-content: flex-end;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.metric-card {
|
||||
padding: 16px 18px;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
|
||||
span,
|
||||
strong {
|
||||
display: block;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #858991;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
strong {
|
||||
margin-top: 8px;
|
||||
color: #253044;
|
||||
font-size: 26px;
|
||||
}
|
||||
|
||||
&.warning strong {
|
||||
color: #d97706;
|
||||
}
|
||||
|
||||
&.danger strong {
|
||||
color: #d92d20;
|
||||
}
|
||||
}
|
||||
|
||||
.table-card {
|
||||
.section-title {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
:deep(.el-table th.el-table__cell) {
|
||||
color: #667085;
|
||||
font-weight: 500;
|
||||
background: #fafbfc;
|
||||
}
|
||||
}
|
||||
|
||||
.member-details {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
padding: 4px 16px 12px;
|
||||
}
|
||||
|
||||
.result-section {
|
||||
min-width: 0;
|
||||
padding: 12px;
|
||||
border-radius: 6px;
|
||||
background: #fafbfc;
|
||||
}
|
||||
|
||||
.result-list {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.result-item {
|
||||
span,
|
||||
small {
|
||||
display: block;
|
||||
}
|
||||
|
||||
span {
|
||||
overflow-wrap: anywhere;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
small {
|
||||
margin-top: 3px;
|
||||
color: #858991;
|
||||
}
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
padding: 44px 0;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
@media (max-width: 960px) {
|
||||
.work-results-page {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.filter-card,
|
||||
.page-head {
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.project-select {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.metric-grid,
|
||||
.member-details {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.metric-grid,
|
||||
.member-details {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user