OfficeSystem/components/ContentDashboard.vue
2025-11-05 15:11:30 +08:00

628 lines
14 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<scroll-view class="dashboard-scroll" scroll-y>
<view class="dashboard-content">
<!-- 任务概览 -->
<view class="task-overview">
<view
class="task-card task-card-base"
v-for="item in taskStats"
:key="item.label"
:class="getTaskCardClass(item.label)"
@click="goToTaskList(item.label)"
>
<text class="task-count">{{ item.count }}</text>
<view class="task-label-wrapper">
<text class="task-label">{{ item.label }}</text>
<uv-tags
v-if="item.label === '逾期任务'"
text="紧急"
type="error"
size="mini"
plain
:custom-style="{ marginTop: '4px' }"
></uv-tags>
<uv-tags
v-else-if="item.label === '即将预期'"
text="注意"
type="warning"
size="mini"
plain
:custom-style="{ marginTop: '4px' }"
></uv-tags>
</view>
</view>
</view>
<!-- 逾期任务详情 -->
<view class="overdue-section" v-if="overdueTasks.length > 0">
<view class="overdue-card task-card-base task-card-overdue" v-for="task in overdueTasks" :key="task.id" @click="goToTaskDetail(task)">
<view class="task-header">
<view class="task-badge-wrapper">
<uv-tags text="逾期" type="error" size="mini"></uv-tags>
</view>
<view class="task-date-wrapper">
<text class="task-date">{{ task.date }}</text>
</view>
</view>
<view class="task-content">
<text class="task-project">所属项目: {{ task.project }}</text>
<text class="task-description">{{ task.description }}</text>
<view class="task-meta">
<text class="task-owner">负责人: {{ task.owner }}</text>
<text class="task-time">发布时间: {{ task.releaseTime }}</text>
</view>
</view>
<view class="task-action">
<uv-button type="error" size="small" @click.stop="handleOverdueTask(task)">立即处理</uv-button>
</view>
</view>
<view class="carousel-dots">
<view class="dot" :class="{ active: true }"></view>
<view class="dot"></view>
<view class="dot"></view>
</view>
</view>
<!-- 公告事项 -->
<view class="announcement-section">
<view class="section-header">
<text class="section-icon">📢</text>
<text class="section-title">公告事项</text>
</view>
<view class="announcement-item task-card-base task-card-announcement" v-for="announcement in announcements" :key="announcement.id" @click="viewAnnouncement(announcement)">
<view class="announcement-content">
<text class="announcement-title">{{ announcement.title }}</text>
<text class="announcement-desc">{{ announcement.description }}</text>
<text class="announcement-time">{{ announcement.time }}</text>
</view>
<text class="arrow"></text>
</view>
</view>
<!-- 项目状态 -->
<view class="project-status-section">
<view class="section-header">
<text class="section-icon">💎</text>
<text class="section-title">项目状态</text>
</view>
<view class="status-grid">
<view
class="status-card task-card-base"
v-for="status in projectStatus"
:key="status.label"
:class="getProjectCardClass(status.label)"
>
<text class="status-count">{{ status.count }}</text>
<view class="status-label-wrapper">
<text class="status-label">{{ status.label }}</text>
<uv-tags
:text="getProjectStatusTag(status.label)"
:type="getProjectStatusType(status.label)"
size="mini"
plain
:custom-style="{ marginTop: '4px' }"
></uv-tags>
</view>
</view>
</view>
</view>
<!-- 客户状态 -->
<view class="customer-status-section">
<view class="section-header">
<text class="section-icon">👤</text>
<text class="section-title">客户状态</text>
</view>
<view class="status-grid">
<view
class="status-card task-card-base"
v-for="status in customerStatus"
:key="status.label"
:class="getCustomerCardClass(status.label)"
>
<text class="status-count">{{ status.count }}</text>
<view class="status-label-wrapper">
<text class="status-label">{{ status.label }}</text>
<uv-tags
:text="getCustomerStatusTag(status.label)"
:type="getCustomerStatusType(status.label)"
size="mini"
plain
:custom-style="{ marginTop: '4px' }"
></uv-tags>
</view>
</view>
</view>
</view>
</view>
</scroll-view>
</template>
<script setup>
import { ref } from 'vue';
// 任务统计
const taskStats = ref([
{ label: '完成任务', count: 78 },
{ label: '待完成任务', count: 28 },
{ label: '即将预期', count: 8 },
{ label: '逾期任务', count: 1 }
]);
// 逾期任务
const overdueTasks = ref([
{
id: 1,
date: '2025-10-15',
project: '创特项目管理系统',
description: '项目内容项目内容项目内容项目内容项目内容项目...',
owner: '张珊珊、李志',
releaseTime: '2025-03-21'
}
]);
// 公告事项
const announcements = ref([
{
id: 1,
title: '·国庆放假通知',
description: '国庆放假安排1号至6号,前后不调休...',
time: '2025-09-26 16:54:46'
}
]);
// 项目状态
const projectStatus = ref([
{ label: '运行中', count: 1 },
{ label: '运维中', count: 1 },
{ label: '即将到期', count: 1 },
{ label: '开发超期', count: 1 }
]);
// 客户状态
const customerStatus = ref([
{ label: '今日新增', count: 1 },
{ label: '今日已跟进', count: 1 },
{ label: '今日待跟进', count: 1 },
{ label: '即将跟进', count: 1 }
]);
// 跳转到任务列表页
const goToTaskList = (label) => {
// 将中文标签映射为状态参数
const statusMap = {
'完成任务': 'completed',
'待完成任务': 'pending',
'即将预期': 'imminent',
'逾期任务': 'overdue'
};
const status = statusMap[label] || '';
uni.navigateTo({
url: `/pages/task-list/index?status=${status}&label=${encodeURIComponent(label)}`
});
};
// 跳转到任务详情页
const goToTaskDetail = (task) => {
// 将任务数据存储到本地,供详情页使用
uni.setStorageSync('taskDetailData', {
id: task.id,
name: task.description || '待办任务名称',
project: task.project || '所属项目',
statusTags: ['已逾期', '紧急'],
deadline: task.date || '2025-10-14 18:00',
creator: '张珊珊',
responsible: task.owner || '张珊珊、李志',
publishTime: task.releaseTime || '2025-10-17',
content: task.description || '任务内容任务。这里是详细的任务描述,可以包含多行文本。根据实际需求,这里可以展示任务的详细要求、步骤说明、注意事项等。任务内容应该清晰明了,便于负责人理解和执行。',
submitRecords: []
});
uni.navigateTo({
url: `/pages/task-detail/index?id=${task.id}`
});
};
// 处理逾期任务
const handleOverdueTask = (task) => {
goToTaskDetail(task);
};
// 查看公告
const viewAnnouncement = (announcement) => {
console.log('查看公告:', announcement);
uni.showToast({
title: '查看公告详情',
icon: 'none'
});
};
// 获取项目状态标签类型
const getProjectStatusType = (label) => {
const typeMap = {
'运行中': 'success',
'运维中': 'primary',
'即将到期': 'warning',
'开发超期': 'error'
};
return typeMap[label] || 'primary';
};
// 获取项目状态标签文本
const getProjectStatusTag = (label) => {
const tagMap = {
'运行中': '正常',
'运维中': '进行中',
'即将到期': '待处理',
'开发超期': '超期'
};
return tagMap[label] || '';
};
// 获取客户状态标签类型
const getCustomerStatusType = (label) => {
const typeMap = {
'今日新增': 'success',
'今日已跟进': 'primary',
'今日待跟进': 'warning',
'即将跟进': 'info'
};
return typeMap[label] || 'primary';
};
// 获取客户状态标签文本
const getCustomerStatusTag = (label) => {
const tagMap = {
'今日新增': '新增',
'今日已跟进': '已完成',
'今日待跟进': '待处理',
'即将跟进': '即将'
};
return tagMap[label] || '';
};
// 获取任务卡片样式类
const getTaskCardClass = (label) => {
const classMap = {
'完成任务': 'task-card-completed',
'待完成任务': 'task-card-pending',
'即将预期': 'task-card-imminent',
'逾期任务': 'task-card-overdue'
};
return classMap[label] || '';
};
// 获取项目状态卡片样式类
const getProjectCardClass = (label) => {
const classMap = {
'运行中': 'status-card-success',
'运维中': 'status-card-primary',
'即将到期': 'status-card-warning',
'开发超期': 'status-card-error'
};
return classMap[label] || '';
};
// 获取客户状态卡片样式类
const getCustomerCardClass = (label) => {
const classMap = {
'今日新增': 'status-card-success',
'今日已跟进': 'status-card-primary',
'今日待跟进': 'status-card-warning',
'即将跟进': 'status-card-info'
};
return classMap[label] || '';
};
</script>
<style lang="scss" scoped>
.dashboard-scroll {
width: 100%;
height: 100%;
}
.dashboard-content {
padding: 10px 30rpx;
}
// 统一卡片基础样式
.task-card-base {
background: #fff;
border-radius: 12px;
padding: 16px;
position: relative;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
}
.task-card-base:active {
transform: scale(0.98);
opacity: 0.9;
}
.task-overview {
display: flex;
justify-content: space-between;
margin-top: 10px;
margin-bottom: 20px;
gap: 12px;
}
.task-card {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
// 任务卡片使用较小的padding
&.task-card-base {
padding: 12px 8px;
}
}
// 任务卡片状态样式
.task-card-imminent {
border-left: 4px solid #ff9800;
}
.task-card-pending {
border-left: 4px solid #2885ff;
}
.task-card-completed {
border-left: 4px solid #909399;
opacity: 0.85;
}
.task-card-overdue {
border-left: 4px solid #f56c6c;
}
.task-count {
font-size: 24px;
font-weight: 600;
color: #333;
margin-bottom: 8px;
}
.task-label-wrapper {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
}
.task-label {
font-size: 12px;
color: #666;
}
.overdue-section {
margin-bottom: 20px;
}
.overdue-card {
margin-bottom: 12px;
display: flex;
flex-direction: column;
}
.overdue-card.task-card-overdue {
background: linear-gradient(135deg, #fff5f5 0%, #ffe6e6 100%);
border-left: 4px solid #f56c6c;
box-shadow: 0 2px 12px rgba(255, 68, 68, 0.1);
}
// 任务卡片头部样式(用于逾期卡片)
.task-header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 12px;
}
.task-badge-wrapper {
flex-shrink: 0;
}
.task-date-wrapper {
background: rgba(0, 0, 0, 0.04);
border-radius: 4px;
padding: 4px 8px;
}
.task-date {
font-size: 14px;
color: #333;
font-weight: 500;
}
.task-content {
flex: 1;
display: flex;
flex-direction: column;
gap: 8px;
}
.task-project {
font-size: 12px;
color: #666;
line-height: 1.5;
}
.task-description {
font-size: 14px;
color: #333;
line-height: 1.5;
margin-bottom: 4px;
}
.task-meta {
display: flex;
flex-direction: column;
gap: 4px;
}
.task-owner {
font-size: 12px;
color: #666;
}
.task-time {
font-size: 12px;
color: #666;
}
.task-action {
margin-top: 12px;
display: flex;
justify-content: flex-end;
}
.carousel-dots {
display: flex;
justify-content: center;
gap: 8px;
margin-top: 8px;
}
.dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: #ddd;
}
.dot.active {
background: #2885ff;
}
.announcement-section,
.project-status-section,
.customer-status-section {
margin-top: 8px;
}
.section-header {
display: flex;
align-items: center;
margin-bottom: 12px;
gap: 8px;
}
.section-icon {
font-size: 18px;
}
.section-title {
font-size: 16px;
font-weight: 600;
color: #333;
}
.announcement-item {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 8px;
}
.task-card-announcement {
border-left: 4px solid #2885ff;
}
.announcement-content {
flex: 1;
display: flex;
flex-direction: column;
gap: 8px;
}
.announcement-title {
font-size: 14px;
font-weight: 500;
color: #333;
}
.announcement-desc {
font-size: 12px;
color: #666;
line-height: 1.5;
}
.announcement-time {
font-size: 12px;
color: #999;
}
.arrow {
font-size: 20px;
color: #999;
margin-left: 12px;
}
.status-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 12px;
}
.status-card {
display: flex;
flex-direction: column;
align-items: center;
// 状态卡片使用较小的padding
&.task-card-base {
padding: 12px 8px;
}
}
// 状态卡片边框样式
.status-card-success {
border-left: 4px solid #67c23a;
}
.status-card-primary {
border-left: 4px solid #2885ff;
}
.status-card-warning {
border-left: 4px solid #ff9800;
}
.status-card-error {
border-left: 4px solid #f56c6c;
}
.status-card-info {
border-left: 4px solid #909399;
}
.status-count {
font-size: 20px;
font-weight: 600;
color: #333;
margin-bottom: 8px;
}
.status-label-wrapper {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
width: 100%;
}
.status-label {
font-size: 12px;
color: #666;
text-align: center;
}
</style>