mirror of
https://github.com/soybeanjs/soybean-admin.git
synced 2026-01-14 02:45:59 +08:00
Merge branch 'example' into feat/global-search
This commit is contained in:
@@ -56,9 +56,7 @@ function handleSelectMixMenu(menu: App.Global.Menu) {
|
||||
:theme-color="themeStore.themeColor"
|
||||
@select="handleSelectMixMenu"
|
||||
@toggle-sider-collapse="appStore.toggleSiderCollapse"
|
||||
>
|
||||
<slot></slot>
|
||||
</FirstLevelMenu>
|
||||
/>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -49,8 +49,11 @@ function handleSelectMixMenu(menu: App.Global.Menu) {
|
||||
}
|
||||
|
||||
function handleResetActiveMenu() {
|
||||
getActiveFirstLevelMenuKey();
|
||||
setDrawerVisible(false);
|
||||
|
||||
if (!appStore.mixSiderFixed) {
|
||||
getActiveFirstLevelMenuKey();
|
||||
}
|
||||
}
|
||||
|
||||
const selectedKey = computed(() => {
|
||||
|
||||
@@ -205,7 +205,8 @@ const local: App.I18n.Schema = {
|
||||
plugin_barcode: 'Barcode',
|
||||
plugin_pinyin: 'pinyin',
|
||||
plugin_excel: 'Excel',
|
||||
plugin_pdf: 'PDF preview'
|
||||
plugin_pdf: 'PDF preview',
|
||||
plugin_gantt: 'Gantt Chart'
|
||||
},
|
||||
page: {
|
||||
login: {
|
||||
|
||||
@@ -205,7 +205,8 @@ const local: App.I18n.Schema = {
|
||||
plugin_barcode: '条形码',
|
||||
plugin_pinyin: '拼音',
|
||||
plugin_excel: 'Excel',
|
||||
plugin_pdf: 'PDF 预览'
|
||||
plugin_pdf: 'PDF 预览',
|
||||
plugin_gantt: '甘特图'
|
||||
},
|
||||
page: {
|
||||
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_quill: () => import("@/views/plugin/editor/quill/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_map: () => import("@/views/plugin/map/index.vue"),
|
||||
plugin_pdf: () => import("@/views/plugin/pdf/index.vue"),
|
||||
|
||||
@@ -424,6 +424,16 @@ export const generatedRoutes: GeneratedRoute[] = [
|
||||
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',
|
||||
path: '/plugin/icon',
|
||||
|
||||
@@ -97,10 +97,13 @@ function transformElegantRouteToVueRoute(
|
||||
if (component) {
|
||||
if (isSingleLevelRoute(route)) {
|
||||
const { layout, view } = getSingleLevelRouteComponent(component);
|
||||
|
||||
|
||||
const singleLevelRoute: RouteRecordRaw = {
|
||||
path,
|
||||
component: layouts[layout],
|
||||
meta: {
|
||||
title: route.meta?.title || ''
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name,
|
||||
@@ -110,36 +113,35 @@ function transformElegantRouteToVueRoute(
|
||||
} as RouteRecordRaw
|
||||
]
|
||||
};
|
||||
|
||||
|
||||
return [singleLevelRoute];
|
||||
}
|
||||
|
||||
|
||||
if (isLayout(component)) {
|
||||
const layoutName = getLayoutName(component);
|
||||
|
||||
|
||||
vueRoute.component = layouts[layoutName];
|
||||
}
|
||||
|
||||
|
||||
if (isView(component)) {
|
||||
const viewName = getViewName(component);
|
||||
|
||||
|
||||
vueRoute.component = views[viewName];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error(`Error transforming route "${route.name}": ${error.toString()}`);
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
// add redirect to child
|
||||
if (children?.length && !vueRoute.redirect) {
|
||||
vueRoute.redirect = {
|
||||
name: children[0].name
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
if (children?.length) {
|
||||
const childRoutes = children.flatMap(child => transformElegantRouteToVueRoute(child, layouts, views));
|
||||
|
||||
@@ -210,6 +212,7 @@ const routeMap: RouteMap = {
|
||||
"plugin_editor_markdown": "/plugin/editor/markdown",
|
||||
"plugin_editor_quill": "/plugin/editor/quill",
|
||||
"plugin_excel": "/plugin/excel",
|
||||
"plugin_gantt": "/plugin/gantt",
|
||||
"plugin_icon": "/plugin/icon",
|
||||
"plugin_map": "/plugin/map",
|
||||
"plugin_pdf": "/plugin/pdf",
|
||||
|
||||
2
src/typings/components.d.ts
vendored
2
src/typings/components.d.ts
vendored
@@ -46,7 +46,6 @@ declare module 'vue' {
|
||||
LangSwitch: typeof import('./../components/common/lang-switch.vue')['default']
|
||||
LookForward: typeof import('./../components/custom/look-forward.vue')['default']
|
||||
MenuToggler: typeof import('./../components/common/menu-toggler.vue')['default']
|
||||
NAlert: typeof import('naive-ui')['NAlert']
|
||||
NBreadcrumb: typeof import('naive-ui')['NBreadcrumb']
|
||||
NBreadcrumbItem: typeof import('naive-ui')['NBreadcrumbItem']
|
||||
NButton: typeof import('naive-ui')['NButton']
|
||||
@@ -70,7 +69,6 @@ declare module 'vue' {
|
||||
NFormItemGi: typeof import('naive-ui')['NFormItemGi']
|
||||
NGi: typeof import('naive-ui')['NGi']
|
||||
NGrid: typeof import('naive-ui')['NGrid']
|
||||
NGridItem: typeof import('naive-ui')['NGridItem']
|
||||
NInput: typeof import('naive-ui')['NInput']
|
||||
NInputGroup: typeof import('naive-ui')['NInputGroup']
|
||||
NInputNumber: typeof import('naive-ui')['NInputNumber']
|
||||
|
||||
6
src/typings/elegant-router.d.ts
vendored
6
src/typings/elegant-router.d.ts
vendored
@@ -66,6 +66,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/gantt";
|
||||
"plugin_icon": "/plugin/icon";
|
||||
"plugin_map": "/plugin/map";
|
||||
"plugin_pdf": "/plugin/pdf";
|
||||
@@ -88,7 +89,7 @@ declare module "@elegant-router/types" {
|
||||
|
||||
/**
|
||||
* custom route key
|
||||
*/
|
||||
*/
|
||||
export type CustomRouteKey = Extract<
|
||||
RouteKey,
|
||||
| "root"
|
||||
@@ -109,7 +110,7 @@ declare module "@elegant-router/types" {
|
||||
|
||||
/**
|
||||
* the generated route key
|
||||
*/
|
||||
*/
|
||||
export type GeneratedRouteKey = Exclude<RouteKey, CustomRouteKey>;
|
||||
|
||||
/**
|
||||
@@ -174,6 +175,7 @@ declare module "@elegant-router/types" {
|
||||
| "plugin_editor_markdown"
|
||||
| "plugin_editor_quill"
|
||||
| "plugin_excel"
|
||||
| "plugin_gantt"
|
||||
| "plugin_icon"
|
||||
| "plugin_map"
|
||||
| "plugin_pdf"
|
||||
|
||||
173
src/views/plugin/gantt/data.ts
Normal file
173
src/views/plugin/gantt/data.ts
Normal file
@@ -0,0 +1,173 @@
|
||||
import type { Task } from 'dhtmlx-gantt';
|
||||
|
||||
export const ganttTasks: Task[] = [
|
||||
{
|
||||
id: 11,
|
||||
text: 'Soybean 架构设计',
|
||||
type: 'project',
|
||||
progress: 0,
|
||||
open: true,
|
||||
start_date: new Date('2024-01-10 00:00'),
|
||||
duration: 12,
|
||||
parent: 0
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
text: '测试版本',
|
||||
start_date: new Date('2024-03-20 00:00'),
|
||||
type: 'project',
|
||||
duration: 5,
|
||||
render: 'split',
|
||||
parent: '11',
|
||||
progress: 0,
|
||||
open: true
|
||||
},
|
||||
{
|
||||
id: 99,
|
||||
text: '测试版本1 发布',
|
||||
start_date: new Date('2024-03-20 00:00'),
|
||||
end_date: new Date('2024-03-25 00:00'),
|
||||
parent: '12',
|
||||
progress: 0,
|
||||
open: true
|
||||
},
|
||||
{
|
||||
id: 98,
|
||||
text: '测试版本2 发布',
|
||||
start_date: new Date('2024-03-26 00:00'),
|
||||
duration: 4,
|
||||
parent: '12',
|
||||
progress: 0,
|
||||
open: true
|
||||
},
|
||||
{
|
||||
id: 97,
|
||||
text: '测试版本3 发布',
|
||||
start_date: new Date('2024-03-31 00:00'),
|
||||
duration: 10,
|
||||
parent: '12',
|
||||
progress: 0,
|
||||
open: true
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
text: '1.0 版本',
|
||||
start_date: new 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: new Date('2024-03-31 00:00'),
|
||||
end_date: new Date('2024-04-03 00:00'),
|
||||
parent: '13',
|
||||
progress: 0,
|
||||
open: true
|
||||
},
|
||||
{
|
||||
id: 18,
|
||||
text: '1.0.1 版本',
|
||||
start_date: new Date('2024-04-03 00:00'),
|
||||
duration: 5,
|
||||
parent: '13',
|
||||
progress: 0,
|
||||
open: true
|
||||
},
|
||||
{
|
||||
id: 19,
|
||||
text: '1.0.2 版本',
|
||||
start_date: new Date('2024-04-08 00:00'),
|
||||
duration: 6,
|
||||
parent: '13',
|
||||
progress: 0,
|
||||
open: true
|
||||
},
|
||||
{
|
||||
id: 20,
|
||||
text: '1.0.3 版本',
|
||||
start_date: new Date('2024-04-16 00:00'),
|
||||
duration: 8,
|
||||
parent: '13',
|
||||
progress: 0,
|
||||
open: true
|
||||
},
|
||||
{
|
||||
id: 31,
|
||||
text: '1.0.4 版本',
|
||||
start_date: new Date('2024-04-17 00:00'),
|
||||
duration: 8,
|
||||
parent: '13',
|
||||
progress: 0,
|
||||
open: true
|
||||
},
|
||||
{
|
||||
id: 32,
|
||||
text: '1.0.5 版本',
|
||||
start_date: new Date('2024-04-26 00:00'),
|
||||
duration: 9,
|
||||
parent: '13',
|
||||
progress: 0,
|
||||
open: true
|
||||
},
|
||||
{
|
||||
id: 33,
|
||||
text: '1.0.9 版本',
|
||||
start_date: new Date('2024-05-05 00:00'),
|
||||
duration: 2,
|
||||
parent: '13',
|
||||
progress: 0,
|
||||
open: true
|
||||
},
|
||||
{
|
||||
id: 14,
|
||||
text: '1.1 版本',
|
||||
start_date: new Date('2024-05-07 00:00'),
|
||||
duration: 30,
|
||||
parent: '11',
|
||||
progress: 0,
|
||||
open: true
|
||||
},
|
||||
{
|
||||
id: 15,
|
||||
text: '1.2 版本',
|
||||
start_date: new 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: new Date('2024-07-22 00:00'),
|
||||
duration: 11
|
||||
},
|
||||
{
|
||||
id: 21,
|
||||
text: '1.3.1版本',
|
||||
start_date: new Date('2024-07-22 00:00'),
|
||||
duration: 7,
|
||||
parent: '16',
|
||||
progress: 0,
|
||||
open: true
|
||||
},
|
||||
{
|
||||
id: 22,
|
||||
text: '1.3.2版本',
|
||||
start_date: new Date('2024-07-29 00:00'),
|
||||
duration: 7,
|
||||
parent: '16',
|
||||
progress: 0,
|
||||
open: true
|
||||
}
|
||||
];
|
||||
208
src/views/plugin/gantt/index.vue
Normal file
208
src/views/plugin/gantt/index.vue
Normal file
@@ -0,0 +1,208 @@
|
||||
<script setup lang="tsx">
|
||||
import { onMounted, shallowRef } from 'vue';
|
||||
import { gantt } from 'dhtmlx-gantt';
|
||||
import type { GanttConfigOptions, ZoomLevels } from 'dhtmlx-gantt';
|
||||
import 'dhtmlx-gantt/codebase/dhtmlxgantt.css';
|
||||
import { ganttTasks } from './data';
|
||||
|
||||
const ganttRef = shallowRef<HTMLElement>();
|
||||
|
||||
type TimeType = 'day' | 'week' | 'month' | 'quarter' | 'year';
|
||||
|
||||
const timeType = shallowRef<TimeType>('quarter');
|
||||
|
||||
interface TimeData {
|
||||
name: string;
|
||||
code: TimeType;
|
||||
}
|
||||
|
||||
const data: TimeData[] = [
|
||||
{
|
||||
name: '天',
|
||||
code: 'day'
|
||||
},
|
||||
{
|
||||
name: '周',
|
||||
code: 'week'
|
||||
},
|
||||
{
|
||||
name: '月',
|
||||
code: 'month'
|
||||
},
|
||||
{
|
||||
name: '季',
|
||||
code: 'quarter'
|
||||
},
|
||||
{
|
||||
name: '年',
|
||||
code: 'year'
|
||||
}
|
||||
];
|
||||
|
||||
function initGantt() {
|
||||
if (!ganttRef.value) return;
|
||||
|
||||
const config: Partial<GanttConfigOptions> = {
|
||||
grid_width: 350,
|
||||
add_column: false,
|
||||
autofit: false,
|
||||
row_height: 60,
|
||||
bar_height: 34,
|
||||
auto_types: true,
|
||||
xml_date: '%Y-%m-%d',
|
||||
columns: [
|
||||
{
|
||||
name: 'text',
|
||||
label: '项目名称',
|
||||
tree: true,
|
||||
width: '*'
|
||||
},
|
||||
{
|
||||
name: 'start_date',
|
||||
label: '开始时间',
|
||||
align: 'center',
|
||||
width: 150
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
Object.assign(gantt.config, config);
|
||||
|
||||
gantt.i18n.setLocale('cn');
|
||||
gantt.init(ganttRef.value);
|
||||
gantt.parse({ data: ganttTasks });
|
||||
|
||||
const zoomLevels: ZoomLevels[] = [
|
||||
{
|
||||
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' }
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
gantt.ext.zoom.init({
|
||||
levels: zoomLevels
|
||||
});
|
||||
gantt.ext.zoom.setLevel(timeType.value);
|
||||
}
|
||||
|
||||
function changeTime(value: TimeType) {
|
||||
timeType.value = value;
|
||||
gantt.ext.zoom.setLevel(value);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initGantt();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="overflow-hidden lt-sm:overflow-auto">
|
||||
<NCard
|
||||
title="甘特图演示"
|
||||
:bordered="false"
|
||||
size="small"
|
||||
content-class="overflow-y-hidden overflow-x-auto"
|
||||
class="h-full card-wrapper"
|
||||
>
|
||||
<template #header-extra>
|
||||
<NTabs
|
||||
:value="timeType"
|
||||
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 ref="ganttRef" class="size-full min-w-800px"></div>
|
||||
</NCard>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
Reference in New Issue
Block a user