mirror of
https://github.com/soybeanjs/soybean-admin.git
synced 2025-09-20 18:46:39 +08:00
feat(projects): add plugin gantt preview (#578)
This commit is contained in:
parent
48e005bcaf
commit
f86bca063a
@ -59,6 +59,7 @@
|
|||||||
"@vueuse/core": "10.11.0",
|
"@vueuse/core": "10.11.0",
|
||||||
"clipboard": "2.0.11",
|
"clipboard": "2.0.11",
|
||||||
"dayjs": "1.11.12",
|
"dayjs": "1.11.12",
|
||||||
|
"dhtmlx-gantt": "8.0.9",
|
||||||
"dompurify": "3.1.6",
|
"dompurify": "3.1.6",
|
||||||
"echarts": "5.5.1",
|
"echarts": "5.5.1",
|
||||||
"jsbarcode": "3.11.6",
|
"jsbarcode": "3.11.6",
|
||||||
|
@ -205,7 +205,8 @@ const local: App.I18n.Schema = {
|
|||||||
plugin_barcode: 'Barcode',
|
plugin_barcode: 'Barcode',
|
||||||
plugin_pinyin: 'pinyin',
|
plugin_pinyin: 'pinyin',
|
||||||
plugin_excel: 'Excel',
|
plugin_excel: 'Excel',
|
||||||
plugin_pdf: 'PDF preview'
|
plugin_pdf: 'PDF preview',
|
||||||
|
plugin_gantt: 'Gantt Chart'
|
||||||
},
|
},
|
||||||
page: {
|
page: {
|
||||||
login: {
|
login: {
|
||||||
|
@ -205,7 +205,8 @@ const local: App.I18n.Schema = {
|
|||||||
plugin_barcode: '条形码',
|
plugin_barcode: '条形码',
|
||||||
plugin_pinyin: '拼音',
|
plugin_pinyin: '拼音',
|
||||||
plugin_excel: 'Excel',
|
plugin_excel: 'Excel',
|
||||||
plugin_pdf: 'PDF 预览'
|
plugin_pdf: 'PDF 预览',
|
||||||
|
plugin_gantt: '甘特图'
|
||||||
},
|
},
|
||||||
page: {
|
page: {
|
||||||
login: {
|
login: {
|
||||||
|
@ -42,6 +42,7 @@ export const views: Record<LastLevelRouteKey, RouteComponent | (() => Promise<Ro
|
|||||||
plugin_editor_markdown: () => import("@/views/plugin/editor/markdown/index.vue"),
|
plugin_editor_markdown: () => import("@/views/plugin/editor/markdown/index.vue"),
|
||||||
plugin_editor_quill: () => import("@/views/plugin/editor/quill/index.vue"),
|
plugin_editor_quill: () => import("@/views/plugin/editor/quill/index.vue"),
|
||||||
plugin_excel: () => import("@/views/plugin/excel/index.vue"),
|
plugin_excel: () => import("@/views/plugin/excel/index.vue"),
|
||||||
|
plugin_gantt: () => import("@/views/plugin/gantt/index.vue"),
|
||||||
plugin_icon: () => import("@/views/plugin/icon/index.vue"),
|
plugin_icon: () => import("@/views/plugin/icon/index.vue"),
|
||||||
plugin_map: () => import("@/views/plugin/map/index.vue"),
|
plugin_map: () => import("@/views/plugin/map/index.vue"),
|
||||||
plugin_pdf: () => import("@/views/plugin/pdf/index.vue"),
|
plugin_pdf: () => import("@/views/plugin/pdf/index.vue"),
|
||||||
|
@ -424,6 +424,16 @@ export const generatedRoutes: GeneratedRoute[] = [
|
|||||||
keepAlive: true
|
keepAlive: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'plugin_gantt',
|
||||||
|
path: '/plugin/gantt',
|
||||||
|
component: 'view.plugin_gantt',
|
||||||
|
meta: {
|
||||||
|
title: 'plugin_gantt',
|
||||||
|
i18nKey: 'route.plugin_gantt',
|
||||||
|
icon: 'ant-design:bar-chart-outlined'
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'plugin_icon',
|
name: 'plugin_icon',
|
||||||
path: '/plugin/icon',
|
path: '/plugin/icon',
|
||||||
|
@ -212,6 +212,7 @@ const routeMap: RouteMap = {
|
|||||||
"plugin_editor_markdown": "/plugin/editor/markdown",
|
"plugin_editor_markdown": "/plugin/editor/markdown",
|
||||||
"plugin_editor_quill": "/plugin/editor/quill",
|
"plugin_editor_quill": "/plugin/editor/quill",
|
||||||
"plugin_excel": "/plugin/excel",
|
"plugin_excel": "/plugin/excel",
|
||||||
|
"plugin_gantt": "/plugin/gantt",
|
||||||
"plugin_icon": "/plugin/icon",
|
"plugin_icon": "/plugin/icon",
|
||||||
"plugin_map": "/plugin/map",
|
"plugin_map": "/plugin/map",
|
||||||
"plugin_pdf": "/plugin/pdf",
|
"plugin_pdf": "/plugin/pdf",
|
||||||
|
2
src/typings/elegant-router.d.ts
vendored
2
src/typings/elegant-router.d.ts
vendored
@ -66,6 +66,7 @@ declare module "@elegant-router/types" {
|
|||||||
"plugin_editor_markdown": "/plugin/editor/markdown";
|
"plugin_editor_markdown": "/plugin/editor/markdown";
|
||||||
"plugin_editor_quill": "/plugin/editor/quill";
|
"plugin_editor_quill": "/plugin/editor/quill";
|
||||||
"plugin_excel": "/plugin/excel";
|
"plugin_excel": "/plugin/excel";
|
||||||
|
"plugin_gantt": "/plugin/gantt";
|
||||||
"plugin_icon": "/plugin/icon";
|
"plugin_icon": "/plugin/icon";
|
||||||
"plugin_map": "/plugin/map";
|
"plugin_map": "/plugin/map";
|
||||||
"plugin_pdf": "/plugin/pdf";
|
"plugin_pdf": "/plugin/pdf";
|
||||||
@ -174,6 +175,7 @@ declare module "@elegant-router/types" {
|
|||||||
| "plugin_editor_markdown"
|
| "plugin_editor_markdown"
|
||||||
| "plugin_editor_quill"
|
| "plugin_editor_quill"
|
||||||
| "plugin_excel"
|
| "plugin_excel"
|
||||||
|
| "plugin_gantt"
|
||||||
| "plugin_icon"
|
| "plugin_icon"
|
||||||
| "plugin_map"
|
| "plugin_map"
|
||||||
| "plugin_pdf"
|
| "plugin_pdf"
|
||||||
|
173
src/views/plugin/gantt/ganntData.ts
Normal file
173
src/views/plugin/gantt/ganntData.ts
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
export const demoData = {
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
id: 11,
|
||||||
|
text: 'Soybean 架构设计',
|
||||||
|
type: 'project',
|
||||||
|
progress: 0,
|
||||||
|
open: true,
|
||||||
|
start_date: '2024-01-10 00:00',
|
||||||
|
duration: 12,
|
||||||
|
parent: 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 12,
|
||||||
|
text: '测试版本',
|
||||||
|
start_date: '2024-03-20 00:00',
|
||||||
|
type: 'project',
|
||||||
|
duration: 5,
|
||||||
|
render: 'split',
|
||||||
|
parent: '11',
|
||||||
|
progress: 0,
|
||||||
|
open: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 99,
|
||||||
|
text: '测试版本1 发布',
|
||||||
|
start_date: '2024-03-20 00:00',
|
||||||
|
end_date: '2024-03-25 00:00',
|
||||||
|
parent: '12',
|
||||||
|
progress: 0,
|
||||||
|
open: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 98,
|
||||||
|
text: '测试版本2 发布',
|
||||||
|
start_date: '2024-03-26 00:00',
|
||||||
|
duration: 4,
|
||||||
|
parent: '12',
|
||||||
|
progress: 0,
|
||||||
|
open: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 97,
|
||||||
|
text: '测试版本3 发布',
|
||||||
|
start_date: '2024-03-31 00:00',
|
||||||
|
duration: 10,
|
||||||
|
parent: '12',
|
||||||
|
progress: 0,
|
||||||
|
open: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 13,
|
||||||
|
text: '1.0 版本',
|
||||||
|
start_date: '2024-03-31 00:00',
|
||||||
|
type: 'project',
|
||||||
|
render: 'split',
|
||||||
|
parent: '11',
|
||||||
|
progress: 0.5,
|
||||||
|
open: false,
|
||||||
|
duration: 11
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 17,
|
||||||
|
text: '1.0正式发布',
|
||||||
|
start_date: '2024-03-31 00:00',
|
||||||
|
end_date: '2024-04-03 00:00',
|
||||||
|
parent: '13',
|
||||||
|
progress: 0,
|
||||||
|
open: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 18,
|
||||||
|
text: '1.0.1 版本',
|
||||||
|
start_date: '2024-04-03 00:00',
|
||||||
|
duration: 5,
|
||||||
|
parent: '13',
|
||||||
|
progress: 0,
|
||||||
|
open: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 19,
|
||||||
|
text: '1.0.2 版本',
|
||||||
|
start_date: '2024-04-08 00:00',
|
||||||
|
duration: 6,
|
||||||
|
parent: '13',
|
||||||
|
progress: 0,
|
||||||
|
open: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 20,
|
||||||
|
text: '1.0.3 版本',
|
||||||
|
start_date: '2024-04-16 00:00',
|
||||||
|
duration: 8,
|
||||||
|
parent: '13',
|
||||||
|
progress: 0,
|
||||||
|
open: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 31,
|
||||||
|
text: '1.0.4 版本',
|
||||||
|
start_date: '2024-04-17 00:00',
|
||||||
|
duration: 8,
|
||||||
|
parent: '13',
|
||||||
|
progress: 0,
|
||||||
|
open: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 32,
|
||||||
|
text: '1.0.5 版本',
|
||||||
|
start_date: '2024-04-26 00:00',
|
||||||
|
duration: 9,
|
||||||
|
parent: '13',
|
||||||
|
progress: 0,
|
||||||
|
open: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 33,
|
||||||
|
text: '1.0.9 版本',
|
||||||
|
start_date: '2024-05-05 00:00',
|
||||||
|
duration: 2,
|
||||||
|
parent: '13',
|
||||||
|
progress: 0,
|
||||||
|
open: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 14,
|
||||||
|
text: '1.1 版本',
|
||||||
|
start_date: '2024-05-07 00:00',
|
||||||
|
duration: 30,
|
||||||
|
parent: '11',
|
||||||
|
progress: 0,
|
||||||
|
open: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 15,
|
||||||
|
text: '1.2 版本',
|
||||||
|
start_date: '2024-06-06 00:00',
|
||||||
|
duration: 46,
|
||||||
|
parent: '11',
|
||||||
|
progress: 0,
|
||||||
|
open: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 16,
|
||||||
|
text: '1.3版本',
|
||||||
|
type: 'project',
|
||||||
|
render: 'split',
|
||||||
|
parent: '11',
|
||||||
|
progress: 0,
|
||||||
|
open: true,
|
||||||
|
start_date: '2024-07-22 00:00',
|
||||||
|
duration: 11
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 21,
|
||||||
|
text: '1.3.1版本',
|
||||||
|
start_date: '2024-07-22 00:00',
|
||||||
|
duration: 7,
|
||||||
|
parent: '16',
|
||||||
|
progress: 0,
|
||||||
|
open: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 22,
|
||||||
|
text: '1.3.2版本',
|
||||||
|
start_date: '2024-07-29 00:00',
|
||||||
|
duration: 7,
|
||||||
|
parent: '16',
|
||||||
|
progress: 0,
|
||||||
|
open: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
258
src/views/plugin/gantt/index.vue
Normal file
258
src/views/plugin/gantt/index.vue
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
<script setup lang="tsx">
|
||||||
|
import { onMounted, shallowRef } from 'vue';
|
||||||
|
import { gantt } from 'dhtmlx-gantt';
|
||||||
|
import type { ZoomLevels } from 'dhtmlx-gantt';
|
||||||
|
import 'dhtmlx-gantt/codebase/dhtmlxgantt.css';
|
||||||
|
import { demoData } from './ganntData';
|
||||||
|
|
||||||
|
type ZoomConfig = {
|
||||||
|
levels: ZoomLevels[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const schema = shallowRef('quarter');
|
||||||
|
|
||||||
|
const data = [
|
||||||
|
{
|
||||||
|
name: '天',
|
||||||
|
code: 'day'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '周',
|
||||||
|
code: 'week'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '月',
|
||||||
|
code: 'month'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '季',
|
||||||
|
code: 'quarter'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '年',
|
||||||
|
code: 'year'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const zoomConfig: ZoomConfig = {
|
||||||
|
levels: [
|
||||||
|
{
|
||||||
|
name: 'day',
|
||||||
|
scale_height: 60,
|
||||||
|
scales: [{ unit: 'day', step: 1, format: '%d %M' }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'week',
|
||||||
|
scale_height: 60,
|
||||||
|
scales: [
|
||||||
|
{
|
||||||
|
unit: 'week',
|
||||||
|
step: 1,
|
||||||
|
format(date: Date) {
|
||||||
|
const dateToStr = gantt.date.date_to_str('%m-%d');
|
||||||
|
const endDate = gantt.date.add(date, -6, 'day'); // 第几周
|
||||||
|
return `${dateToStr(endDate)} 至 ${dateToStr(date)}`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
unit: 'day',
|
||||||
|
step: 1,
|
||||||
|
format: '%d',
|
||||||
|
css(date: Date) {
|
||||||
|
if (date.getDay() === 0 || date.getDay() === 6) {
|
||||||
|
return 'day-item weekend weekend-border-bottom';
|
||||||
|
}
|
||||||
|
return 'day-item';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'month',
|
||||||
|
scale_height: 60,
|
||||||
|
min_column_width: 18,
|
||||||
|
scales: [
|
||||||
|
{ unit: 'month', format: '%Y-%m' },
|
||||||
|
{
|
||||||
|
unit: 'day',
|
||||||
|
step: 1,
|
||||||
|
format: '%d',
|
||||||
|
css(date: Date) {
|
||||||
|
if (date.getDay() === 0 || date.getDay() === 6) {
|
||||||
|
return 'day-item weekend weekend-border-bottom';
|
||||||
|
}
|
||||||
|
return 'day-item';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'quarter',
|
||||||
|
height: 60,
|
||||||
|
min_column_width: 110,
|
||||||
|
scales: [
|
||||||
|
{
|
||||||
|
unit: 'quarter',
|
||||||
|
step: 1,
|
||||||
|
format(date: Date) {
|
||||||
|
const yearStr = `${new Date(date).getFullYear()}年`;
|
||||||
|
const dateToStr = gantt.date.date_to_str('%M');
|
||||||
|
const endDate = gantt.date.add(gantt.date.add(date, 3, 'month'), -1, 'day');
|
||||||
|
return `${yearStr + dateToStr(date)} - ${dateToStr(endDate)}`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
unit: 'week',
|
||||||
|
step: 1,
|
||||||
|
format(date: Date) {
|
||||||
|
const dateToStr = gantt.date.date_to_str('%m-%d');
|
||||||
|
const endDate = gantt.date.add(date, 6, 'day');
|
||||||
|
return `${dateToStr(date)} 至 ${dateToStr(endDate)}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'year',
|
||||||
|
scale_height: 50,
|
||||||
|
min_column_width: 150,
|
||||||
|
scales: [
|
||||||
|
{ unit: 'year', step: 1, format: '%Y年' },
|
||||||
|
{ unit: 'month', format: '%Y-%m' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
function scrollInit() {
|
||||||
|
const nav = document.querySelector<HTMLElement>('.gantt_task')!;
|
||||||
|
const parNav = document.querySelector<HTMLElement>('.gantt_hor_scroll')!;
|
||||||
|
parNav.scrollLeft = 0;
|
||||||
|
let flag: boolean = false;
|
||||||
|
let downX: number = 0;
|
||||||
|
let scrollLeft: number = 0;
|
||||||
|
nav.addEventListener('mousedown', event => {
|
||||||
|
flag = true;
|
||||||
|
downX = event.clientX;
|
||||||
|
|
||||||
|
scrollLeft = event.offsetX;
|
||||||
|
});
|
||||||
|
nav.addEventListener('mousemove', event => {
|
||||||
|
if (flag) {
|
||||||
|
const moveX = event.clientX;
|
||||||
|
const scrollX = moveX - downX;
|
||||||
|
parNav.scrollLeft = scrollLeft - scrollX;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
nav.addEventListener('mouseup', () => {
|
||||||
|
flag = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
nav.addEventListener('mouseleave', () => {
|
||||||
|
flag = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function initGantt() {
|
||||||
|
gantt.config.grid_width = 350;
|
||||||
|
gantt.config.add_column = false;
|
||||||
|
|
||||||
|
gantt.config.autofit = false;
|
||||||
|
gantt.config.row_height = 60;
|
||||||
|
gantt.config.bar_height = 34;
|
||||||
|
|
||||||
|
gantt.config.auto_types = true;
|
||||||
|
gantt.config.xml_date = '%Y-%m-%d';
|
||||||
|
|
||||||
|
gantt.config.columns = [
|
||||||
|
{
|
||||||
|
name: 'text',
|
||||||
|
label: '项目名称',
|
||||||
|
tree: true,
|
||||||
|
width: '*'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'start_date',
|
||||||
|
label: '开始时间',
|
||||||
|
align: 'center',
|
||||||
|
width: 150
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
gantt.i18n.setLocale('cn');
|
||||||
|
gantt.init('gantt_here');
|
||||||
|
gantt.parse(demoData);
|
||||||
|
|
||||||
|
scrollInit();
|
||||||
|
|
||||||
|
gantt.ext.zoom.init(zoomConfig);
|
||||||
|
gantt.ext.zoom.setLevel(schema.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeTime(value: string) {
|
||||||
|
schema.value = value;
|
||||||
|
gantt.ext.zoom.setLevel(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
initGantt();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="my-gantt min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
|
||||||
|
<NCard
|
||||||
|
title="甘特图演示"
|
||||||
|
:bordered="false"
|
||||||
|
size="small"
|
||||||
|
content-class="flex-x-center"
|
||||||
|
class="sm:flex-1-hidden card-wrapper"
|
||||||
|
>
|
||||||
|
<template #header-extra>
|
||||||
|
<NTabs :value="schema" type="segment" animated size="small" class="relative w-320px" @update:value="changeTime">
|
||||||
|
<NTab v-for="item in data" :key="item.code" :name="item.code">
|
||||||
|
{{ item.name }}
|
||||||
|
</NTab>
|
||||||
|
</NTabs>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div id="gantt_here" class="gantt-container h-full w-full container"></div>
|
||||||
|
</NCard>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.my-gantt {
|
||||||
|
::v-deep .gantt-container {
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
height: 100%;
|
||||||
|
.gantt_task_content {
|
||||||
|
border-radius: 17px;
|
||||||
|
}
|
||||||
|
.gantt_task_line.gantt_project {
|
||||||
|
border-radius: 17px;
|
||||||
|
border: none;
|
||||||
|
--uno: bg-primary;
|
||||||
|
}
|
||||||
|
.gantt_task_line {
|
||||||
|
--uno: bg-primary-400;
|
||||||
|
border: none;
|
||||||
|
border-radius: 15px;
|
||||||
|
}
|
||||||
|
.gantt_grid_data .gantt_row.odd:hover,
|
||||||
|
.gantt_grid_data .gantt_row:hover {
|
||||||
|
--uno: bg-warning;
|
||||||
|
}
|
||||||
|
.gantt_task_row.gantt_selected .gantt_task_cell {
|
||||||
|
--uno: bg-warning;
|
||||||
|
}
|
||||||
|
.gantt_grid_data .gantt_row.gantt_selected,
|
||||||
|
.gantt_grid_data .gantt_row.odd.gantt_selected,
|
||||||
|
.gantt_task_row.gantt_selected {
|
||||||
|
--uno: bg-warning;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
Reference in New Issue
Block a user