未验证 提交 c8b5d1e5 编写于 作者: D Devosend 提交者: GitHub

[Feature][UI Next][V1.0.0-Alpha] Support gantt (#8697)

上级 e1862a81
......@@ -121,6 +121,16 @@ export default {
auth: []
}
},
{
path: '/projects/:projectCode/workflow/instances/:id/gantt',
name: 'workflow-instance-gantt',
component: components['projects-workflow-instance-gantt'],
meta: {
title: '工作流实例甘特图',
showSide: true,
auth: []
}
},
{
path: '/projects/:projectCode/task/definitions',
name: 'task-definition',
......
......@@ -118,7 +118,7 @@ export function queryTaskListByProcessId(id: number, code: number): any {
})
}
export function vieGanttTree(id: IdReq, code: CodeReq): any {
export function viewGanttTree(id: number, code: number): any {
return axios({
url: `/projects/${code}/process-instances/${id}/view-gantt`,
method: 'get'
......
......@@ -261,7 +261,7 @@ export const tasksState = (t: any): ITaskState => ({
SUCCESS: {
id: 7,
desc: `${t('project.workflow.success')}`,
color: '#33cc00',
color: '#95DF96',
icon: CheckCircleOutlined,
isSpin: false,
classNames: 'success'
......
......@@ -62,6 +62,14 @@ export default defineComponent({
})
}
const handleGantt = () => {
router.push({
name: 'workflow-instance-gantt',
params: { id: props.row!.id },
query: { code: props.row!.processDefinitionCode }
})
}
const handleReRun = () => {
ctx.emit('reRun')
}
......@@ -89,6 +97,7 @@ export default defineComponent({
handleStop,
handleSuspend,
handleDeleteInstance,
handleGantt,
...toRefs(props)
}
},
......@@ -280,8 +289,8 @@ export default defineComponent({
size='tiny'
type='info'
circle
/* TODO: Goto gantt*/
disabled={this.row?.disabled}
onClick={this.handleGantt}
>
<NIcon>
<ControlOutlined />
......
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { defineComponent, ref, PropType } from 'vue'
import * as echarts from 'echarts'
import type { Ref } from 'vue'
import { useI18n } from 'vue-i18n'
import initChart from '@/components/chart'
import { tasksState } from '@/utils/common'
import { format } from 'date-fns'
import { parseTime } from '@/utils/common'
import { ISeriesData } from '../type'
const props = {
height: {
type: [String, Number] as PropType<string | number>,
default: window.innerHeight - 174
},
width: {
type: [String, Number] as PropType<string | number>,
default: '100%'
},
seriesData: {
type: Array as PropType<Array<any>>,
default: () => []
},
taskList: {
type: Array as PropType<Array<string>>,
default: []
}
}
const GanttChart = defineComponent({
name: 'GanttChart',
props,
setup(props) {
const graphChartRef: Ref<HTMLDivElement | null> = ref(null)
const { t } = useI18n()
const state = tasksState(t)
const data: ISeriesData = {}
Object.keys(state).forEach((key) => (data[key] = []))
const series = Object.keys(state).map((key) => ({
id: key,
type: 'custom',
name: state[key].desc,
renderItem: renderItem,
itemStyle: {
opacity: 0.8,
color: state[key].color,
color0: state[key].color
},
encode: {
x: [1, 2],
y: 0
},
data: data[key]
}))
// format series data
let minTime = Number(new Date())
props.seriesData.forEach(function (task, index) {
minTime = minTime < task.startDate[0] ? minTime : task.startDate[0]
data[task.status].push({
name: task.name,
value: [
index,
task.startDate[0],
task.endDate[0],
task.endDate[0] - task.startDate[0]
],
itemStyle: {
color: state[task.status].color
}
})
})
// customer render
function renderItem(params: any, api: any) {
const taskIndex = api.value(0)
const start = api.coord([api.value(1), taskIndex])
const end = api.coord([api.value(2), taskIndex])
const height = api.size([0, 1])[1] * 0.6
const rectShape = echarts.graphic.clipRectByRect(
{
x: start[0],
y: start[1] - height / 2,
width: end[0] - start[0],
height: height
},
{
x: params.coordSys.x,
y: params.coordSys.y,
width: params.coordSys.width,
height: params.coordSys.height
}
)
return (
rectShape && {
type: 'rect',
transition: ['shape'],
shape: rectShape,
style: api.style()
}
)
}
const option = {
title: {
text: t('project.workflow.task_state'),
textStyle: {
fontWeight: 'normal',
fontSize: 14
},
left: 50
},
tooltip: {
formatter: function (params: any) {
const taskName = params.data.name
const data = props.seriesData.filter(
(item) => item.taskName === taskName
)
let str = `taskName : ${taskName}</br>`
str += `status : ${state[data[0].status].desc} (${
data[0].status
})</br>`
str += `startTime : ${data[0].isoStart}</br>`
str += `endTime : ${data[0].isoEnd}</br>`
str += `duration : ${data[0].duration}</br>`
return str
}
},
legend: {
left: 150,
padding: [5, 5, 5, 5]
},
dataZoom: [
{
type: 'slider',
filterMode: 'weakFilter',
showDataShadow: false,
top:
props.taskList.length * 100 > 200
? props.taskList.length * 100
: 200,
labelFormatter: ''
},
{
type: 'inside',
filterMode: 'weakFilter'
}
],
grid: {
height: props.taskList.length * 50,
top: 80
},
xAxis: {
min: minTime,
scale: true,
position: 'top',
axisTick: { show: true },
splitLine: { show: false },
axisLabel: {
formatter: function (val: number) {
return format(parseTime(val), 'HH:mm:ss')
}
}
},
yAxis: {
axisTick: { show: false },
splitLine: { show: false },
axisLine: { show: false },
max: props.taskList.length,
data: props.taskList
},
series: series
}
initChart(graphChartRef, option)
return { graphChartRef }
},
render() {
const { height, width } = this
return (
<div
ref='graphChartRef'
style={{
height: typeof height === 'number' ? height + 'px' : height,
width: typeof width === 'number' ? width + 'px' : width
}}
/>
)
}
})
export default GanttChart
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { defineComponent, onMounted, toRefs, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRoute } from 'vue-router'
import Card from '@/components/card'
import GanttChart from './components/gantt-chart'
import { useGantt } from './use-gantt'
const workflowRelation = defineComponent({
name: 'workflow-relation',
setup() {
const { t, locale } = useI18n()
const route = useRoute()
const { variables, getGantt } = useGantt()
const id = Number(route.params.id)
const code = Number(route.params.projectCode)
const handleResetDate = () => {
variables.seriesData = []
variables.taskList = []
getGantt(id, code)
}
onMounted(() => {
getGantt(id, code)
})
watch(
() => [locale.value],
() => {
handleResetDate()
}
)
return { t, ...toRefs(variables) }
},
render() {
const { t } = this
return (
<Card title={t('project.workflow.gantt')}>
{{
default: () =>
this.seriesData.length > 0 && (
<GanttChart
seriesData={this.seriesData}
taskList={this.taskList}
/>
)
}}
</Card>
)
}
})
export default workflowRelation
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
interface ITask {
taskName: string
startDate: Array<number>
endDate: Array<number>
isoStart: string
isoEnd: string
status: string
duration: string
}
interface IGanttRes {
height: number
taskNames: Array<number>
taskStatus: Object
tasks: Array<ITask>
}
interface ISeriesData {
[taskState: string]: Array<any>
}
export { ITask, IGanttRes, ISeriesData }
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { reactive } from 'vue'
import { useAsyncState } from '@vueuse/core'
import { viewGanttTree } from '@/service/modules/process-instances'
import { IGanttRes } from './type'
export function useGantt() {
const variables = reactive({
seriesData: [],
taskList: [] as Array<string>
})
const formatGantt = (obj: IGanttRes) => {
variables.seriesData = []
variables.taskList = []
variables.seriesData = obj.tasks.map((item) => {
variables.taskList.push(item.taskName)
return {
name: item.taskName,
...item
}
}) as any
}
const getGantt = (id: number, code: number) => {
const { state } = useAsyncState(
viewGanttTree(id, code).then((res: IGanttRes) => {
formatGantt(res)
}),
{}
)
return state
}
return { variables, getGantt }
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册