0.5.1 更新首页
This commit is contained in:
parent
3da7554476
commit
a0636c093f
|
@ -11,4 +11,4 @@ VUE_APP_BASE_API = '/prod-api'
|
||||||
VUE_APP_QINIU_DOMAIN = 'https://api.ccttiot.com'
|
VUE_APP_QINIU_DOMAIN = 'https://api.ccttiot.com'
|
||||||
|
|
||||||
# WebSocket地址
|
# WebSocket地址
|
||||||
VUE_APP_WS_HOST = 'wss://pm.chuangtewl.com'
|
VUE_APP_WS_HOST = 'wss://pm.chuangtewl.com/prod-api'
|
|
@ -1,13 +1,5 @@
|
||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
|
|
||||||
// 获取本人概览
|
|
||||||
export function getMineBrief() {
|
|
||||||
return request({
|
|
||||||
url: '/dashboard/mineBrief',
|
|
||||||
method: 'get'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取概览
|
// 获取概览
|
||||||
export function getBrief(params) {
|
export function getBrief(params) {
|
||||||
return request({
|
return request({
|
||||||
|
|
|
@ -43,14 +43,16 @@ export default {
|
||||||
},
|
},
|
||||||
backgroundColor() {
|
backgroundColor() {
|
||||||
const colors = [
|
const colors = [
|
||||||
'#BBDEFB', // 浅蓝色
|
'#90CAF9', // 蓝色
|
||||||
'#E1BEE7', // 浅紫色
|
'#CE93D8', // 紫色
|
||||||
'#C8E6C9', // 浅绿色
|
'#FFB74D', // 橙色
|
||||||
'#FFE0B2', // 浅橙色
|
'#81D4FA', // 天蓝色
|
||||||
'#CFD8DC', // 浅灰蓝色
|
'#F48FB1', // 粉色
|
||||||
'#B3E5FC', // 天蓝色
|
'#E57373', // 红色
|
||||||
'#F8BBD0', // 浅粉色
|
'#9575CD', // 深紫色
|
||||||
'#D7CCC8' // 浅棕色
|
'#4DB6AC', // 青色
|
||||||
|
'#FF8A65', // 珊瑚色
|
||||||
|
'#7986CB' // 靛蓝色
|
||||||
]
|
]
|
||||||
// 将中文字符串转换为数字
|
// 将中文字符串转换为数字
|
||||||
const nameStr = this.name || ''
|
const nameStr = this.name || ''
|
||||||
|
|
121
src/components/Dashboard/StatCard.vue
Normal file
121
src/components/Dashboard/StatCard.vue
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
<template>
|
||||||
|
<el-card class="stat-card" shadow="hover">
|
||||||
|
<div class="stat-header">
|
||||||
|
<div class="stat-title">
|
||||||
|
<i :class="icon" :style="{ color: iconColor }"></i>
|
||||||
|
<span>{{ title }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-main">
|
||||||
|
<div class="main-value">
|
||||||
|
<count-to
|
||||||
|
:start-val="0"
|
||||||
|
:end-val="mainValue"
|
||||||
|
:duration="2000"
|
||||||
|
class="main-count"
|
||||||
|
/>
|
||||||
|
<div class="main-label">{{ mainLabel }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-footer">
|
||||||
|
<div v-for="(item, index) in items" :key="index" class="footer-item">
|
||||||
|
<span class="item-label">{{ item.label }}</span>
|
||||||
|
<count-to
|
||||||
|
:start-val="0"
|
||||||
|
:end-val="item.value"
|
||||||
|
:duration="2000"
|
||||||
|
class="item-value"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import CountTo from 'vue-count-to'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'StatCard',
|
||||||
|
components: {
|
||||||
|
CountTo
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
iconColor: {
|
||||||
|
type: String,
|
||||||
|
default: '#409EFF'
|
||||||
|
},
|
||||||
|
mainValue: {
|
||||||
|
type: Number,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
mainLabel: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
items: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.stat-card {
|
||||||
|
height: 100%;
|
||||||
|
.stat-header {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
.stat-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 16px;
|
||||||
|
i {
|
||||||
|
margin-right: 8px;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.stat-main {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
.main-value {
|
||||||
|
text-align: center;
|
||||||
|
.main-count {
|
||||||
|
font-size: 36px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #303133;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
.main-label {
|
||||||
|
margin-top: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.stat-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
.footer-item {
|
||||||
|
text-align: center;
|
||||||
|
.item-label {
|
||||||
|
display: block;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #909399;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
.item-value {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -110,3 +110,29 @@ export const MessageBstType = {
|
||||||
CUSTOMER: "customer", // 客户
|
CUSTOMER: "customer", // 客户
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 统计数据键值
|
||||||
|
export const BriefKeys = {
|
||||||
|
// 项目
|
||||||
|
PROJECT_STATUS: "projectStatus", // 项目状态
|
||||||
|
PROJECT_DEV_COMPLETED: "projectDevCompleted", // 项目开发完成
|
||||||
|
PROJECT_OVERDUE_DEV_UNCOMPLETED: "projectOverdueDevUncompleted", // 项目逾期开发未完成
|
||||||
|
PROJECT_OVERDUE_DEV_COMPLETED: "projectOverdueDevCompleted", // 项目逾期开发完成
|
||||||
|
PROJECT_DEV_OVERDUE: "projectDevOverdue", // 项目开发逾期
|
||||||
|
PROJECT_DEV_SOON_EXPIRE: "projectDevSoonExpire", // 开发即将到期项目
|
||||||
|
|
||||||
|
// 任务
|
||||||
|
TASK_STATUS: "taskStatus", // 状态数据
|
||||||
|
TASK_TYPE: "taskType", // 类型分组数据
|
||||||
|
TASK_OVERDUE_UNCOMPLETED: "taskOverdueUncompleted", // 逾期未完成
|
||||||
|
TASK_OVERDUE_COMPLETED: "taskOverdueCompleted", // 逾期完成
|
||||||
|
TASK_TODAY_COMPLETED: "taskTodayCompleted", // 今日完成任务
|
||||||
|
TASK_SOON_EXPIRE: "taskSoonExpire", // 即将到期任务
|
||||||
|
|
||||||
|
// 客户
|
||||||
|
CUSTOMER_STATUS: "customerStatus", // 状态分组数据
|
||||||
|
CUSTOMER_TODAY_CREATE: "customerTodayCreate", // 今日新增客户
|
||||||
|
CUSTOMER_TODAY_FOLLOWED: "customerTodayFollowed", // 今日已跟进客户
|
||||||
|
CUSTOMER_TODAY_WAIT_FOLLOW: "customerTodayWaitFollow", // 今日待跟进客户
|
||||||
|
CUSTOMER_SOON_FOLLOW: "customerSoonFollow", // 即将需要跟进的客户
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -222,6 +222,17 @@ export default {
|
||||||
mixins: [$showColumns],
|
mixins: [$showColumns],
|
||||||
dicts: ['customer_intent_level', 'customer_status'],
|
dicts: ['customer_intent_level', 'customer_status'],
|
||||||
components: {FormCol, CustomerLink, CustomerFollowEditDialog},
|
components: {FormCol, CustomerLink, CustomerFollowEditDialog},
|
||||||
|
props: {
|
||||||
|
query: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
|
// 初始化是否显示搜索条件
|
||||||
|
initShowSearch: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
}
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
showFollowDialog: false,
|
showFollowDialog: false,
|
||||||
|
@ -229,7 +240,7 @@ export default {
|
||||||
span: 24,
|
span: 24,
|
||||||
// 字段列表
|
// 字段列表
|
||||||
columns: [
|
columns: [
|
||||||
{key: 'id', visible: true, label: '编号', minWidth: null, sortable: true, overflow: false, align: 'center', width: "80"},
|
{key: 'id', visible: false, label: '编号', minWidth: null, sortable: true, overflow: false, align: 'center', width: "80"},
|
||||||
{key: 'name', visible: true, label: '名称', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
|
{key: 'name', visible: true, label: '名称', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
|
||||||
{key: 'status', visible: true, label: '状态', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
|
{key: 'status', visible: true, label: '状态', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
|
||||||
{key: 'intentLevel', visible: true, label: '意向强度', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
|
{key: 'intentLevel', visible: true, label: '意向强度', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
|
||||||
|
@ -289,6 +300,14 @@ export default {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
|
this.initColumns();
|
||||||
|
this.showSearch = this.initShowSearch;
|
||||||
|
|
||||||
|
this.queryParams = {
|
||||||
|
...this.queryParams,
|
||||||
|
...this.query
|
||||||
|
}
|
||||||
|
|
||||||
this.getList();
|
this.getList();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
@ -99,9 +99,7 @@ export default {
|
||||||
detail: {},
|
detail: {},
|
||||||
queryParams: {
|
queryParams: {
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
pageSize: 5,
|
pageSize: 2,
|
||||||
orderByColumn: 'top',
|
|
||||||
isAsc: 'desc'
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,11 +1,32 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="app-container">
|
<div class="app-container">
|
||||||
<el-row :gutter="12">
|
<el-row :gutter="12">
|
||||||
<el-col :xs="24" :lg="5">
|
<el-col :xs="24" :lg="19" class="mb8">
|
||||||
|
<el-row :gutter="12" class="mb8">
|
||||||
|
<el-col :xs="24" :lg="6">
|
||||||
|
<notice-panel/>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :lg="18">
|
||||||
<panel-group />
|
<panel-group />
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :xs="24" :lg="14">
|
</el-row>
|
||||||
<project-list-panel/>
|
<el-card>
|
||||||
|
<el-tabs >
|
||||||
|
<el-tab-pane label="项目列表" lazy v-if="checkPermi(['bst:project:list'])">
|
||||||
|
<project :hide-columns="projectHideColumns" :init-show-search="false"/>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="任务列表" lazy v-if="checkPermi(['bst:task:list'])">
|
||||||
|
<task :hide-columns="taskHideColumns" :init-show-search="false"/>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="今日完成任务" lazy v-if="checkPermi(['bst:task:list'])">
|
||||||
|
<task :hide-columns="taskHideColumns" :query="{passDateRange: [today, today]}" :init-show-search="false"/>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="今日新增客户" lazy v-if="checkPermi(['bst:customer:list'])">
|
||||||
|
<customer :hide-columns="customerHideColumns" :query="{createDate: today}" :init-show-search="false"/>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
</el-card>
|
||||||
|
<!-- <project-list-panel/> -->
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :xs="24" :lg="5">
|
<el-col :xs="24" :lg="5">
|
||||||
<el-card class="card-box">
|
<el-card class="card-box">
|
||||||
|
@ -14,7 +35,6 @@
|
||||||
<el-card class="card-box">
|
<el-card class="card-box">
|
||||||
<month-project-chart height="180px" bar-width="50%"/>
|
<month-project-chart height="180px" bar-width="50%"/>
|
||||||
</el-card>
|
</el-card>
|
||||||
<notice-panel/>
|
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</div>
|
</div>
|
||||||
|
@ -26,13 +46,60 @@ import NoticePanel from '@/views/bst/index/components/NoticePanel.vue';
|
||||||
import MonthProjectChart from '@/views/dashboard/MonthProjectChart.vue';
|
import MonthProjectChart from '@/views/dashboard/MonthProjectChart.vue';
|
||||||
import ProjectRateChart from '@/views/dashboard/ProjectRateChart.vue';
|
import ProjectRateChart from '@/views/dashboard/ProjectRateChart.vue';
|
||||||
import ProjectListPanel from './components/ProjectListPanel.vue';
|
import ProjectListPanel from './components/ProjectListPanel.vue';
|
||||||
|
import Project from '@/views/bst/project/index.vue';
|
||||||
|
import Task from '@/views/bst/task/index.vue';
|
||||||
|
import Customer from '@/views/bst/customer/index.vue';
|
||||||
|
import { checkPermi } from '@/utils/permission';
|
||||||
|
import { parseTime } from '@/utils/ruoyi';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Index',
|
name: 'Index',
|
||||||
components: { PanelGroup, NoticePanel, MonthProjectChart, ProjectRateChart, ProjectListPanel },
|
components: { PanelGroup, NoticePanel, MonthProjectChart, ProjectRateChart, ProjectListPanel, Project, Task, Customer },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
today: parseTime(new Date(), '{y}-{m}-{d}'),
|
||||||
|
projectHideColumns: [
|
||||||
|
'id',
|
||||||
|
'no',
|
||||||
|
'status',
|
||||||
|
'customerName',
|
||||||
|
'expireTime',
|
||||||
|
'expectedCompleteDate',
|
||||||
|
'maintenanceEndDate',
|
||||||
|
'ownerName',
|
||||||
|
'followName',
|
||||||
|
'amount',
|
||||||
|
'receivedAmount',
|
||||||
|
'operationAmount',
|
||||||
|
'remark',
|
||||||
|
'createTime'
|
||||||
|
],
|
||||||
|
taskHideColumns: [
|
||||||
|
'id',
|
||||||
|
'no',
|
||||||
|
'status',
|
||||||
|
'level',
|
||||||
|
'type',
|
||||||
|
'createId',
|
||||||
|
'createName',
|
||||||
|
'createTime',
|
||||||
|
'remark',
|
||||||
|
'submitCount',
|
||||||
|
'passCount',
|
||||||
|
],
|
||||||
|
customerHideColumns: [
|
||||||
|
'id',
|
||||||
|
'code',
|
||||||
|
'source',
|
||||||
|
'followName',
|
||||||
|
'remark',
|
||||||
|
'createName',
|
||||||
|
'createTime'
|
||||||
|
],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
methods: {
|
||||||
|
checkPermi,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -153,7 +153,9 @@ export default {
|
||||||
// 处理消息
|
// 处理消息
|
||||||
let message = JSON.parse(event.data);
|
let message = JSON.parse(event.data);
|
||||||
this.showDesktopNotification(message);
|
this.showDesktopNotification(message);
|
||||||
|
if (message.id != null) {
|
||||||
this.unreadCount ++;
|
this.unreadCount ++;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.ws.onclose = (event) => {
|
this.ws.onclose = (event) => {
|
||||||
|
|
|
@ -117,7 +117,7 @@
|
||||||
/
|
/
|
||||||
<el-link class="task-count" type="success" :underline="false">{{ d.row.taskPassCount | dv }}</el-link>
|
<el-link class="task-count" type="success" :underline="false">{{ d.row.taskPassCount | dv }}</el-link>
|
||||||
/
|
/
|
||||||
<el-link class="task-count" type="warning" :underline="false">{{ d.row.taskWaitConfirmCount | dv }}</el-link>
|
<el-link class="task-count" type="warning" :underline="false">{{ d.row.taskProcessCount | dv }}</el-link>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="['no','name'].includes(column.key)">
|
<template v-else-if="['no','name'].includes(column.key)">
|
||||||
<div class="flex-row" style="justify-content: left;">
|
<div class="flex-row" style="justify-content: left;">
|
||||||
|
@ -232,7 +232,12 @@
|
||||||
<!-- 新增任务弹窗 -->
|
<!-- 新增任务弹窗 -->
|
||||||
<task-edit-dialog
|
<task-edit-dialog
|
||||||
:show.sync="showTaskDialog"
|
:show.sync="showTaskDialog"
|
||||||
:init-data="{projectId: row.id}"
|
:init-data="{
|
||||||
|
projectId: row.id,
|
||||||
|
projectOwnerId: row.ownerId,
|
||||||
|
projectFollowId: row.followId,
|
||||||
|
projectMemberIds: row.memberIds
|
||||||
|
}"
|
||||||
@success="getList"
|
@success="getList"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -280,6 +285,10 @@ export default {
|
||||||
initData: {
|
initData: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => {}
|
default: () => {}
|
||||||
|
},
|
||||||
|
initShowSearch: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
@ -309,7 +318,7 @@ export default {
|
||||||
{key: 'remark', visible: true, label: '备注', minWidth: null, sortable: true, overflow: true, align: 'center', width: null},
|
{key: 'remark', visible: true, label: '备注', minWidth: null, sortable: true, overflow: true, align: 'center', width: null},
|
||||||
{key: 'time', visible: true, label: '截止时间', minWidth: null, sortable: false, overflow: false, align: 'center', width: "100"},
|
{key: 'time', visible: true, label: '截止时间', minWidth: null, sortable: false, overflow: false, align: 'center', width: "100"},
|
||||||
{key: 'createTime', visible: false, label: '创建时间', minWidth: null, sortable: true, overflow: false, align: 'center', width: "100"},
|
{key: 'createTime', visible: false, label: '创建时间', minWidth: null, sortable: true, overflow: false, align: 'center', width: "100"},
|
||||||
{key: 'taskCount', visible: true, label: '任务(总数/已通过/待审核)', minWidth: null, sortable: true, overflow: false, align: 'center', width: "200"},
|
{key: 'taskCount', visible: true, label: '任务(总数/已通过/进行中)', minWidth: null, sortable: false, overflow: false, align: 'center', width: "200"},
|
||||||
],
|
],
|
||||||
// 排序方式
|
// 排序方式
|
||||||
orderSorts: ['ascending', 'descending', null],
|
orderSorts: ['ascending', 'descending', null],
|
||||||
|
@ -350,6 +359,7 @@ export default {
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.initColumns();
|
this.initColumns();
|
||||||
|
this.showSearch = this.initShowSearch;
|
||||||
|
|
||||||
this.queryParams = {
|
this.queryParams = {
|
||||||
...this.queryParams,
|
...this.queryParams,
|
||||||
|
|
|
@ -39,6 +39,6 @@ export const ProjectUtils = {
|
||||||
const expireTime = new Date(value);
|
const expireTime = new Date(value);
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const days = (expireTime - now) / 24 / 60 / 60 / 1000;
|
const days = (expireTime - now) / 24 / 60 / 60 / 1000;
|
||||||
return days <= 7 && days > 0;
|
return days <= 2 && days > 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
43
src/views/bst/task/components/TaskStatistics.vue
Normal file
43
src/views/bst/task/components/TaskStatistics.vue
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
<template>
|
||||||
|
<el-alert title="任务统计" type="info" v-loading="loading">
|
||||||
|
今日完成:{{ brief.task.todayCompleted | dv }}
|
||||||
|
</el-alert>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getBrief } from '@/api/dashboard/dashboard'
|
||||||
|
import { BriefKeys } from '@/utils/enums'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'TaskStatistics',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
brief: {
|
||||||
|
task: {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getBrief()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getBrief() {
|
||||||
|
this.loading = true
|
||||||
|
getBrief({ keys: [
|
||||||
|
BriefKeys.TASK_TODAY_COMPLETED,
|
||||||
|
]}).then(res => {
|
||||||
|
if (res.code === 200 && res.data) {
|
||||||
|
this.brief = res.data
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
</style>
|
|
@ -41,6 +41,17 @@
|
||||||
<el-radio-button :label="false">正常</el-radio-button>
|
<el-radio-button :label="false">正常</el-radio-button>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="完成日期" prop="passDateRange" v-if="isShow('expireTime')">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="queryParams.passDateRange"
|
||||||
|
type="daterange"
|
||||||
|
range-separator="至"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
value-format="yyyy-MM-dd"
|
||||||
|
@change="handleQuery"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item label="创建人" prop="createId" v-if="isShow('createName')">
|
<el-form-item label="创建人" prop="createId" v-if="isShow('createName')">
|
||||||
<user-select v-model="queryParams.createId" @change="handleQuery"/>
|
<user-select v-model="queryParams.createId" @change="handleQuery"/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
@ -88,6 +99,10 @@
|
||||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList" :columns="columns"></right-toolbar>
|
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList" :columns="columns"></right-toolbar>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
|
<el-row class="mb8">
|
||||||
|
<task-statistics />
|
||||||
|
</el-row>
|
||||||
|
|
||||||
<el-table v-loading="loading" :data="taskList" @selection-change="handleSelectionChange" :default-sort="defaultSort" @sort-change="onSortChange">
|
<el-table v-loading="loading" :data="taskList" @selection-change="handleSelectionChange" :default-sort="defaultSort" @sort-change="onSortChange">
|
||||||
<el-table-column type="selection" width="55" align="center" />
|
<el-table-column type="selection" width="55" align="center" />
|
||||||
<template v-for="column of showColumns">
|
<template v-for="column of showColumns">
|
||||||
|
@ -119,7 +134,7 @@
|
||||||
<template v-else-if="column.key === 'projectName'">
|
<template v-else-if="column.key === 'projectName'">
|
||||||
<project-link :id="d.row.projectId" :text="d.row.projectName"/>
|
<project-link :id="d.row.projectId" :text="d.row.projectName"/>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="column.key === 'createTime'">
|
<template v-else-if="column.key === 'expireTime'">
|
||||||
创建:{{d.row.createTime | dv}}<br/>
|
创建:{{d.row.createTime | dv}}<br/>
|
||||||
截止:{{d.row.expireTime | dv}}<br/>
|
截止:{{d.row.expireTime | dv}}<br/>
|
||||||
完成:{{d.row.passTime | dv}}<br/>
|
完成:{{d.row.passTime | dv}}<br/>
|
||||||
|
@ -135,7 +150,7 @@
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</template>
|
</template>
|
||||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="180" fixed="right">
|
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="120" fixed="right">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-button
|
<el-button
|
||||||
size="mini"
|
size="mini"
|
||||||
|
@ -200,6 +215,7 @@ import AvatarList from '@/components/AvatarList/index.vue';
|
||||||
import UserSelect from '@/components/Business/User/UserSelect.vue';
|
import UserSelect from '@/components/Business/User/UserSelect.vue';
|
||||||
import {TaskStatus} from '@/utils/enums'
|
import {TaskStatus} from '@/utils/enums'
|
||||||
import BooleanTag from '@/components/BooleanTag'
|
import BooleanTag from '@/components/BooleanTag'
|
||||||
|
import TaskStatistics from '@/views/bst/task/components/TaskStatistics.vue'
|
||||||
// 默认排序字段
|
// 默认排序字段
|
||||||
const defaultSort = {
|
const defaultSort = {
|
||||||
prop: "createTime",
|
prop: "createTime",
|
||||||
|
@ -210,7 +226,7 @@ export default {
|
||||||
name: "Task",
|
name: "Task",
|
||||||
mixins: [$showColumns, $task],
|
mixins: [$showColumns, $task],
|
||||||
dicts: ['task_status', 'task_level', 'task_type'],
|
dicts: ['task_status', 'task_level', 'task_type'],
|
||||||
components: {FormCol, TaskEditDialog, ProjectSelect, TaskViewDialog, ProjectLink, AvatarList, UserSelect, BooleanTag},
|
components: {FormCol, TaskEditDialog, ProjectSelect, TaskViewDialog, ProjectLink, AvatarList, UserSelect, BooleanTag, TaskStatistics},
|
||||||
props: {
|
props: {
|
||||||
initData: {
|
initData: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -219,6 +235,10 @@ export default {
|
||||||
query: {
|
query: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({})
|
default: () => ({})
|
||||||
|
},
|
||||||
|
initShowSearch: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
@ -235,7 +255,7 @@ export default {
|
||||||
{key: 'createName', visible: true, label: '创建人', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
|
{key: 'createName', visible: true, label: '创建人', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},
|
||||||
{key: 'submitCount', visible: true, label: '提交', minWidth: null, sortable: false, overflow: false, align: 'center', width: "50"},
|
{key: 'submitCount', visible: true, label: '提交', minWidth: null, sortable: false, overflow: false, align: 'center', width: "50"},
|
||||||
{key: 'passCount', visible: true, label: '完成', minWidth: null, sortable: false, overflow: false, align: 'center', width: "50"},
|
{key: 'passCount', visible: true, label: '完成', minWidth: null, sortable: false, overflow: false, align: 'center', width: "50"},
|
||||||
{key: 'createTime', visible: true, label: '时间', minWidth: null, sortable: true, overflow: false, align: 'left', width: "190"},
|
{key: 'expireTime', visible: true, label: '时间', minWidth: null, sortable: true, overflow: false, align: 'left', width: "190"},
|
||||||
],
|
],
|
||||||
// 排序方式
|
// 排序方式
|
||||||
orderSorts: ['ascending', 'descending', null],
|
orderSorts: ['ascending', 'descending', null],
|
||||||
|
@ -264,6 +284,7 @@ export default {
|
||||||
pageSize: 20,
|
pageSize: 20,
|
||||||
orderByColumn: defaultSort.prop,
|
orderByColumn: defaultSort.prop,
|
||||||
isAsc: defaultSort.order,
|
isAsc: defaultSort.order,
|
||||||
|
passDateRange: [],
|
||||||
id: null,
|
id: null,
|
||||||
projectId: null,
|
projectId: null,
|
||||||
name: null,
|
name: null,
|
||||||
|
@ -281,7 +302,7 @@ export default {
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.initColumns();
|
this.initColumns();
|
||||||
|
this.showSearch = this.initShowSearch;
|
||||||
this.queryParams = {
|
this.queryParams = {
|
||||||
...this.queryParams,
|
...this.queryParams,
|
||||||
...this.query
|
...this.query
|
||||||
|
|
|
@ -1,269 +1,67 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="dashboard-panel" v-loading="loading">
|
<div class="dashboard-panel" v-loading="loading">
|
||||||
<!-- 项目统计 -->
|
<el-row :gutter="12">
|
||||||
<el-card class="card-box" header="参与项目">
|
|
||||||
<el-row :gutter="10">
|
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
<div class="stat-card project" :class="{'hover': hoveredCard === 'project-total'}">
|
<stat-card
|
||||||
<div class="card-header">
|
title="任务"
|
||||||
<div class="card-icon">
|
icon="el-icon-s-order"
|
||||||
<i class="el-icon-folder"></i>
|
icon-color="#409EFF"
|
||||||
</div>
|
:main-value="data.task.todayCompleted"
|
||||||
<div class="card-label">合计项目</div>
|
main-label="今日完成任务"
|
||||||
</div>
|
:items="[
|
||||||
<div class="card-value">
|
{ label: '待完成任务', value: data.task.waitCompleted },
|
||||||
<count-to :start-val="0" :end-val="data.project.total || 0" :duration="2000" class="num"/>
|
{ label: '即将到期', value: data.task.soonExpire },
|
||||||
</div>
|
{ label: '逾期任务', value: data.task.overdueUncompleted }
|
||||||
</div>
|
]"
|
||||||
|
/>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
<div class="stat-card project" :class="{'hover': hoveredCard === 'project-progress'}">
|
<stat-card
|
||||||
<div class="card-header">
|
title="项目"
|
||||||
<div class="card-icon">
|
icon="el-icon-s-cooperation"
|
||||||
<i class="el-icon-refresh"></i>
|
icon-color="#67C23A"
|
||||||
</div>
|
:main-value="data.project.inProgress"
|
||||||
<div class="card-label">进行中</div>
|
main-label="进行中项目"
|
||||||
</div>
|
:items="[
|
||||||
<div class="card-value">
|
{ label: '运维中', value: data.project.maintenance },
|
||||||
<count-to :start-val="0" :end-val="data.project.inProgress || 0" :duration="2000" class="num"/>
|
{ label: '即将到期', value: data.project.devSoonExpire },
|
||||||
</div>
|
{ label: '开发超期', value: data.project.devOverdue }
|
||||||
</div>
|
]"
|
||||||
|
/>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
<div class="stat-card project" :class="{'hover': hoveredCard === 'project-completed'}">
|
<stat-card
|
||||||
<div class="card-header">
|
title="客户"
|
||||||
<div class="card-icon">
|
icon="el-icon-user"
|
||||||
<i class="el-icon-check"></i>
|
icon-color="#E6A23C"
|
||||||
</div>
|
:main-value="data.customer.today"
|
||||||
<div class="card-label">已完成</div>
|
main-label="今日新增客户"
|
||||||
</div>
|
:items="[
|
||||||
<div class="card-value">
|
{ label: '今日已跟进', value: data.customer.todayFollowed },
|
||||||
<count-to :start-val="0" :end-val="data.project.completed || 0" :duration="2000" class="num"/>
|
{ label: '今日待跟进', value: data.customer.todayWaitFollow },
|
||||||
</div>
|
{ label: '即将跟进', value: data.customer.soonFollow }
|
||||||
</div>
|
]"
|
||||||
</el-col>
|
/>
|
||||||
<el-col :span="8">
|
|
||||||
<div class="stat-card project" :class="{'hover': hoveredCard === 'project-overdue'}">
|
|
||||||
<div class="card-header">
|
|
||||||
<div class="card-icon">
|
|
||||||
<i class="el-icon-refresh"></i>
|
|
||||||
</div>
|
|
||||||
<div class="card-label">维护中</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-value">
|
|
||||||
<count-to :start-val="0" :end-val="data.project.maintenance || 0" :duration="2000" class="num"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="8">
|
|
||||||
<div class="stat-card project warning" :class="{'hover': hoveredCard === 'project-overdue'}">
|
|
||||||
<div class="card-header">
|
|
||||||
<div class="card-icon">
|
|
||||||
<i class="el-icon-warning"></i>
|
|
||||||
</div>
|
|
||||||
<div class="card-label">开发超期</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-value">
|
|
||||||
<count-to :start-val="0" :end-val="data.project.developmentOverdue || 0" :duration="2000" class="num"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="8">
|
|
||||||
<div class="stat-card project warning" :class="{'hover': hoveredCard === 'project-overdue'}">
|
|
||||||
<div class="card-header">
|
|
||||||
<div class="card-icon">
|
|
||||||
<i class="el-icon-warning"></i>
|
|
||||||
</div>
|
|
||||||
<div class="card-label">维护到期</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-value">
|
|
||||||
<count-to :start-val="0" :end-val="data.project.maintenanceOverdue || 0" :duration="2000" class="num"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</el-card>
|
|
||||||
|
|
||||||
|
|
||||||
<!-- 任务统计 -->
|
|
||||||
<el-card class="card-box" header="负责任务">
|
|
||||||
<el-row :gutter="10">
|
|
||||||
<el-col :span="8">
|
|
||||||
<div class="stat-card task" :class="{'hover': hoveredCard === 'task-total'}">
|
|
||||||
<div class="card-header">
|
|
||||||
<div class="card-icon">
|
|
||||||
<i class="el-icon-tickets"></i>
|
|
||||||
</div>
|
|
||||||
<div class="card-label">总任务</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-value">
|
|
||||||
<count-to :start-val="0" :end-val="data.task.total || 0" :duration="2000" class="num"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="8">
|
|
||||||
<div class="stat-card task" :class="{'hover': hoveredCard === 'task-wait'}">
|
|
||||||
<div class="card-header">
|
|
||||||
<div class="card-icon">
|
|
||||||
<i class="el-icon-timer"></i>
|
|
||||||
</div>
|
|
||||||
<div class="card-label">待完成</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-value">
|
|
||||||
<count-to :start-val="0" :end-val="data.task.waitCompleted || 0" :duration="2000" class="num"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="8">
|
|
||||||
<div class="stat-card task" :class="{'hover': hoveredCard === 'task-progress'}">
|
|
||||||
<div class="card-header">
|
|
||||||
<div class="card-icon">
|
|
||||||
<i class="el-icon-refresh"></i>
|
|
||||||
</div>
|
|
||||||
<div class="card-label">进行中</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-value">
|
|
||||||
<count-to :start-val="0" :end-val="data.task.inProgress || 0" :duration="2000" class="num"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="8">
|
|
||||||
<div class="stat-card task" :class="{'hover': hoveredCard === 'task-confirm'}">
|
|
||||||
<div class="card-header">
|
|
||||||
<div class="card-icon">
|
|
||||||
<i class="el-icon-document-checked"></i>
|
|
||||||
</div>
|
|
||||||
<div class="card-label">待审核</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-value">
|
|
||||||
<count-to :start-val="0" :end-val="data.task.waitConfirm || 0" :duration="2000" class="num"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="8">
|
|
||||||
<div class="stat-card task warning" :class="{'hover': hoveredCard === 'task-confirm'}">
|
|
||||||
<div class="card-header">
|
|
||||||
<div class="card-icon">
|
|
||||||
<i class="el-icon-warning"></i>
|
|
||||||
</div>
|
|
||||||
<div class="card-label">已驳回</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-value">
|
|
||||||
<count-to :start-val="0" :end-val="data.task.reject || 0" :duration="2000" class="num"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="8">
|
|
||||||
<div class="stat-card task success" :class="{'hover': hoveredCard === 'task-completed'}">
|
|
||||||
<div class="card-header">
|
|
||||||
<div class="card-icon">
|
|
||||||
<i class="el-icon-circle-check"></i>
|
|
||||||
</div>
|
|
||||||
<div class="card-label">已完成</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-value">
|
|
||||||
<count-to :start-val="0" :end-val="data.task.completed || 0" :duration="2000" class="num"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</el-card>
|
|
||||||
|
|
||||||
<!-- 客户统计 -->
|
|
||||||
<el-card class="card-box" header="我的客户">
|
|
||||||
<el-row :gutter="10">
|
|
||||||
<el-col :span="8">
|
|
||||||
<div class="stat-card customer" :class="{'hover': hoveredCard === 'customer-total'}">
|
|
||||||
<div class="card-header">
|
|
||||||
<div class="card-icon">
|
|
||||||
<i class="el-icon-user"></i>
|
|
||||||
</div>
|
|
||||||
<div class="card-label">总客户</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-value">
|
|
||||||
<count-to :start-val="0" :end-val="data.customer.total || 0" :duration="2000" class="num"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="8">
|
|
||||||
<div class="stat-card customer" :class="{'hover': hoveredCard === 'customer-today'}">
|
|
||||||
<div class="card-header">
|
|
||||||
<div class="card-icon">
|
|
||||||
<i class="el-icon-date"></i>
|
|
||||||
</div>
|
|
||||||
<div class="card-label">今日新增</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-value">
|
|
||||||
<count-to :start-val="0" :end-val="data.customer.today || 0" :duration="2000" class="num"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="8">
|
|
||||||
<div class="stat-card customer" :class="{'hover': hoveredCard === 'customer-potential'}">
|
|
||||||
<div class="card-header">
|
|
||||||
<div class="card-icon">
|
|
||||||
<i class="el-icon-user-solid"></i>
|
|
||||||
</div>
|
|
||||||
<div class="card-label">潜在客户</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-value">
|
|
||||||
<count-to :start-val="0" :end-val="data.customer.potential || 0" :duration="2000" class="num"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="8">
|
|
||||||
<div class="stat-card customer" :class="{'hover': hoveredCard === 'customer-intention'}">
|
|
||||||
<div class="card-header">
|
|
||||||
<div class="card-icon">
|
|
||||||
<i class="el-icon-star-on"></i>
|
|
||||||
</div>
|
|
||||||
<div class="card-label">意向客户</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-value">
|
|
||||||
<count-to :start-val="0" :end-val="data.customer.intention || 0" :duration="2000" class="num"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="8">
|
|
||||||
<div class="stat-card customer success" :class="{'hover': hoveredCard === 'customer-transaction'}">
|
|
||||||
<div class="card-header">
|
|
||||||
<div class="card-icon">
|
|
||||||
<i class="el-icon-shopping-cart-full"></i>
|
|
||||||
</div>
|
|
||||||
<div class="card-label">成交客户</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-value">
|
|
||||||
<count-to :start-val="0" :end-val="data.customer.transaction || 0" :duration="2000" class="num"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="8">
|
|
||||||
<div class="stat-card customer warning" :class="{'hover': hoveredCard === 'customer-invalid'}">
|
|
||||||
<div class="card-header">
|
|
||||||
<div class="card-icon">
|
|
||||||
<i class="el-icon-close"></i>
|
|
||||||
</div>
|
|
||||||
<div class="card-label">失效客户</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-value">
|
|
||||||
<count-to :start-val="0" :end-val="data.customer.invalid || 0" :duration="2000" class="num"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</el-card>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import CountTo from 'vue-count-to'
|
import CountTo from 'vue-count-to'
|
||||||
import {getMineBrief} from "@/api/dashboard/dashboard";
|
import StatCard from '@/components/Dashboard/StatCard'
|
||||||
|
import { getBrief } from "@/api/dashboard/dashboard"
|
||||||
|
import { BriefKeys } from '@/utils/enums'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
name: 'PanelGroup',
|
||||||
|
components: {
|
||||||
|
CountTo,
|
||||||
|
StatCard
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading: false,
|
loading: false,
|
||||||
hoveredCard: '',
|
|
||||||
data: {
|
data: {
|
||||||
project: {},
|
project: {},
|
||||||
task: {},
|
task: {},
|
||||||
|
@ -271,177 +69,33 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
components: {
|
|
||||||
CountTo
|
|
||||||
},
|
|
||||||
created() {
|
created() {
|
||||||
this.getData()
|
this.getData()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getData() {
|
getData() {
|
||||||
this.loading = true;
|
this.loading = true
|
||||||
getMineBrief().then(res => {
|
getBrief({
|
||||||
this.data = res.data;
|
keys: [
|
||||||
|
BriefKeys.TASK_STATUS,
|
||||||
|
BriefKeys.TASK_TODAY_COMPLETED,
|
||||||
|
BriefKeys.TASK_SOON_EXPIRE,
|
||||||
|
BriefKeys.TASK_OVERDUE_UNCOMPLETED,
|
||||||
|
BriefKeys.PROJECT_STATUS,
|
||||||
|
BriefKeys.PROJECT_DEV_SOON_EXPIRE,
|
||||||
|
BriefKeys.PROJECT_DEV_OVERDUE,
|
||||||
|
BriefKeys.CUSTOMER_TODAY_CREATE,
|
||||||
|
BriefKeys.CUSTOMER_TODAY_FOLLOWED,
|
||||||
|
BriefKeys.CUSTOMER_TODAY_WAIT_FOLLOW,
|
||||||
|
BriefKeys.CUSTOMER_SOON_FOLLOW,
|
||||||
|
]
|
||||||
|
}).then(res => {
|
||||||
|
this.data = res.data
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
this.loading = false;
|
this.loading = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.dashboard-panel {
|
|
||||||
|
|
||||||
.stat-card {
|
|
||||||
background: #fff;
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 10px;
|
|
||||||
text-align: left;
|
|
||||||
box-shadow: 0 1px 4px rgba(0,0,0,.1);
|
|
||||||
transition: all .3s;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
cursor: pointer;
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
z-index: 0;
|
|
||||||
opacity: 1;
|
|
||||||
transition: opacity .3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover::before {
|
|
||||||
opacity: 1.2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 6px;
|
|
||||||
position: relative;
|
|
||||||
z-index: 1;
|
|
||||||
|
|
||||||
.card-icon {
|
|
||||||
margin-right: 6px;
|
|
||||||
i {
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-label {
|
|
||||||
font-size: 13px;
|
|
||||||
color: #909399;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-value {
|
|
||||||
position: relative;
|
|
||||||
z-index: 1;
|
|
||||||
.num {
|
|
||||||
font-size: 22px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #1f2f3d;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.project {
|
|
||||||
&::before {
|
|
||||||
background: linear-gradient(135deg, rgba(128, 100, 255, 0.08) 0%, rgba(255, 255, 255, 0) 45%, rgba(128, 100, 255, 0.1) 100%);
|
|
||||||
}
|
|
||||||
.card-icon {
|
|
||||||
color: #8064ff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.task {
|
|
||||||
&::before {
|
|
||||||
background: linear-gradient(135deg, rgba(255, 156, 110, 0.08) 0%, rgba(255, 255, 255, 0) 45%, rgba(255, 156, 110, 0.1) 100%);
|
|
||||||
}
|
|
||||||
.card-icon {
|
|
||||||
color: #ff9c6e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.customer {
|
|
||||||
&::before {
|
|
||||||
background: linear-gradient(135deg, rgba(32, 201, 151, 0.08) 0%, rgba(255, 255, 255, 0) 45%, rgba(32, 201, 151, 0.1) 100%);
|
|
||||||
}
|
|
||||||
.card-icon {
|
|
||||||
color: #20c997;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.warning {
|
|
||||||
&::before {
|
|
||||||
background: linear-gradient(135deg, rgba(255, 77, 79, 0.08) 0%, rgba(255, 255, 255, 0) 45%, rgba(255, 77, 79, 0.1) 100%);
|
|
||||||
}
|
|
||||||
.card-icon {
|
|
||||||
color: #ff4d4f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.success {
|
|
||||||
&::before {
|
|
||||||
background: linear-gradient(135deg, rgba(82, 196, 26, 0.08) 0%, rgba(255, 255, 255, 0) 45%, rgba(82, 196, 26, 0.1) 100%);
|
|
||||||
}
|
|
||||||
.card-icon {
|
|
||||||
color: #52c41a;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover, &.hover {
|
|
||||||
transform: translateY(-2px);
|
|
||||||
box-shadow: 0 4px 12px rgba(0,0,0,.15);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 768px) {
|
|
||||||
.dashboard-panel {
|
|
||||||
padding: 10px;
|
|
||||||
|
|
||||||
.stat-section {
|
|
||||||
margin-bottom: 15px;
|
|
||||||
|
|
||||||
.section-title {
|
|
||||||
font-size: 14px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-card {
|
|
||||||
padding: 8px;
|
|
||||||
|
|
||||||
.card-header {
|
|
||||||
margin-bottom: 4px;
|
|
||||||
|
|
||||||
.card-icon {
|
|
||||||
i {
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-label {
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-value {
|
|
||||||
.num {
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -87,6 +87,7 @@
|
||||||
<script>
|
<script>
|
||||||
import StatisticsCard from '@/components/StatisticsCard'
|
import StatisticsCard from '@/components/StatisticsCard'
|
||||||
import { getBrief } from '@/api/dashboard/dashboard'
|
import { getBrief } from '@/api/dashboard/dashboard'
|
||||||
|
import { BriefKeys } from '@/utils/enums'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'UserStatistics',
|
name: 'UserStatistics',
|
||||||
|
@ -112,7 +113,16 @@ export default {
|
||||||
methods: {
|
methods: {
|
||||||
getBrief() {
|
getBrief() {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
getBrief({ joinUserId: this.userId }).then(res => {
|
getBrief({ joinUserId: this.userId, keys: [
|
||||||
|
BriefKeys.PROJECT_STATUS,
|
||||||
|
BriefKeys.PROJECT_DEV_COMPLETED,
|
||||||
|
BriefKeys.PROJECT_OVERDUE_DEV_UNCOMPLETED,
|
||||||
|
BriefKeys.PROJECT_OVERDUE_DEV_COMPLETED,
|
||||||
|
BriefKeys.TASK_STATUS,
|
||||||
|
BriefKeys.TASK_TYPE,
|
||||||
|
BriefKeys.TASK_OVERDUE_UNCOMPLETED,
|
||||||
|
BriefKeys.TASK_OVERDUE_COMPLETED,
|
||||||
|
]}).then(res => {
|
||||||
if (res.code === 200 && res.data) {
|
if (res.code === 200 && res.data) {
|
||||||
this.task = res.data.task
|
this.task = res.data.task
|
||||||
this.project = res.data.project
|
this.project = res.data.project
|
||||||
|
|
Loading…
Reference in New Issue
Block a user