From 8b92f8cb5763eaf8a8a5d93a4690c923c2de1a85 Mon Sep 17 00:00:00 2001
From: chenyao <1219716595@qq.com>
Date: Thu, 03 Apr 2025 14:34:31 +0800
Subject: [PATCH] feat: 任务管理功能
---
src/views/TaskManage/components/TaskLeft/TaskLeft.vue | 19 +
src/views/TaskManage/components/TaskLeft/TaskTotal.vue | 100 +++++
src/views/TaskManage/components/TaskRight/TaskRight.vue | 25 +
src/store/modules/user.js | 1
src/api/home/task.js | 52 +++
src/api/home/common.js | 27 +
src/assets/images/signMachineNest/return.png | 0
src/views/TaskManage/components/TaskRight/TaskEvent.vue | 150 ++++++++
src/views/TaskManage/components/TaskRight/TaskTime.vue | 125 +++++++
src/views/TaskManage/components/SearchBox.vue | 192 +++++++++++
src/views/SignMachineNest/components/MachineLeft/MachineLeft.vue | 22 +
src/views/TaskManage/TaskManage.vue | 9
src/views/TaskManage/components/TaskLeft/TaskIndustry.vue | 132 +++++++
src/views/TaskManage/components/TaskIntermediateContent/TaskIntermediateContent.vue | 148 ++++++++
14 files changed, 998 insertions(+), 4 deletions(-)
diff --git a/src/api/home/common.js b/src/api/home/common.js
new file mode 100644
index 0000000..367f845
--- /dev/null
+++ b/src/api/home/common.js
@@ -0,0 +1,27 @@
+import request from '@/axios'
+
+// 字典查询 行业 算法 工单类型
+export const getDictionary = params => {
+ return request({
+ url: `/blade-system/dict-biz/listByCodes?code=${params}`,
+ method: 'get',
+ })
+}
+
+// 区域
+export const deptsByAreaCode = params => {
+ return request({
+ url: `/blade-system/dept/deptsByAreaCode`,
+ method: 'get',
+ params,
+ })
+}
+
+// 获取机场信息
+export const getDockInfo = params => {
+ return request({
+ url: `/drone-device-core/dp/home/getDockInfo`,
+ method: 'get',
+ params,
+ })
+}
diff --git a/src/api/home/task.js b/src/api/home/task.js
new file mode 100644
index 0000000..3994d30
--- /dev/null
+++ b/src/api/home/task.js
@@ -0,0 +1,52 @@
+import request from '@/axios'
+
+// 任务总数统计
+export const totalJobNum = params => {
+ return request({
+ url: '/drone-device-core/wayline/waylineJobInfo/totalJobNum',
+ method: 'get',
+ params: params,
+ })
+}
+// 其他任务统计
+export const jobStatistics = data => {
+ return request({
+ url: '/drone-device-core/wayline/waylineJobInfo/jobStatistics',
+ method: 'post',
+ data: data,
+ })
+}
+// 行业任务统计
+export const industryJobNumPieChart = data => {
+ return request({
+ url: '/drone-device-core/wayline/waylineJobInfo/industryJobNumPieChart',
+ method: 'post',
+ data: data,
+ })
+}
+// 时间任务统计
+export const jobNumBar = data => {
+ return request({
+ url: '/drone-device-core/wayline/waylineJobInfo/jobNumBar',
+ method: 'post',
+ data: data,
+ })
+}
+// 事件任务统计
+export const jobEventBar = data => {
+ return request({
+ url: '/drone-device-core/wayline/waylineJobInfo/jobEventBar',
+ method: 'post',
+ data: data,
+ })
+}
+// 任务列表
+export const jobList = data => {
+ return request({
+ url: '/drone-device-core/wayline/waylineJobInfo/jobList?current=' + data.current + '&size=' + data.size,
+ method: 'post',
+ data: {},
+ })
+}
+
+
diff --git a/src/assets/images/signMachineNest/return.png b/src/assets/images/signMachineNest/return.png
new file mode 100644
index 0000000..5e98312
--- /dev/null
+++ b/src/assets/images/signMachineNest/return.png
Binary files differ
diff --git a/src/store/modules/user.js b/src/store/modules/user.js
index 16ec216..9d50b4d 100644
--- a/src/store/modules/user.js
+++ b/src/store/modules/user.js
@@ -299,7 +299,6 @@
setToken(token)
state.token = token
setStore({ name: 'token', content: state.token })
- console.log('顶顶顶111', token)
window.localStorage.setItem(ELocalStorageKey.Token, token)
},
SET_REFRESH_TOKEN: (state, refreshToken) => {
diff --git a/src/views/SignMachineNest/components/MachineLeft/MachineLeft.vue b/src/views/SignMachineNest/components/MachineLeft/MachineLeft.vue
index 52adde4..9083755 100644
--- a/src/views/SignMachineNest/components/MachineLeft/MachineLeft.vue
+++ b/src/views/SignMachineNest/components/MachineLeft/MachineLeft.vue
@@ -3,7 +3,10 @@
<!--时间 天气-->
<common-weather></common-weather>
<!-- 机巢数据 -->
- <MachineData></MachineData>
+ <MachineData></MachineData>
+ <div class="do-return" @click="goBack">
+ <img src="@/assets/images/signMachineNest/return.png" alt="" />
+ </div>
<!--巡检任务详情-->
<InspectionRaskDetails></InspectionRaskDetails>
</div>
@@ -13,6 +16,13 @@
import CommonWeather from '@/components/CommonWeather.vue';
import MachineData from './MachineData.vue';
import InspectionRaskDetails from './InspectionRaskDetails.vue';
+import { useRouter } from 'vue-router';
+
+const router = useRouter();
+
+const goBack = () => {
+ router.push('/index');
+};
</script>
<style scoped lang="scss">
@@ -20,5 +30,15 @@
position: absolute;
top: 88px;
color: #e7f5ff;
+ .do-return {
+ position: absolute;
+ top: 56px;
+ right: -50px;
+ cursor: pointer;
+ img {
+ width: 40px;
+ height: 40px;
+ }
+ }
}
</style>
\ No newline at end of file
diff --git a/src/views/TaskManage/TaskManage.vue b/src/views/TaskManage/TaskManage.vue
index d649d9c..b67105d 100644
--- a/src/views/TaskManage/TaskManage.vue
+++ b/src/views/TaskManage/TaskManage.vue
@@ -1,11 +1,16 @@
<script setup>
-
+import TaskLeft from "./components/TaskLeft/TaskLeft.vue";
+import TaskRight from "./components/TaskRight/TaskRight.vue";
+import TaskIntermediateContent from "./components/TaskIntermediateContent/TaskIntermediateContent.vue";
</script>
<template>
- TaskManage
+ <TaskLeft/>
+ <TaskIntermediateContent />
+ <TaskRight/>
</template>
+
<style scoped lang="scss">
</style>
diff --git a/src/views/TaskManage/components/SearchBox.vue b/src/views/TaskManage/components/SearchBox.vue
new file mode 100644
index 0000000..e02299d
--- /dev/null
+++ b/src/views/TaskManage/components/SearchBox.vue
@@ -0,0 +1,192 @@
+<template>
+ <div class="search-box">
+ <el-form :model="searchForm" inline>
+ <el-form-item label="任务名称">
+ <el-input v-model="searchForm.name" placeholder="请输入任务名称" clearable />
+ </el-form-item>
+ <el-form-item label="所属部门">
+ <el-select
+ v-model="searchForm.deptId"
+ placeholder="请选择部门"
+ clearable
+ >
+ <el-tree-select
+ v-model="searchForm.deptId"
+ :data="deptTreeData"
+ node-key="id"
+ :props="{
+ label: 'name',
+ children: 'children'
+ }"
+ check-strictly
+ clearable
+ />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="周期">
+ <el-select v-model="searchForm.status" placeholder="请选择状态" clearable>
+ <el-option label="日" value="日" />
+ <el-option label="周" value="周" />
+ <el-option label="月" value="月" />
+ <el-option label="年" value="年" />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="任务状态">
+ <el-select v-model="searchForm.status" placeholder="请选择状态" clearable>
+ <el-option v-for="item in statusOptions" :key="item.value" :label="item.label" :value="item.value" />
+ </el-select>
+ </el-form-item>
+ <el-form-item label="任务时间">
+ <el-date-picker
+ v-model="searchForm.dateRange"
+ type="daterange"
+ range-separator="至"
+ start-placeholder="开始日期"
+ end-placeholder="结束日期"
+ value-format="YYYY-MM-DD"
+ />
+ </el-form-item>
+ <el-form-item label="任务算法">
+ <el-select v-model="searchForm.status" placeholder="请选择状态" clearable>
+ <el-option label="识别火情" value="日" />
+ <el-option label="识别车辆" value="周" />
+ <el-option label="识别船只" value="月" />
+ </el-select>
+ </el-form-item>
+ <el-form-item>
+ <el-button type="primary" @click="handleSearch">查询</el-button>
+ <el-button @click="handleReset">重置</el-button>
+ </el-form-item>
+ </el-form>
+ </div>
+</template>
+<script setup>
+import { getDictionary, deptsByAreaCode, getDockInfo } from '@/api/home/common';
+
+
+const searchForm = reactive({
+ name: '',
+ job_info_num: '',
+ status: '',
+ dateRange: []
+});
+const statusOptions = [
+ { label: '待执行', value: 1 },
+ { label: '执行中', value: 2 },
+ { label: '已完成', value: 3 },
+ { label: '已取消', value: 4 },
+ { label: '执行失败', value: 5 }
+];
+
+// 部门以及机巢
+const deptTreeData = ref([]);
+
+const emit = defineEmits(['search']);
+
+const handleSearch = () => {
+ emit('search', {
+ ...searchForm,
+ begin_time: searchForm.dateRange[0],
+ end_time: searchForm.dateRange[1]
+ });
+};
+
+const handleReset = () => {
+ searchForm.name = '';
+ searchForm.job_info_num = '';
+ searchForm.status = '';
+ searchForm.dateRange = [];
+ handleSearch();
+};
+
+// 请求字典字段
+const requestDictionary = () => {
+ getDictionary('SF,HYLB').then((res) => {
+ if (res.data.code !== 0) {
+ // 处理数据
+ console.log('111111',res.data)
+ }
+ });
+};
+
+// 处理部门数据为树形结构
+const handleDeptData = (data) => {
+ const buildTree = (items, parentId = 0) => {
+ const result = [];
+ for (const item of items) {
+ if (item.parent_id === parentId) {
+ const children = buildTree(items, item.id);
+ if (children.length) {
+ item.children = children;
+ }
+ result.push(item);
+ }
+ }
+ return result;
+ };
+ return buildTree(data);
+};
+// 部门信息
+const getDeptsByAreaCode = () => {
+ deptsByAreaCode().then((res) => {
+ if (res.data.code !== 0) {
+ deptTreeData.value = res.data;
+ // 处理数据
+ console.log('22222',res.data)
+ }
+ });
+};
+
+// 机巢信息
+const requestDockInfo = () => {
+ getDockInfo().then((res) => {
+ if (res.data.code !== 0) {
+ // 处理数据
+ console.log('33333',res.data)
+ }
+ });
+};
+
+
+onMounted(() => {
+ requestDictionary();
+ deptsByAreaCode();
+ requestDockInfo();
+});
+</script>
+
+<style lang="scss" scoped>
+.search-box {
+ position: absolute;
+ top: 120px;
+ left: 450px;
+ width: calc(100% - 400px - 400px - 100px);
+ height:60px;
+ color: #fff;
+ background: rgba(31, 62, 122, 0.35);
+ padding: 10px 20px;
+
+ :deep(.el-form) {
+ --el-text-color-regular: #fff;
+
+ .el-form-item {
+ margin-bottom: 0;
+ margin-right: 20px;
+
+ .el-form-item__label {
+ color: #fff;
+ }
+
+ .el-input,
+ .el-select,
+ .el-date-editor {
+ width: 200px;
+ --el-input-bg-color: transparent;
+ --el-input-border-color: rgba(255, 255, 255, 0.3);
+ --el-input-text-color: #fff;
+ --el-input-placeholder-color: rgba(255, 255, 255, 0.5);
+ }
+ }
+ }
+}
+</style>
\ No newline at end of file
diff --git a/src/views/TaskManage/components/TaskIntermediateContent/TaskIntermediateContent.vue b/src/views/TaskManage/components/TaskIntermediateContent/TaskIntermediateContent.vue
new file mode 100644
index 0000000..ccf669e
--- /dev/null
+++ b/src/views/TaskManage/components/TaskIntermediateContent/TaskIntermediateContent.vue
@@ -0,0 +1,148 @@
+<!-- 任务统计表格 -->
+<template>
+ <SearchBox></SearchBox>
+ <div class="task-intermediate-content">
+ <el-table :data="jobListData" style="width: 100%" height="calc(100vh - 180px)">
+ <el-table-column label="序号" width="60">
+ <template #default="scope">
+ {{ (jobListParams.page - 1) * jobListParams.limit + scope.$index + 1 }}
+ </template>
+ </el-table-column>
+ <el-table-column prop="job_info_num" label="任务编号" />
+ <el-table-column prop="name" label="任务名称" />
+ <el-table-column prop="dept_name" label="所属部门" />
+ <el-table-column prop="device_names" label="所属机巢" />
+ <el-table-column prop="ai_type_str" label="关联算法" />
+ <el-table-column prop="status" label="任务状态" >
+ <template #default="scope">
+ <el-tag :type="getStatusType(scope.row.status)">
+ {{ getStatusText(scope.row.status) }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column prop="industry_type_str" label="任务类型" />
+ <el-table-column prop="event_number" label="关联事件" />
+ <el-table-column prop="begin_time" label="任务时间" />
+ <el-table-column prop="creator_name" label="创建人" />
+ <el-table-column label="操作" >
+ <template #default="scope">
+ <el-button link type="primary" @click="handleDetail(scope.row)">查看</el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ <div class="pagination">
+ <el-pagination
+ v-model:current-page="jobListParams.page"
+ v-model:page-size="jobListParams.limit"
+ :page-sizes="[10, 20, 30, 50]"
+ layout="total, sizes, prev, pager, next"
+ :total="total"
+ @size-change="handleSizeChange"
+ @current-change="handleCurrentChange"
+ />
+ </div>
+ </div>
+</template>
+
+<script setup>
+import SearchBox from '../SearchBox.vue';
+import { jobList } from '@/api/home/task';
+
+const jobListParams = reactive({
+ page: 1,
+ limit: 10,
+});
+const jobListData = ref([]);
+const total = ref(0);
+
+// 获取任务列表
+const getJobList = () => {
+ jobList({current:1,size:10}).then(res => {
+ if (res.data.code !== 0) return;
+ jobListData.value = res.data.data.records;
+ total.value = res.data.data.total;
+ });
+};
+// 状态文字
+const getStatusText = (status) => {
+ const statusMap = {
+ 1: '待执行',
+ 2: '执行中',
+ 3: '已完成',
+ 4: '已取消',
+ 5: '执行失败'
+ };
+ return statusMap[status] || '未知';
+};
+
+// 状态标签类型
+const getStatusType = (status) => {
+ const typeMap = {
+ 1: 'info',
+ 2: 'warning',
+ 3: 'success',
+ 4: '',
+ 5: 'danger'
+ };
+ return typeMap[status] || '';
+};
+
+// 查看详情
+const handleDetail = (row) => {
+ console.log('查看详情', row);
+};
+
+// 分页大小改变
+const handleSizeChange = (val) => {
+ jobListParams.limit = val;
+ getJobList();
+};
+
+// 页码改变
+const handleCurrentChange = (val) => {
+ jobListParams.page = val;
+ getJobList();
+};
+
+onMounted(() => {
+ getJobList();
+});
+</script>
+
+<style lang="scss" scoped>
+.task-intermediate-content {
+ position: absolute;
+ top: 190px;
+ width: calc(100% - 400px - 400px - 100px);
+ left: 450px;
+ height: 770px;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+
+ :deep(.el-table) {
+ background-color: transparent;
+ --el-table-tr-bg-color: transparent;
+ --el-table-border-color: rgba(255, 255, 255, 0.1);
+ --el-table-header-bg-color: rgba(31, 62, 122, 0.5);
+ --el-table-header-text-color: #fff;
+ --el-table-text-color: #fff;
+ .el-table__body tr:hover > td {
+ background-color: rgba(31, 62, 122, 0.3) !important;
+ }
+ }
+
+ .pagination {
+ padding: 20px 0;
+ display: flex;
+ justify-content: flex-end;
+
+ :deep(.el-pagination) {
+ --el-pagination-bg-color: transparent;
+ --el-pagination-text-color: #fff;
+ --el-pagination-button-color: #fff;
+ --el-pagination-hover-color: #409eff;
+ }
+ }
+}
+</style>
\ No newline at end of file
diff --git a/src/views/TaskManage/components/TaskLeft/TaskIndustry.vue b/src/views/TaskManage/components/TaskLeft/TaskIndustry.vue
new file mode 100644
index 0000000..f8f895f
--- /dev/null
+++ b/src/views/TaskManage/components/TaskLeft/TaskIndustry.vue
@@ -0,0 +1,132 @@
+<!-- 业务统计 -->
+ <template>
+ <common-title title="行业统计" :style="{ marginLeft: pxToRem(14) }"></common-title>
+ <div class="task-industry">
+ <div class="chart" ref="chartRef"></div>
+ </div>
+ </template>
+
+<script setup>
+import CommonTitle from '@/components/CommonTitle.vue';
+import * as echarts from 'echarts';
+import { industryJobNumPieChart } from '@/api/home/task';
+const chartRef = ref(null);
+let chart = null;
+
+const option = {
+ tooltip: {
+ trigger: 'item',
+ formatter: '{b}: {c} ({d}%)'
+ },
+ legend: {
+ orient: 'horizontal',
+ left: 'center',
+ bottom: '5%',
+ textStyle: {
+ color: '#fff',
+ fontSize: 12
+ },
+ itemWidth: 10,
+ itemHeight: 10,
+ itemGap: 10,
+ formatter: function(name) {
+ let data = option.series[0].data;
+ let total = 0;
+ let tarValue = 0;
+ for (let i = 0; i < data.length; i++) {
+ total += data[i].value;
+ if (data[i].name === name) {
+ tarValue = data[i].value;
+ }
+ }
+ let percentage = ((tarValue / total) * 100).toFixed(1);
+ return `${name} ${percentage}%`;
+ }
+ },
+ series: [
+ {
+ name: '行业统计',
+ type: 'pie',
+ radius: '60%',
+ center: ['35%', '50%'],
+ itemStyle: {
+ borderRadius: 4,
+ borderColor: 'rgba(255,255,255,0.2)',
+ borderWidth: 1
+ },
+ label: {
+ show: true,
+ position: 'inside',
+ formatter: '{c}',
+ fontSize: 12,
+ color: '#fff'
+ },
+ labelLine: {
+ show: false
+ },
+ emphasis: {
+ scale: true,
+ scaleSize: 10
+ }
+ }
+ ]
+};
+
+// 获取行业统计数据
+const getIndustryJobNumPieChart = () => {
+ industryJobNumPieChart().then(res => {
+ if (res.data.code !== 0) return;
+ const data = res.data.data.map(item => ({
+ name: item.name,
+ value: item.value,
+ itemStyle: {
+ color: getRandomColor()
+ }
+ }));
+ option.series[0].data = data;
+ chart.setOption(option);
+ });
+};
+
+// 生成随机颜色
+const getRandomColor = () => {
+ const colors = ['#1890FF', '#36CBCB', '#4ECB73', '#FBD437', '#F2637B', '#975FE5'];
+ return colors[Math.floor(Math.random() * colors.length)];
+};
+
+onMounted(() => {
+ chart = echarts.init(chartRef.value);
+ getIndustryJobNumPieChart();
+
+ window.addEventListener('resize', () => {
+ chart?.resize();
+ });
+});
+
+onUnmounted(() => {
+ window.removeEventListener('resize', chart?.resize);
+ chart?.dispose();
+});
+</script>
+
+<style lang="scss" scoped>
+.task-industry {
+ font-family: YouSheBiaoTiHei, YouSheBiaoTiHei;
+ margin-left: 29px;
+ padding: 16px 16px;
+ width: 340px;
+ height: 400px;
+ background: linear-gradient(
+ 270deg,
+ rgba(31, 62, 122, 0) 0%,
+ rgba(31, 62, 122, 0.35) 21%,
+ #1f3e7a 100%
+ );
+ border-radius: 0px 0px 0px 0px;
+ opacity: 0.85;
+ .chart {
+ width: 100%;
+ height: 100%;
+ }
+}
+</style>
\ No newline at end of file
diff --git a/src/views/TaskManage/components/TaskLeft/TaskLeft.vue b/src/views/TaskManage/components/TaskLeft/TaskLeft.vue
new file mode 100644
index 0000000..f0117c5
--- /dev/null
+++ b/src/views/TaskManage/components/TaskLeft/TaskLeft.vue
@@ -0,0 +1,19 @@
+<template>
+ <div class="task-left">
+ <TaskTotal />
+ <TaskIndustry />
+ </div>
+</template>
+
+<script setup>
+import TaskTotal from './TaskTotal.vue';
+import TaskIndustry from './TaskIndustry.vue';
+</script>
+
+<style lang="scss" scoped>
+.task-left {
+ // width: 300px;
+ position: relative;
+ top: 20px;
+}
+</style>
\ No newline at end of file
diff --git a/src/views/TaskManage/components/TaskLeft/TaskTotal.vue b/src/views/TaskManage/components/TaskLeft/TaskTotal.vue
new file mode 100644
index 0000000..ad69663
--- /dev/null
+++ b/src/views/TaskManage/components/TaskLeft/TaskTotal.vue
@@ -0,0 +1,100 @@
+<!-- 任务统计 -->
+<template>
+ <common-title title="任务统计" :style="{ marginLeft: pxToRem(14) }"></common-title>
+ <div class="task-total">
+ <div class="card" v-for="item in list">
+ <div>
+ <div class="value">{{ item.value }}</div>
+ <div class="name">{{item.name}}</div>
+ </div>
+ </div>
+ </div>
+</template>
+
+<script setup>
+import { pxToRem } from '@/utils/rem';
+import CommonTitle from '@/components/CommonTitle.vue';
+import { totalJobNum, jobStatistics } from "@/api/home/task";
+
+const list = ref([
+ { name: '总任务数', value: '1888'},
+ { name: '计划执行', value: '18'},
+ { name: '执行中', value: '999'},
+ { name: '待执行', value: '8888'},
+ { name: '已执行', value: '666'},
+ { name: '执行失败', value: '2222'},
+]);
+
+// 获取任务统计总数
+const getTotalJobNum = () => {
+ totalJobNum().then((res) => {
+ if (res.data.code !== 0) returen;
+ list.value[0].value = res.data.data;
+ });
+};
+// 获取其他任务统计
+const getJobStatistics = () => {
+ jobStatistics().then((res) => {
+ if (res.data.code !== 0) returen;
+ list.value[1].value = res.data.data.planned_executions;
+ list.value[2].value = res.data.data.running_num;
+ list.value[3].value = res.data.data.pending_executions;
+ list.value[4].value = res.data.data.executed;
+ list.value[5].value = res.data.data.failed_executions;
+ });
+};
+
+onMounted(() => {
+ getTotalJobNum();
+ getJobStatistics();
+});
+</script>
+
+<style lang="scss" scoped>
+.task-total {
+ font-family: YouSheBiaoTiHei, YouSheBiaoTiHei;
+ margin-left: 29px;
+ padding: 16px 16px;
+ width: 340px;
+ height: 400px;
+ background: linear-gradient(
+ 270deg,
+ rgba(31, 62, 122, 0) 0%,
+ rgba(31, 62, 122, 0.35) 21%,
+ #1f3e7a 100%
+ );
+ border-radius: 0px 0px 0px 0px;
+ opacity: 0.85;
+ margin-bottom: 12px;
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ grid-template-rows: repeat(3, 1fr);
+ gap: 20px;
+ padding: 20px;
+ .card {
+ /* position: absolute;
+ top: 8px;
+ right: 10px;
+ width: 200px; */
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+ justify-content: flex-start;
+ line-height: 22px;
+ padding: 0 10px 10px 0;
+ color: #ffffff;
+ .name {
+ font-family: Source Han Sans CN, Source Han Sans CN;
+ font-weight: 400;
+ font-size: 14px;
+
+ }
+
+ .value {
+ font-family: YouSheBiaoTiHei, YouSheBiaoTiHei;
+ font-weight: 400;
+ font-size: 26px;
+ }
+ }
+}
+</style>
\ No newline at end of file
diff --git a/src/views/TaskManage/components/TaskRight/TaskEvent.vue b/src/views/TaskManage/components/TaskRight/TaskEvent.vue
new file mode 100644
index 0000000..5600ec3
--- /dev/null
+++ b/src/views/TaskManage/components/TaskRight/TaskEvent.vue
@@ -0,0 +1,150 @@
+<!-- 任务事件统计 -->
+<template>
+ <common-title title="任务事件统计"></common-title>
+ <div class="task-event">
+ <div class="chart" ref="chartRef"></div>
+ </div>
+ </template>
+
+<script setup>
+import CommonTitle from '@/components/CommonTitle.vue';
+import * as echarts from 'echarts';
+import { jobEventBar } from '@/api/home/task';
+import dayjs from 'dayjs';
+
+// 日期
+const currenDate = dayjs().format('YYYY-MM-DD');
+const newTime = ref([currenDate, currenDate]);
+// 图表
+const chartRef = ref(null);
+let chart = null;
+
+const option = {
+ tooltip: {
+ trigger: 'axis',
+ axisPointer: {
+ type: 'shadow'
+ }
+ },
+ legend: {
+ data: ['任务', '事件'],
+ textStyle: {
+ color: '#fff'
+ },
+ bottom: '5%'
+ },
+ grid: {
+ top: '15%',
+ left: '3%',
+ right: '4%',
+ bottom: '15%',
+ containLabel: true
+ },
+ xAxis: {
+ type: 'category',
+ axisLine: {
+ lineStyle: {
+ color: '#fff'
+ }
+ },
+ axisLabel: {
+ color: '#fff',
+ interval: 0,
+ rotate: 30
+ }
+ },
+ yAxis: {
+ type: 'value',
+ axisLine: {
+ lineStyle: {
+ color: '#fff'
+ }
+ },
+ splitLine: {
+ lineStyle: {
+ color: 'rgba(255, 255, 255, 0.1)'
+ }
+ },
+ axisLabel: {
+ color: '#fff'
+ }
+ },
+ series: [
+ {
+ name: '任务',
+ type: 'bar',
+ barWidth: '20%',
+ itemStyle: {
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+ { offset: 0, color: '#1EE7E7' },
+ { offset: 1, color: 'rgba(30, 231, 231, 0.1)' }
+ ])
+ }
+ },
+ {
+ name: '事件',
+ type: 'bar',
+ barWidth: '20%',
+ itemStyle: {
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+ { offset: 0, color: '#0070FF' },
+ { offset: 1, color: 'rgba(0, 112, 255, 0.1)' }
+ ])
+ }
+ }
+ ]
+};
+
+// 获取行业统计数据
+const getJobEventBar = (value,date_enum) => {
+ jobEventBar({date_enum}).then(res => {
+ if (res.data.code !== 0) return;
+ option.xAxis.data = res.data.data.map(item => item.name);
+ option.series[0].data = res.data?.data.map(item => item.data[0].value);
+ option.series[1].data = res.data?.data.map(item => item.data[1].value);
+ chart.setOption(option);
+ });
+};
+
+// 生成随机颜色
+const getRandomColor = () => {
+ const colors = ['#1890FF', '#36CBCB', '#4ECB73', '#FBD437', '#F2637B', '#975FE5'];
+ return colors[Math.floor(Math.random() * colors.length)];
+};
+
+onMounted(() => {
+ chart = echarts.init(chartRef.value);
+ getJobEventBar(newTime.value, 'CURRENT_YEAR');
+
+ window.addEventListener('resize', () => {
+ chart?.resize();
+ });
+});
+
+onUnmounted(() => {
+ window.removeEventListener('resize', chart?.resize);
+ chart?.dispose();
+});
+</script>
+
+<style lang="scss" scoped>
+.task-event {
+ font-family: YouSheBiaoTiHei, YouSheBiaoTiHei;
+ margin-left: 29px;
+ padding: 16px 16px;
+ width: 340px;
+ height: 400px;
+ background: linear-gradient(
+ 270deg,
+ rgba(31, 62, 122, 0) 0%,
+ rgba(31, 62, 122, 0.35) 21%,
+ #1f3e7a 100%
+ );
+ border-radius: 0px 0px 0px 0px;
+ opacity: 0.85;
+ .chart {
+ width: 100%;
+ height: 100%;
+ }
+}
+</style>
\ No newline at end of file
diff --git a/src/views/TaskManage/components/TaskRight/TaskRight.vue b/src/views/TaskManage/components/TaskRight/TaskRight.vue
new file mode 100644
index 0000000..7a23af9
--- /dev/null
+++ b/src/views/TaskManage/components/TaskRight/TaskRight.vue
@@ -0,0 +1,25 @@
+<template>
+ <div class="task-right">
+ <TaskTime />
+ <TaskEvent />
+ </div>
+</template>
+
+<script setup>
+import TaskTime from './TaskTime.vue';
+import TaskEvent from './TaskEvent.vue';
+</script>
+
+<style scoped lang="scss">
+.task-right {
+ position: absolute;
+ top: 122px;
+ right: 31px;
+ width: 404px;
+
+ .titleBox {
+ width: 404px;
+ height: 43px;
+ }
+}
+</style>
\ No newline at end of file
diff --git a/src/views/TaskManage/components/TaskRight/TaskTime.vue b/src/views/TaskManage/components/TaskRight/TaskTime.vue
new file mode 100644
index 0000000..48b9495
--- /dev/null
+++ b/src/views/TaskManage/components/TaskRight/TaskTime.vue
@@ -0,0 +1,125 @@
+<!-- 任务事件统计 -->
+<template>
+ <common-title title="任务时间统计"></common-title>
+ <div class="task-time">
+ <div class="chart" ref="chartRef"></div>
+ </div>
+ </template>
+
+<script setup>
+import CommonTitle from '@/components/CommonTitle.vue';
+import * as echarts from 'echarts';
+import { jobNumBar } from '@/api/home/task';
+import dayjs from 'dayjs';
+
+// 日期
+const currenDate = dayjs().format('YYYY-MM-DD');
+const newTime = ref([currenDate, currenDate]);
+
+const chartRef = ref(null);
+let chart = null;
+
+const option = {
+ tooltip: {
+ trigger: 'axis',
+ axisPointer: {
+ type: 'shadow'
+ }
+ },
+ grid: {
+ top: '15%',
+ left: '3%',
+ right: '4%',
+ bottom: '3%',
+ containLabel: true
+ },
+ xAxis: {
+ type: 'category',
+ axisLine: {
+ lineStyle: {
+ color: '#fff'
+ }
+ },
+ axisLabel: {
+ color: '#fff',
+ interval: 0,
+ rotate: 30
+ }
+ },
+ yAxis: {
+ type: 'value',
+ axisLine: {
+ lineStyle: {
+ color: '#fff'
+ }
+ },
+ splitLine: {
+ lineStyle: {
+ color: 'rgba(255, 255, 255, 0.1)'
+ }
+ },
+ axisLabel: {
+ color: '#fff'
+ }
+ },
+ series: [
+ {
+ type: 'bar',
+ barWidth: '40%',
+ itemStyle: {
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+ { offset: 0, color: '#1EE7E7' },
+ { offset: 1, color: 'rgba(30, 231, 231, 0.1)' }
+ ])
+ }
+ }
+ ]
+};
+
+// 获取任务时间统计数据
+const getJobNumBar = (value,date_enum) => {
+ jobNumBar({date_enum}).then(res => {
+ if (res.data.code !== 0) return;
+ option.xAxis.data = res.data.data.map(item => item.name);
+ option.series[0].data = res.data.data.map(item => item.value);
+ chart.setOption(option);
+ });
+};
+
+
+onMounted(() => {
+ chart = echarts.init(chartRef.value);
+ getJobNumBar(newTime.value, 'CURRENT_YEAR');
+
+ window.addEventListener('resize', () => {
+ chart?.resize();
+ });
+});
+
+onUnmounted(() => {
+ window.removeEventListener('resize', chart?.resize);
+ chart?.dispose();
+});
+</script>
+
+<style lang="scss" scoped>
+.task-time {
+ font-family: YouSheBiaoTiHei, YouSheBiaoTiHei;
+ margin-left: 29px;
+ padding: 16px 16px;
+ width: 340px;
+ height: 400px;
+ background: linear-gradient(
+ 270deg,
+ rgba(31, 62, 122, 0) 0%,
+ rgba(31, 62, 122, 0.35) 21%,
+ #1f3e7a 100%
+ );
+ border-radius: 0px 0px 0px 0px;
+ opacity: 0.85;
+ .chart {
+ width: 100%;
+ height: 100%;
+ }
+}
+</style>
\ No newline at end of file
--
Gitblit v1.9.3