merge origin main conflicts resolved
This commit is contained in:
@@ -66,7 +66,14 @@ public class HrmFlowCcController extends BaseController {
|
||||
Long userId = LoginHelper.getUserId();
|
||||
return toAjax(service.markRead(ccId, userId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记抄送未读(新增)
|
||||
*/
|
||||
@PostMapping("/{ccId}/unread")
|
||||
public R<Void> unread(@PathVariable Long ccId) {
|
||||
Long userId = LoginHelper.getUserId();
|
||||
return toAjax(service.markUnread(ccId, userId));
|
||||
}
|
||||
@GetMapping("/ping")
|
||||
public R<String> ping(@RequestParam @NotNull String x) {
|
||||
return R.ok(x);
|
||||
|
||||
@@ -27,5 +27,9 @@ public interface IHrmFlowCcService {
|
||||
* 标记已读
|
||||
*/
|
||||
Boolean markRead(Long ccId, Long userId);
|
||||
/**
|
||||
* 标记未读
|
||||
*/
|
||||
Boolean markUnread(Long ccId, Long userId);
|
||||
}
|
||||
|
||||
|
||||
@@ -99,6 +99,23 @@ public class HrmFlowCcServiceImpl implements IHrmFlowCcService {
|
||||
.eq(HrmFlowCc::getDelFlag, 0)
|
||||
) > 0;
|
||||
}
|
||||
/**
|
||||
* 标记未读
|
||||
*/
|
||||
@Override
|
||||
public Boolean markUnread(Long ccId, Long userId) {
|
||||
if (ccId == null || userId == null) {
|
||||
return false;
|
||||
}
|
||||
return baseMapper.update(
|
||||
null,
|
||||
Wrappers.<HrmFlowCc>lambdaUpdate()
|
||||
.set(HrmFlowCc::getReadFlag, 0)
|
||||
.eq(HrmFlowCc::getCcId, ccId)
|
||||
.eq(HrmFlowCc::getCcUserId, userId)
|
||||
.eq(HrmFlowCc::getDelFlag, 0)
|
||||
) > 0;
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<HrmFlowCc> buildQueryWrapper(HrmFlowCcBo bo) {
|
||||
LambdaQueryWrapper<HrmFlowCc> lqw = Wrappers.lambdaQuery();
|
||||
|
||||
@@ -15,7 +15,6 @@ import com.ruoyi.system.domain.SysOss;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@@ -286,7 +285,20 @@ public class SysOaProjectVo {
|
||||
|
||||
private Long deliveryOrderCount;
|
||||
|
||||
/** 进度步骤总数(列表请求 scheduleStats=true 时由后端填充) */
|
||||
/** 列表 SQL 聚合:任务/进度总览 */
|
||||
@ExcelIgnore
|
||||
private Long taskFinishCount;
|
||||
|
||||
@ExcelIgnore
|
||||
private Long taskTotalCount;
|
||||
|
||||
@ExcelIgnore
|
||||
private Long scheduleTotalCount;
|
||||
|
||||
@ExcelIgnore
|
||||
private Long scheduleFinishCount;
|
||||
|
||||
/** 进度步骤汇总(列表请求 scheduleStats=true 时由服务层填充) */
|
||||
@ExcelIgnore
|
||||
private Long scheduleStepTotal;
|
||||
|
||||
|
||||
@@ -251,6 +251,7 @@ public class SysOaProjectServiceImpl implements ISysOaProjectService {
|
||||
}
|
||||
qw.eq(bo.getCustomerId() != null, "p.customer_id", bo.getCustomerId());
|
||||
qw.orderByDesc("p.is_top").orderByDesc("p.create_time");
|
||||
qw.groupBy("p.project_id");
|
||||
return qw;
|
||||
}
|
||||
|
||||
|
||||
@@ -154,6 +154,7 @@ public class SysOaTaskServiceImpl implements ISysOaTaskService {
|
||||
QueryWrapper<SysOaTask> lqw = Wrappers.query();
|
||||
lqw.eq("sot.del_flag", 0);
|
||||
lqw.eq(bo.getProjectId()!=null, "sot.project_id", bo.getProjectId());
|
||||
lqw.eq(bo.getTaskId()!=null, "sot.task_id", bo.getTaskId());
|
||||
lqw.eq(bo.getCreateUserId()!=null, "sot.create_user_id", bo.getCreateUserId());
|
||||
lqw.eq(bo.getWorkerId()!=null, "sot.worker_id", bo.getWorkerId());
|
||||
lqw.eq(bo.getProjectId()!=null, "sot.project_id", bo.getProjectId());
|
||||
|
||||
@@ -310,9 +310,15 @@
|
||||
TIMESTAMPDIFF(DAY, NOW(), p.postpone_time) AS remainTime,
|
||||
p.customer_id AS customerId,
|
||||
c.name AS customerName,
|
||||
p.is_top AS isTop
|
||||
p.is_top AS isTop,
|
||||
COUNT(sot.task_id) AS taskTotalCount ,
|
||||
SUM(CASE WHEN sot.status = 1 THEN 1 ELSE 0 END) AS taskFinishCount,
|
||||
COUNT(ops.schedule_id) AS scheduleTotalCount,
|
||||
SUM(CASE WHEN ops.status = 2 THEN 1 ELSE 0 END) AS scheduleFinishCount
|
||||
FROM sys_oa_project p
|
||||
LEFT JOIN oa_customer c ON p.customer_id = c.customer_id
|
||||
left join sys_oa_task sot on sot.project_id = p.project_id and sot.del_flag = 0
|
||||
left join oa_project_schedule ops on ops.project_id = p.project_id and ops.del_flag = 0
|
||||
${ew.getCustomSqlSegment}
|
||||
</select>
|
||||
|
||||
|
||||
@@ -16,7 +16,13 @@ export function readCc(ccId) {
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
||||
// 标记抄送为未读
|
||||
export function unreadCc(ccId) {
|
||||
return request({
|
||||
url: `/hrm/flow/cc/${ccId}/unread`,
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
// 手动抄送
|
||||
export function addCc(data) {
|
||||
return request({
|
||||
|
||||
@@ -24,7 +24,7 @@ const state = {
|
||||
|
||||
projectQuery: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
pageSize: 15,
|
||||
keyword: ''
|
||||
},
|
||||
projectList: [],
|
||||
|
||||
@@ -56,8 +56,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listCc, readCc } from '@/api/hrm/cc';
|
||||
import applyTypeMinix from '@/views/hrm/minix/applyTypeMinix.js';
|
||||
import { listCc, readCc, unreadCc } from '@/api/hrm/cc';
|
||||
|
||||
export default {
|
||||
name: 'HrmFlowCc',
|
||||
@@ -115,6 +115,14 @@ export default {
|
||||
this.getList()
|
||||
})
|
||||
},
|
||||
// 标记未读
|
||||
handleUnread(row) {
|
||||
unreadCc(row.ccId).then(() => {
|
||||
this.$modal.msgSuccess('已标记为未读')
|
||||
// 刷新当前列表(这条记录会移到未读列表)
|
||||
this.getList()
|
||||
})
|
||||
},
|
||||
handleRefresh () {
|
||||
this.getList()
|
||||
}
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
-->
|
||||
<template>
|
||||
<div class="app-container dashboard2" v-loading="pageLoading">
|
||||
<div class="layout">
|
||||
<!-- 左侧 20%:项目列表 -->
|
||||
<div class="dashboard-shell">
|
||||
<div class="layout">
|
||||
<!-- 左侧 20%:项目列表 -->
|
||||
<div class="left">
|
||||
<div class="left-search">
|
||||
<el-input
|
||||
@@ -28,10 +29,16 @@
|
||||
:class="{ active: String(p.projectId) === String(currentProjectId) }"
|
||||
@click="handleSelectProject(p)"
|
||||
>
|
||||
<div class="project-name text-ellipsis">{{ p.projectName }}</div>
|
||||
<div class="project-meta text-ellipsis">
|
||||
<span v-if="p.projectCode" class="code">{{ p.projectCode }}</span>
|
||||
<span class="time">{{ formatDate(p.beginTime) }} ~ {{ formatDate(p.finishTime) }}</span>
|
||||
<div class="project-main">
|
||||
<div class="project-name text-ellipsis">{{ p.projectName }}</div>
|
||||
<div class="project-meta text-ellipsis">
|
||||
<span v-if="p.projectCode" class="code">{{ p.projectCode }}</span>
|
||||
<span class="time">{{ formatDate(p.beginTime) }} ~ {{ formatDate(p.finishTime) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="project-stats">
|
||||
<span class="stat-item">任务 {{ p.taskFinishCount || 0 }}/{{ p.taskTotalCount || 0 }}</span>
|
||||
<span class="stat-item">进度 {{ p.scheduleFinishCount || 0 }}/{{ p.scheduleTotalCount || 0 }}</span>
|
||||
</div>
|
||||
<div
|
||||
class="project-progress-row"
|
||||
@@ -45,18 +52,9 @@
|
||||
<el-empty :image-size="72" description="暂无项目"></el-empty>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="left-pager">
|
||||
<el-pagination
|
||||
small
|
||||
background
|
||||
layout="total, prev, pager, next"
|
||||
:total="projectTotal"
|
||||
:current-page.sync="projectQuery.pageNum"
|
||||
:page-size.sync="projectQuery.pageSize"
|
||||
@current-change="getProjectList"
|
||||
/>
|
||||
</div>
|
||||
<!-- 分页组件 -->
|
||||
<pagination :total="projectTotal" :page.sync="projectQuery.pageNum" :limit.sync="projectQuery.pageSize" layout="prev,pager, next" pagerCount="3"
|
||||
@pagination="getProjectList" style="margin-top: 10px;" />
|
||||
</div>
|
||||
|
||||
<!-- 右侧 80% -->
|
||||
@@ -117,6 +115,7 @@
|
||||
<div
|
||||
v-else
|
||||
class="schedule-progress-wrap schedule-progress-wrap--linked"
|
||||
:class="{ 'is-link': scheduleProgressClick(row) }"
|
||||
:title="scheduleProgressTitle(row)"
|
||||
@click.stop="goToPaceFromTaskRow(row)"
|
||||
>
|
||||
@@ -168,7 +167,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 下:进度板块 — 与进度中心(step) 同款的简要信息条 + Tab -->
|
||||
<!-- 下:进度板块 — Tab1 进度明细表(默认);Tab2 原思维导图 + 图例 -->
|
||||
<div class="panel schedule-panel">
|
||||
<div v-if="currentProjectId" class="schedule-board-summary schedule-board-summary--pace-like">
|
||||
<el-row>
|
||||
@@ -215,7 +214,7 @@
|
||||
<div v-else class="progress-table-scroll">
|
||||
<el-table
|
||||
:data="scheduleStepsForTable"
|
||||
size="mini"
|
||||
|
||||
border
|
||||
stripe
|
||||
class="progress-step-el-table"
|
||||
@@ -241,6 +240,12 @@
|
||||
<el-table-column label="负责人" min-width="96" show-overflow-tooltip>
|
||||
<template slot-scope="{ row }">{{ formatStepResponsible(row) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作">
|
||||
<template slot-scope="{ row }">
|
||||
|
||||
<el-button size="text" @click="openStepById(row.trackId)" >详情 </el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -256,7 +261,7 @@
|
||||
ref="dashboardMindXmind"
|
||||
v-else-if="scheduleViewTab === 'mind'"
|
||||
:list="stepList"
|
||||
height="100%"
|
||||
height="1200px"
|
||||
dashboard-mode
|
||||
@refresh="onXmindRefresh"
|
||||
/>
|
||||
@@ -286,6 +291,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -375,19 +381,18 @@ export default {
|
||||
this.getProjectList()
|
||||
},
|
||||
methods: {
|
||||
/** 进度导图挂在 Tab 内,切换为可见后触发 ECharts 重新测量,避免线上画布宽/高为 0 */
|
||||
/** 进度导图 Tab 可见后触发 ECharts 重新测量 */
|
||||
onScheduleTabClick (tab) {
|
||||
if (!tab || tab.name !== 'mind') return;
|
||||
if (!tab || tab.name !== 'mind') return
|
||||
this.$nextTick(() => {
|
||||
requestAnimationFrame(() => {
|
||||
const comp = this.$refs.dashboardMindXmind;
|
||||
const comp = this.$refs.dashboardMindXmind
|
||||
if (comp && typeof comp.scheduleResize === 'function') {
|
||||
comp.scheduleResize();
|
||||
comp.scheduleResize()
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
async getProjectList () {
|
||||
this.projectLoading = true
|
||||
try {
|
||||
@@ -417,9 +422,30 @@ export default {
|
||||
} finally {
|
||||
this.pageLoading = false
|
||||
this.xmindLoading = false
|
||||
if (this.scheduleViewTab === 'mind') {
|
||||
this.$nextTick(() => {
|
||||
requestAnimationFrame(() => {
|
||||
const comp = this.$refs.dashboardMindXmind
|
||||
if (comp && typeof comp.scheduleResize === 'function') {
|
||||
comp.scheduleResize()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
openTaskById (taskId) {
|
||||
if (!taskId) return
|
||||
this.$router.push({ path: '/task/task/allocation', query: { taskId } })
|
||||
},
|
||||
|
||||
openStepById (trackId) {
|
||||
if (!trackId) return
|
||||
const projectId = this.currentProjectId
|
||||
this.$router.push({ path: '/step/files', query: { trackId, projectId } })
|
||||
},
|
||||
|
||||
resetTaskPage () {
|
||||
this.$store.commit('oaProjectDashboard2/SET_TASK_QUERY', { pageNum: 1 })
|
||||
},
|
||||
@@ -603,6 +629,10 @@ export default {
|
||||
return `${this.scheduleStepStatusLabel(row)} ${this.scheduleProgressPath(row)}`.trim()
|
||||
},
|
||||
|
||||
scheduleProgressClick (row) {
|
||||
return !!(row && row.trackId != null && String(row.trackId).trim() !== '')
|
||||
},
|
||||
|
||||
/** oa_project_schedule_step.status:与任务表「对应进度」标签一致 */
|
||||
stepStatusLabel (row) {
|
||||
return this.scheduleStepStatusLabel({ scheduleStatus: row && row.status })
|
||||
@@ -765,7 +795,9 @@ export default {
|
||||
margin-right: 8px;
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.stat-item{
|
||||
margin-right:5px;
|
||||
}
|
||||
.left-pager {
|
||||
padding-top: 8px;
|
||||
margin-top: 4px;
|
||||
@@ -847,21 +879,26 @@ export default {
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.schedule-board-summary--pace-like {
|
||||
flex-shrink: 0;
|
||||
padding: 10px 12px 8px;
|
||||
margin: -10px -10px 0;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
}
|
||||
|
||||
/*
|
||||
* schedule-panel-tabs 与 .el-tabs 在同一 DOM 节点上,不能用「.schedule-panel-tabs :deep(.el-tabs)」后代选择器(永远匹配不到)。
|
||||
* 生产包合并 CSS 后,Element 全局 .el-tabs 可能后加载并盖住单独的 .schedule-panel-tabs,导致 flex 丢失。
|
||||
* 使用双类选择器提高命中与优先级。
|
||||
*/
|
||||
.schedule-panel-tabs.el-tabs {
|
||||
.schedule-panel-tabs {
|
||||
flex: 1 1 0%;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
}
|
||||
|
||||
/* Element Tabs 根节点参与纵向 flex,才能把剩余高度交给内容区 */
|
||||
.schedule-panel-tabs :deep(.el-tabs) {
|
||||
flex: 1 1 0%;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
@@ -869,13 +906,13 @@ export default {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.schedule-panel-tabs.el-tabs :deep(.el-tabs__header) {
|
||||
.schedule-panel-tabs :deep(.el-tabs__header) {
|
||||
margin-bottom: 0;
|
||||
padding: 0 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.schedule-panel-tabs.el-tabs :deep(.el-tabs__content) {
|
||||
.schedule-panel-tabs :deep(.el-tabs__content) {
|
||||
flex: 1 1 0%;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
@@ -885,11 +922,9 @@ export default {
|
||||
}
|
||||
|
||||
/* 当前激活的 pane 占满内容区高度,子元素才能算出可滚动区域 */
|
||||
.schedule-panel-tabs.el-tabs :deep(.el-tab-pane) {
|
||||
.schedule-panel-tabs :deep(.el-tab-pane) {
|
||||
flex: 1 1 0%;
|
||||
min-height: 0;
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -898,8 +933,6 @@ export default {
|
||||
.schedule-tab-pane-inner {
|
||||
flex: 1 1 0%;
|
||||
min-height: 0;
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
@@ -909,24 +942,19 @@ export default {
|
||||
.schedule-tab-pane-inner--mind {
|
||||
min-height: 280px;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/* 进度明细 Tab:表格与面板四边留白,避免贴边 */
|
||||
/* 进度明细 Tab:占满下方板块;内部仅滚动区参与滚动 */
|
||||
.progress-table-pane {
|
||||
flex: 1 1 0%;
|
||||
min-height: 0;
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
height:600px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 14px 18px 18px;
|
||||
}
|
||||
padding: 8px 12px 10px;
|
||||
|
||||
.progress-table-pane :deep(.el-empty) {
|
||||
padding: 24px 8px;
|
||||
}
|
||||
|
||||
.progress-table-scroll {
|
||||
@@ -935,8 +963,6 @@ export default {
|
||||
min-width: 0;
|
||||
overflow: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
/* 表格与滚动容器边缘再留一线间距,滚动条不压表格边框 */
|
||||
padding: 2px 4px 4px 2px;
|
||||
}
|
||||
|
||||
.progress-step-el-table {
|
||||
@@ -948,10 +974,7 @@ export default {
|
||||
flex-direction: row;
|
||||
align-items: stretch;
|
||||
flex: 1;
|
||||
flex-basis: 0;
|
||||
min-height: 0;
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.task-table-wrap {
|
||||
@@ -1014,13 +1037,13 @@ export default {
|
||||
}
|
||||
|
||||
.xmind-wrap {
|
||||
flex: 1 1 0%;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
position: relative;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 320px;
|
||||
min-height: 1600px;
|
||||
}
|
||||
|
||||
.xmind-wrap :deep(.xmind-box--dashboard) {
|
||||
@@ -1028,11 +1051,12 @@ export default {
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 1600px;
|
||||
}
|
||||
|
||||
.xmind-wrap :deep(.xmind-container) {
|
||||
flex: 1;
|
||||
min-height: 300px !important;
|
||||
min-height: 1580px !important;
|
||||
}
|
||||
|
||||
.mind-legend-aside {
|
||||
|
||||
@@ -90,10 +90,10 @@ export default {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
/** 容器高度,综合看板等场景可传 100% 以撑满父级 */
|
||||
/** 容器高度 */
|
||||
height: {
|
||||
type: String,
|
||||
default: '800px'
|
||||
default: '4800px'
|
||||
},
|
||||
/** 综合看板:紧凑、防重叠、三色状态、小圆点 */
|
||||
dashboardMode: {
|
||||
@@ -106,9 +106,7 @@ export default {
|
||||
return {
|
||||
width: '100%',
|
||||
height: this.height,
|
||||
minHeight: this.dashboardMode ? '300px' : '240px',
|
||||
/* 看板嵌在横向 flex 内,避免 min-width:auto 把可用宽压成 0 */
|
||||
...(this.dashboardMode ? { minWidth: 0, boxSizing: 'border-box' } : {})
|
||||
minHeight: this.dashboardMode ? '1200px' : '240px'
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -120,92 +118,37 @@ export default {
|
||||
clickEvent: null, // 新增:存储点击事件句柄,用于销毁解绑
|
||||
users: [],
|
||||
supplierList: [],
|
||||
chartResizeObserver: null,
|
||||
chartResizeObserverRaf: null
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
// 监听列表数据变化,自动更新图表
|
||||
list: {
|
||||
deep: true,
|
||||
handler () {
|
||||
this.$nextTick(() => {
|
||||
if (this.chartInstance) {
|
||||
this.initChart();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.initChart();
|
||||
this.deferResizeChart();
|
||||
this.$nextTick(() => this.resizeChart());
|
||||
window.addEventListener('resize', this.resizeChart);
|
||||
this.$nextTick(() => {
|
||||
this.bindChartResizeObserver();
|
||||
});
|
||||
},
|
||||
beforeDestroy () {
|
||||
window.removeEventListener('resize', this.resizeChart);
|
||||
this.unbindChartResizeObserver();
|
||||
// 新增:解绑Echarts点击事件,防止内存泄漏
|
||||
if (this.chartInstance && this.clickEvent) {
|
||||
this.chartInstance.off('click', this.clickEvent);
|
||||
}
|
||||
// 销毁图表实例,防止内存泄漏
|
||||
this.chartInstance?.dispose();
|
||||
},
|
||||
methods: {
|
||||
// 优化:增加防抖处理-窗口自适应,避免频繁触发
|
||||
resizeChart () {
|
||||
this.chartInstance?.resize();
|
||||
},
|
||||
|
||||
/**
|
||||
* 供综合看板在切换 Tab 后调用:Tab 从 display:none 变为可见后需重新测量画布。
|
||||
*/
|
||||
scheduleResize () {
|
||||
this.deferResizeChart();
|
||||
},
|
||||
|
||||
/**
|
||||
* el-tab / flex 布局下首帧常为 0 宽高,生产环境更明显;延迟到布局稳定后再 resize。
|
||||
*/
|
||||
deferResizeChart () {
|
||||
this.$nextTick(() => {
|
||||
this.$nextTick(() => {
|
||||
requestAnimationFrame(() => {
|
||||
this.resizeChart();
|
||||
requestAnimationFrame(() => this.resizeChart());
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
bindChartResizeObserver () {
|
||||
if (!this.dashboardMode || typeof ResizeObserver === 'undefined') {
|
||||
return;
|
||||
}
|
||||
const el = this.$refs.chart;
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
this.unbindChartResizeObserver();
|
||||
this.chartResizeObserver = new ResizeObserver(() => {
|
||||
if (this.chartResizeObserverRaf != null) {
|
||||
cancelAnimationFrame(this.chartResizeObserverRaf);
|
||||
}
|
||||
this.chartResizeObserverRaf = requestAnimationFrame(() => {
|
||||
this.chartResizeObserverRaf = null;
|
||||
this.resizeChart();
|
||||
});
|
||||
});
|
||||
this.chartResizeObserver.observe(el);
|
||||
},
|
||||
|
||||
unbindChartResizeObserver () {
|
||||
if (this.chartResizeObserverRaf != null) {
|
||||
cancelAnimationFrame(this.chartResizeObserverRaf);
|
||||
this.chartResizeObserverRaf = null;
|
||||
}
|
||||
if (this.chartResizeObserver) {
|
||||
this.chartResizeObserver.disconnect();
|
||||
this.chartResizeObserver = null;
|
||||
}
|
||||
this.chartInstance?.resize()
|
||||
},
|
||||
|
||||
handleSubmit () {
|
||||
@@ -345,13 +288,11 @@ export default {
|
||||
};
|
||||
},
|
||||
|
||||
// 初始化图表
|
||||
initChart () {
|
||||
const dom = this.$refs.chart;
|
||||
if (!dom) {
|
||||
return;
|
||||
}
|
||||
// 初始化图表实例
|
||||
if (!this.chartInstance) {
|
||||
this.chartInstance = echarts.init(dom);
|
||||
this.chartInstance = echarts.init(this.$refs.chart);
|
||||
}
|
||||
// 重要:先解绑已有点击事件,防止多次绑定导致弹窗多次触发
|
||||
if (this.clickEvent) {
|
||||
@@ -391,24 +332,24 @@ export default {
|
||||
type: 'tree',
|
||||
data: [treeData],
|
||||
/* 与折线图区域一致:留白、白底在容器上 */
|
||||
...(dm ? { left: '1%', right: '5%', top: '2%', bottom: '2%' } : {}),
|
||||
...(dm ? { left: '3%', right: '8%', top: '4%', bottom: '4%' } : {}),
|
||||
symbol: 'circle',
|
||||
...(dm ? {} : { symbolSize: 6 }),
|
||||
edgeShape: dm ? 'polyline' : 'curve',
|
||||
edgeForkPosition: dm ? '74%' : '50%',
|
||||
edgeForkPosition: dm ? '68%' : '50%',
|
||||
orient: 'LR',
|
||||
initialTreeDepth: 4,
|
||||
initialTreeDepth: 3,
|
||||
roam: true,
|
||||
scaleLimit: dm ? { min: 0.22, max: 5 } : undefined,
|
||||
expandAndCollapse: false,
|
||||
scaleLimit: dm ? { min: 0.3, max: 4 } : undefined,
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: dm ? 11 : 12,
|
||||
fontWeight: 400,
|
||||
position: 'left',
|
||||
position: 'top',
|
||||
verticalAlign: 'middle',
|
||||
...(dm ? { align: 'right' } : {}),
|
||||
distance: 8,
|
||||
overflow: 'none',
|
||||
distance: 6,
|
||||
overflow: 'break',
|
||||
lineHeight: dm ? 15 : 14,
|
||||
color: dm ? '#606266' : undefined
|
||||
},
|
||||
@@ -417,46 +358,59 @@ export default {
|
||||
*/
|
||||
levels: dm
|
||||
? [
|
||||
{
|
||||
symbolSize: 18,
|
||||
itemStyle: { borderWidth: 2, borderColor: '#fff' },
|
||||
label: {
|
||||
position: 'top',
|
||||
distance: 8,
|
||||
fontSize: 13,
|
||||
fontWeight: 600,
|
||||
width: 140,
|
||||
overflow: 'break',
|
||||
lineHeight: 16,
|
||||
padding: [4, 8, 4, 8]
|
||||
}
|
||||
},
|
||||
{
|
||||
symbolSize: 14,
|
||||
itemStyle: { borderWidth: 1.5, borderColor: '#fff' },
|
||||
label: {
|
||||
position: 'top',
|
||||
distance: 6,
|
||||
fontSize: 12,
|
||||
width: 180,
|
||||
overflow: 'break',
|
||||
lineHeight: 15,
|
||||
padding: [3, 6, 3, 6]
|
||||
}
|
||||
},
|
||||
{
|
||||
symbolSize: 10,
|
||||
itemStyle: { borderWidth: 1, borderColor: '#fff', shadowBlur: 2, shadowColor: 'rgba(0,0,0,0.1)' },
|
||||
label: {
|
||||
position: 'top',
|
||||
verticalAlign: 'bottom',
|
||||
distance: 6,
|
||||
fontSize: 11,
|
||||
width: 180,
|
||||
overflow: 'break',
|
||||
lineHeight: 14,
|
||||
padding: [3, 6, 3, 6]
|
||||
}
|
||||
},
|
||||
{
|
||||
symbolSize: 6,
|
||||
itemStyle: { borderWidth: 1, borderColor: '#fff' },
|
||||
label: {
|
||||
position: 'left',
|
||||
distance: 8,
|
||||
fontSize: 11,
|
||||
width: 118,
|
||||
position: 'bottom',
|
||||
verticalAlign: 'top',
|
||||
distance: 4,
|
||||
fontSize: 10,
|
||||
width: 140,
|
||||
overflow: 'break',
|
||||
lineHeight: 14,
|
||||
padding: [2, 6, 2, 6]
|
||||
}
|
||||
},
|
||||
{
|
||||
symbolSize: 5,
|
||||
itemStyle: { borderWidth: 1, borderColor: '#fff' },
|
||||
label: {
|
||||
position: 'left',
|
||||
distance: 10,
|
||||
fontSize: 11,
|
||||
width: 160,
|
||||
overflow: 'break',
|
||||
lineHeight: 14,
|
||||
padding: [2, 8, 2, 6]
|
||||
}
|
||||
},
|
||||
{
|
||||
symbolSize: 4,
|
||||
itemStyle: { borderWidth: 1, borderColor: '#fff', shadowBlur: 2, shadowColor: 'rgba(0,0,0,0.1)' },
|
||||
label: {
|
||||
position: 'right',
|
||||
verticalAlign: 'middle',
|
||||
align: 'left',
|
||||
distance: 12,
|
||||
fontSize: 11,
|
||||
width: 232,
|
||||
overflow: 'break',
|
||||
lineHeight: 15,
|
||||
padding: [2, 8, 2, 8],
|
||||
formatter: (p) => this.wrapLabelText(p.name, 17)
|
||||
lineHeight: 13,
|
||||
padding: [2, 4, 2, 4]
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -467,22 +421,22 @@ export default {
|
||||
{ symbolSize: 3 }
|
||||
],
|
||||
lineStyle: {
|
||||
width: dm ? 1 : 1.2,
|
||||
curveness: dm ? 0.1 : 0.3,
|
||||
color: dm ? '#e4e7ed' : '#ccc'
|
||||
width: 1,
|
||||
curveness: 0.5,
|
||||
color: '#c0c4cc'
|
||||
},
|
||||
emphasis: {
|
||||
focus: 'descendant',
|
||||
lineStyle: { width: 2, color: '#409eff' },
|
||||
itemStyle: dm ? { shadowBlur: 6, shadowColor: 'rgba(64,158,255,0.45)' } : undefined
|
||||
itemStyle: { shadowBlur: 10, shadowColor: 'rgba(64,158,255,0.5)' }
|
||||
},
|
||||
expandAndCollapse: true,
|
||||
animationDuration: 280
|
||||
|
||||
animationDuration: 300
|
||||
}
|
||||
]
|
||||
};
|
||||
// 渲染图表
|
||||
this.chartInstance?.setOption(option, true);
|
||||
this.deferResizeChart();
|
||||
|
||||
this.clickEvent = (params) => {
|
||||
const data = params.data;
|
||||
@@ -520,16 +474,12 @@ export default {
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/* 与综合看板折线图区域一致:白底、细边框、轻圆角 */
|
||||
.xmind-box--dashboard .xmind-container {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 6px;
|
||||
@@ -572,4 +522,4 @@ export default {
|
||||
:deep(.dialog-footer) {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -282,7 +282,6 @@ export default {
|
||||
this.getScheduleDetail(row);
|
||||
this.$nextTick(() => this.clearPaceDeepLinkQuery());
|
||||
},
|
||||
// 关闭细节窗口
|
||||
closeDetailShow (done) {
|
||||
this.scheduleStepFocusHint = null;
|
||||
this.getList();
|
||||
@@ -319,14 +318,13 @@ export default {
|
||||
},
|
||||
getList () {
|
||||
this.loading = true
|
||||
|
||||
console.log(this.queryParams, this.searchTime)
|
||||
/* 日期搜索条件 */
|
||||
if (this.searchTime && this.searchTime.length) {
|
||||
this.queryParams.startTime = this.getDateStr(this.searchTime[0])
|
||||
this.queryParams.endTime = this.getDateStr(this.searchTime[1])
|
||||
}
|
||||
|
||||
this.queryParams.projectId = this.$route.query.projectId?this.$route.query.projectId:null
|
||||
this.queryParams.trackId = this.$route.query.trackId?this.$route.query.trackId:null
|
||||
listProjectSchedule(this.queryParams).then(res => {
|
||||
this.scheduleList = res.rows
|
||||
this.total = res.total
|
||||
|
||||
@@ -499,6 +499,7 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
// 转换数据为级联选择器格式
|
||||
convertToCascader (rows) {
|
||||
if (!rows || !rows.length) return [];
|
||||
@@ -614,13 +615,15 @@ export default {
|
||||
},
|
||||
/** 查询项目管理列表 */
|
||||
getList () {
|
||||
|
||||
this.loading = true;
|
||||
const payload = {
|
||||
...this.queryParams,
|
||||
taskId:this.$route.query.taskId!=null?this.$route.query.taskId:null,
|
||||
beginTime: this.queryParams.searchTime[0] ? this.queryParams.searchTime[0] + ' 00:00:00' : undefined,
|
||||
finishTime: this.queryParams.searchTime[1] ? this.queryParams.searchTime[1] + ' 23:59:59' : undefined
|
||||
}
|
||||
console.log(payload, this.queryParams, 'payload');
|
||||
|
||||
listTask(payload).then(response => {
|
||||
this.taskList = response.rows;
|
||||
this.total = response.total;
|
||||
|
||||
Reference in New Issue
Block a user