diff --git a/src/plugins/resource-schedule-table/resource-schedule-table.controller.ts b/src/plugins/resource-schedule-table/resource-schedule-table.controller.ts index 0dd0c6b092459e02ea6d1c30b9a20cc6b5b5e128..eea7f2b695e60b81eb79474375db86f34642f6fb 100644 --- a/src/plugins/resource-schedule-table/resource-schedule-table.controller.ts +++ b/src/plugins/resource-schedule-table/resource-schedule-table.controller.ts @@ -1,3 +1,254 @@ -import { ControlController } from '@ibiz-template/runtime'; +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable array-callback-return */ +import { + ContextMenuController, + ControlController, + ScriptFactory, + UIActionUtil, +} from '@ibiz-template/runtime'; +import { ISysCalendar } from '@ibiz/model-core'; +import { IResourceScheduleTableState } from './resource-schedule-table.state'; -export class ResourceScheduleTableController extends ControlController {} +export class ResourceScheduleTableController extends ControlController< + ISysCalendar, + IResourceScheduleTableState +> { + /** + * 上下文菜单控制器 + * @author lxm + * @date 2023-08-21 10:56:24 + * @type {{ [p: string]: ContextMenuController }} + */ + contextMenus: { [p: string]: ContextMenuController } = {}; + + protected initState(): void { + super.initState(); + this.state.tasks = []; + this.state.resources = []; + } + + async load(): Promise { + const { sysCalendarItems } = this.model; + const app = ibiz.hub.getApp(this.context.srfappid); + if (sysCalendarItems) { + const tempContext = this.context.clone(); + await app.deService.exec( + this.model.appDataEntityId!, + 'get', + tempContext, + this.params, + {}, + ); + const promises = sysCalendarItems.map(async item => { + const fetchAction = item.appDEDataSetId || 'fetchdefault'; + const res = await app.deService.exec( + item.appDataEntityId!, + fetchAction, + tempContext, + this.params, + {}, + ); + return { + id: item.id, + data: this.transformTasks(item.id!, res.data as IData[]), + }; + }); + + const resArray = await Promise.all(promises); + const tasks = resArray.find(item => { + return item.id === 'task'; + }); + const resources = resArray.find(item => { + return item.id === 'resource'; + }); + if (tasks) { + this.state.tasks = tasks.data; + } + if (resources) { + this.state.resources = resources.data; + } + } + } + + transformTasks(key: string, items: IData[]): IData[] { + let results: IData[] = []; + if (this.controlParams[`${key}fillmap`]) { + const fillmap = JSON.parse(this.controlParams[`${key}fillmap`]); + results = items.map(item => { + const tempItem: IData = {}; + Object.keys(fillmap).forEach(_key => { + if (_key === 'start' || _key === 'end') { + tempItem[_key] = new Date(item[fillmap[_key]]); + } else { + tempItem[_key] = item[fillmap[_key]]; + } + }); + tempItem.data = item; + return tempItem; + }); + } + return results; + } + + protected async onCreated(): Promise { + await super.onCreated(); + + this.model.sysCalendarItems?.forEach((item: IData) => { + if (item.decontextMenu?.detoolbarItems?.length) { + this.contextMenus[item.id!] = new ContextMenuController( + item.decontextMenu, + this.context, + this.params, + this.ctx, + ); + } + }); + const { controls = [] } = this.model; + const quickToolbar = controls.find( + x => x.name === `${this.model.name!}_quicktoolbar`, + ); + if (quickToolbar) { + this.contextMenus[quickToolbar.id!] = new ContextMenuController( + quickToolbar, + this.context, + this.params, + this.ctx, + ); + } + + // 上下文菜单控制器初始化 + await Promise.all( + Object.values(this.contextMenus).map(menu => menu.created()), + ); + + await this.load(); + } + + /** + * 计算是否存在自定义Htmljiaoben + * + * @param {string} type + * @return {*} {(string | undefined)} + * @memberof ResourceScheduleTableController + */ + calcHasRender(type: string): string | undefined { + const tempItem = this.model.sysCalendarItems?.find(item => { + return item.id === type; + }); + if ( + tempItem?.controlRenders?.length && + tempItem.controlRenders[0].layoutPanelModel + ) { + let scriptCode = tempItem.controlRenders[0].layoutPanelModel; + // 兼容单行脚本 + if (!scriptCode.includes('return')) { + scriptCode = `return (${scriptCode})`; + } + return scriptCode; + } + } + + /** + * 获取自定义Html + * + * @param {string} type + * @param {*} [data={}] + * @return {*} {string} + * @memberof ResourceScheduleTableController + */ + getCustomHtml(scriptCode: string, data: any = {}): string { + return ScriptFactory.execScriptFn( + { + data, + context: this.context, + params: this.params, + controller: this, + ctrl: this, + view: this.view, + }, + scriptCode, + { isAsync: false }, + ) as string; + } + + /** + * 保存 + * + * @param {IData} [config={}] + * @param {IData[]} [resources=[]] + * @param {IData[]} [tasks=[]] + * @return {*} {boolean} + * @memberof ResourceScheduleTableController + */ + async onSave( + config: IData = {}, + resources: IData[] = [], + tasks: IData[] = [], + ): Promise { + console.log('触发数据保存事件', config, resources, tasks); + await this.startLoading(); + // todo + return new Promise((resolve, reject) => { + setTimeout(() => { + this.endLoading(); + resolve(true); + }, 1500); + }); + } + + /** + * 另存为 + * + * @param {IData} [config={}] + * @param {IData[]} [resources=[]] + * @param {IData[]} [tasks=[]] + * @return {*} {boolean} + * @memberof ResourceScheduleTableController + */ + async onSaveAs( + config: IData = {}, + resources: IData[] = [], + tasks: IData[] = [], + ): Promise { + console.log('触发数据另存为事件', config, resources, tasks); + await this.startLoading(); + // todo + return new Promise((resolve, reject) => { + setTimeout(() => { + this.endLoading(); + resolve(true); + }, 1500); + }); + } + + /** + * 执行界面行为 + * + * @param {string} uiActionId + * @param {*} item + * @param {MouseEvent} event + * @param {string} appId + * @return {*} {Promise} + * @memberof ResourceScheduleTableController + */ + async doUIAction( + uiActionId: string, + item: any, + event: MouseEvent, + appId: string, + ): Promise { + const eventArgs = this.getEventArgs(); + await UIActionUtil.exec( + uiActionId!, + { + ...eventArgs, + data: [item.deData], + context: this.context.clone(), + params: this.params, + event, + }, + appId, + ); + } +} diff --git a/src/plugins/resource-schedule-table/resource-schedule-table.scss b/src/plugins/resource-schedule-table/resource-schedule-table.scss index 10c8c9cb2efefab84e2b735594d664238832aaf5..4b8ae87421f5aca0630b5ebf1913d8ff28780150 100644 --- a/src/plugins/resource-schedule-table/resource-schedule-table.scss +++ b/src/plugins/resource-schedule-table/resource-schedule-table.scss @@ -1,13 +1,25 @@ @include b(resource-schedule-table) { - // 样式 - width: 100%; - height: 100%; -} - -.ibiz-panel-container--container_table { - >.ibiz-row { - >.ibiz-col { - height: 100%; - } - } -} \ No newline at end of file + // 样式 + width: 100%; + height: 100%; + } + + .ibiz-panel-container--container_table { + >.ibiz-row { + >.ibiz-col { + height: 100%; + } + } + } + + .task-item-content{ + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + @include b(resource-schedule-table-context-menu) { + .mx-context-menu-scroll{ + position: unset; + pointer-events: unset; + } + } \ No newline at end of file diff --git a/src/plugins/resource-schedule-table/resource-schedule-table.state.ts b/src/plugins/resource-schedule-table/resource-schedule-table.state.ts new file mode 100644 index 0000000000000000000000000000000000000000..7d0027c54d03a2a488aeb716c354cae100bb9595 --- /dev/null +++ b/src/plugins/resource-schedule-table/resource-schedule-table.state.ts @@ -0,0 +1,19 @@ +import { IControlState } from '@ibiz-template/runtime'; + +export interface IResourceScheduleTableState extends IControlState { + /** + * 任务 + * + * @type {IData[]} + * @memberof ResourceScheduleTableState + */ + tasks: IData[]; + + /** + * 资源数据 + * + * @type {IData[]} + * @memberof ResourceScheduleTableState + */ + resources: IData[]; +} diff --git a/src/plugins/resource-schedule-table/resource-schedule-table.tsx b/src/plugins/resource-schedule-table/resource-schedule-table.tsx index 650b5fc2aeb92ba7d99475db976b79d8e3c93545..9d4c59c753e98db69f999114fffd36adb4571422 100644 --- a/src/plugins/resource-schedule-table/resource-schedule-table.tsx +++ b/src/plugins/resource-schedule-table/resource-schedule-table.tsx @@ -1,8 +1,23 @@ -import { defineComponent, PropType, reactive } from 'vue'; +/* eslint-disable @typescript-eslint/explicit-function-return-type */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { defineComponent, PropType, resolveComponent } from 'vue'; import { useControlController, useNamespace } from '@ibiz-template/vue3-util'; -import { IControl } from '@ibiz/model-core'; -import { IControlProvider } from '@ibiz-template/runtime'; +import { + IControl, + IDETBGroupItem, + IDETBRawItem, + IDETBUIActionItem, + IDEToolbarItem, + ISysCalendarItem, +} from '@ibiz/model-core'; +import { + IButtonContainerState, + IButtonState, + IControlProvider, +} from '@ibiz-template/runtime'; +import { MenuItem } from '@imengyu/vue3-context-menu'; import { ResourceScheduleTableController } from './resource-schedule-table.controller'; +import { IGlobalConfig } from '../resource-scheduler/interface'; import './resource-schedule-table.scss'; export const ResourceScheduleTable = defineComponent({ @@ -32,212 +47,252 @@ export const ResourceScheduleTable = defineComponent({ ); const ns = useNamespace('resource-schedule-table'); - const resources = reactive([ - { - id: 'room-a', - name: 'A会议室', - data: { - capacity: 50, - equipment: ['投影仪', '音响系统', '白板'], - location: '主楼3层', - }, - }, - { - id: 'room-b', - name: 'B会议室', - data: { - capacity: 30, - equipment: ['视频会议系统', '投影仪'], - location: '主楼2层', - }, - }, - { - id: 'room-c', - name: 'C会议室', - data: { - capacity: 100, - equipment: ['大屏显示器', '音响系统', '无线麦克风', '投影仪'], - location: '副楼1层', - }, - }, - ]); - - const tasks = reactive([ - { - id: 'course-001', - name: '项目管理基础', - start: new Date('2025-09-30T09:00:00'), - end: new Date('2025-09-30T10:30:00'), - resourceId: 'room-a', - data: { instructor: '王教授', students: 25, type: '理论课' }, - }, - { - id: 'course-002', - name: '需求工程实践', - start: new Date('2025-09-30T10:00:00'), - end: new Date('2025-09-30T12:00:00'), - resourceId: 'room-a', - data: { instructor: '李博士', students: 20, type: '实践课' }, - }, - { - id: 'course-003', - name: '高级架构设计', - start: new Date('2025-09-30T22:00:00'), - end: new Date('2025-10-01T06:00:00'), - resourceId: 'room-b', - data: { instructor: '张专家', students: 15, type: '研讨会' }, - }, - { - id: 'course-004', - name: '数据库优化技巧', - start: new Date('2025-10-01T14:00:00'), - end: new Date('2025-10-01T16:00:00'), - resourceId: 'room-c', - data: { instructor: '赵工程师', students: 60, type: '专题讲座' }, - }, - { - id: 'course-005', - name: '前端框架应用', - start: new Date('2025-10-02T09:00:00'), - end: new Date('2025-10-02T18:00:00'), - resourceId: 'room-a', - data: { instructor: '陈讲师', students: 30, type: '实训课' }, - }, - { - id: 'course-006', - name: '微服务架构', - start: new Date('2025-10-02T10:00:00'), - end: new Date('2025-10-02T17:00:00'), - resourceId: 'room-b', - data: { instructor: '刘高工', students: 25, type: '深度讲解' }, - }, - { - id: 'course-007', - name: '软件测试方法', - start: new Date('2025-10-03T16:00:00'), - end: new Date('2025-10-04T10:00:00'), - resourceId: 'room-a', - data: { instructor: '孙老师', students: 20, type: '实验课' }, - }, - { - id: 'course-008', - name: 'DevOps实战', - start: new Date('2025-10-04T09:00:00'), - end: new Date('2025-10-04T15:00:00'), - resourceId: 'room-c', - data: { instructor: '周总监', students: 45, type: '工作坊' }, - }, - { - id: 'course-009', - name: '移动应用开发', - start: new Date('2025-10-05T11:00:00'), - end: new Date('2025-10-05T13:00:00'), - resourceId: 'room-b', - data: { instructor: '吴专家', students: 18, type: '技术分享' }, - }, - { - id: 'course-010', - name: '信息安全防护', - start: new Date('2025-10-05T12:00:00'), - end: new Date('2025-10-05T14:00:00'), - resourceId: 'room-b', - data: { instructor: '郑教授', students: 22, type: '专题报告' }, - }, - { - id: 'course-011', - name: '用户体验设计', - start: new Date('2025-10-06T10:00:00'), - end: new Date('2025-10-06T12:00:00'), - resourceId: 'room-a', - data: { instructor: '钱设计师', students: 28, type: '案例分析' }, - }, - { - id: 'course-012', - name: '云计算平台应用', - start: new Date('2025-10-07T00:00:00'), - end: new Date('2025-10-07T05:00:00'), - resourceId: 'room-b', - data: { instructor: '范冰', students: 35, type: '实操训练' }, - }, - { - id: 'course-013', - name: '人工智能导论', - start: new Date('2025-10-07T09:00:00'), - end: new Date('2025-10-07T17:00:00'), - resourceId: 'room-c', - data: { instructor: '林教授', students: 80, type: '公开课' }, - }, - { - id: 'course-014', - name: '大数据分析', - start: new Date('2025-10-08T09:00:00'), - end: new Date('2025-10-08T11:00:00'), - resourceId: 'room-a', - data: { instructor: '黄博士', students: 26, type: '理论课' }, - }, - { - id: 'course-015', - name: '敏捷开发实践', - start: new Date('2025-10-09T22:00:00'), - end: new Date('2025-10-10T08:00:00'), - resourceId: 'room-b', - data: { instructor: '许教练', students: 30, type: '工作坊' }, - }, - { - id: 'course-016', - name: '区块链技术', - start: new Date('2025-10-10T13:00:00'), - end: new Date('2025-10-10T15:00:00'), - resourceId: 'room-c', - data: { instructor: '何专家', students: 40, type: '技术讲座' }, - }, - { - id: 'course-017', - name: '毕业设计指导', - start: new Date('2025-10-11T14:00:00'), - end: new Date('2025-10-12T10:00:00'), - resourceId: 'room-b', - data: { instructor: '邓教授', students: 12, type: '辅导课' }, - }, - { - id: 'course-018', - name: '学术论文写作', - start: new Date('2025-10-12T09:00:00'), - end: new Date('2025-10-12T17:00:00'), - resourceId: 'room-a', - data: { instructor: '胡研究员', students: 22, type: '专题课' }, - }, - { - id: 'course-019', - name: '职业规划讲座', - start: new Date('2025-10-13T10:00:00'), - end: new Date('2025-10-13T16:00:00'), - resourceId: 'room-b', - data: { instructor: '郭HR', students: 100, type: '公开讲座' }, - }, - { - id: 'course-020', - name: '学期总结大会', - start: new Date('2025-10-14T15:00:00'), - end: new Date('2025-10-15T10:00:00'), - resourceId: 'room-c', - data: { instructor: '校领导', students: 200, type: '全校会议' }, - }, - ]); + // *上下文菜单相关 / + + let ContextMenu: IData; + + const iBizRawItem = resolveComponent('IBizRawItem'); + const iBizIcon = resolveComponent('IBizIcon'); + + c.evt.on('onMounted', () => { + // 有上下文菜单时加载组件 + if (Object.values(c.contextMenus).length > 0) { + const importMenu = () => import('@imengyu/vue3-context-menu'); + importMenu().then(value => { + ContextMenu = value.default; + if (ContextMenu.default && !ContextMenu.showContextMenu) { + ContextMenu = ContextMenu.default; + } + }); + } + }); + + /** + * 计算上下文菜单组件配置项集合 + */ + const calcContextMenuItems = ( + toolbarItems: IDEToolbarItem[], + calendarData: any, + evt: MouseEvent, + menuState: IButtonContainerState, + ): MenuItem[] => { + const result: MenuItem[] = []; + toolbarItems.forEach(item => { + if (item.itemType === 'SEPERATOR') { + result.push({ + divided: 'self', + }); + return; + } + + const buttonState = menuState[item.id!] as IButtonState; + if (buttonState && !buttonState.visible) { + return; + } + + // 除分隔符之外的公共部分 + let menuItem: MenuItem | undefined = {}; + if (item.showCaption && item.caption) { + menuItem.label = item.caption; + } + if (item.sysImage && item.showIcon) { + menuItem.icon = () as any; + } + + // 界面行为项 + if (item.itemType === 'DEUIACTION') { + menuItem.disabled = buttonState.disabled; + menuItem.clickClose = true; + const { uiactionId } = item as IDETBUIActionItem; + if (uiactionId) { + menuItem.onClick = () => { + c.doUIAction(uiactionId, calendarData, evt, item.appId); + }; + } + } else if (item.itemType === 'RAWITEM') { + const { rawItem } = item as IDETBRawItem; + if (rawItem) { + menuItem.label = ( + + ) as any; + } + } else if (item.itemType === 'ITEMS') { + // 分组项绘制子菜单 + const group = item as IDETBGroupItem; + if (group.detoolbarItems?.length) { + menuItem.children = calcContextMenuItems( + group.detoolbarItems!, + calendarData, + evt, + menuState, + ); + } + // 分组项配置界面行为组 + if (group.uiactionGroup && group.groupExtractMode) { + const menuItems = group.uiactionGroup.uiactionGroupDetails + ?.filter(detail => { + const detailState: IButtonState = menuState[detail.id!]; + return detailState.visible; + }) + .map(detail => { + const detailState: IButtonState = menuState[detail.id!]; + const { sysImage } = detail as IData; + return { + label: detail.showCaption ? detail.caption : undefined, + icon: detail.showIcon ? ( + + ) : undefined, + disabled: detailState.disabled, + clickableWhenHasChildren: true, + onClick: () => { + ContextMenu.closeContextMenu(); + c.doUIAction( + detail.uiactionId!, + calendarData, + evt, + detail.appId, + ); + }, + }; + }); + switch (group.groupExtractMode) { + case 'ITEMS': + menuItem.children = menuItems as any; + break; + case 'ITEMX': + if (menuItems) { + menuItem = menuItems[0] as any; + if (menuItem) { + menuItem.children = menuItems.slice(1) as any; + } + } + break; + case 'ITEM': + default: + menuItem = undefined; + if (menuItems) { + result.push(...(menuItems as any)); + } + break; + } + } + } + if (menuItem) { + result.push(menuItem); + } + }); + + return result; + }; + + /** + * 节点右键菜单点击事件 + */ + const onNodeContextmenu = async ( + type: string, + item: any, + evt: MouseEvent, + ) => { + // 阻止原生浏览器右键菜单打开 + evt.preventDefault(); + evt.stopPropagation(); + let targetToolbar: IData; + let contextMenuC: IData; + if (type === 'blank') { + const { controls = [] } = c.model; + const quickToolbar = controls.find( + (x: any) => x.name === `${c.model.name!}_quicktoolbar`, + ); + if (!quickToolbar) { + return; + } + targetToolbar = quickToolbar; + contextMenuC = c.contextMenus[quickToolbar.id!]; + } else { + const { sysCalendarItems } = c.model; + const targetCalendarItem = sysCalendarItems?.find( + (_item: ISysCalendarItem) => { + return _item.id === type; + }, + ); + if (!targetCalendarItem?.decontextMenu) { + return; + } + targetToolbar = targetCalendarItem.decontextMenu; + contextMenuC = c.contextMenus[targetCalendarItem.id!]; + } + + if (!contextMenuC || !contextMenuC.model.detoolbarItems) { + return; + } + + // 更新菜单的权限状态 + await contextMenuC.calcButtonState(item, targetToolbar.appDataEntityId, { + view: c.view, + ctrl: c, + }); + const menuState = contextMenuC.state.buttonsState; + + const menus: MenuItem[] = calcContextMenuItems( + contextMenuC.model.detoolbarItems, + item, + evt, + menuState as IButtonContainerState, + ); + if (!menus.length) { + return; + } + + ContextMenu.showContextMenu({ + x: evt.x, + y: evt.y, + customClass: ns.b('context-menu'), + items: menus, + zIndex: 9999, + }); + }; + return { c, ns, - resources, - tasks, + onNodeContextmenu, }; }, render() { return (
+ resources={this.c.state.resources} + tasks={this.c.state.tasks} + caption={this.modelData.logicName} + onSave={this.c.onSave.bind(this.c)} + onSaveAs={this.c.onSaveAs.bind(this.c)} + onContextMenu={( + type: 'resource' | 'task' | 'blank', + config: IGlobalConfig, + data: IScheduleResource | IScheduleTask, + event: MouseEvent, + ) => this.onNodeContextmenu(type, data, event)} + > + {{ + resourceItem: (data: IScheduleResource) => { + const scriptCode = this.c.calcHasRender('resource'); + if (scriptCode) { + const html = this.c.getCustomHtml(scriptCode, data); + return
; + } + return
{data.name}
; + }, + taskItem: (data: IScheduleTask) => { + const scriptCode = this.c.calcHasRender('task'); + if (scriptCode) { + const html = this.c.getCustomHtml(scriptCode, data); + return
; + } + return
{data.name}
; + }, + }} +
); }, diff --git a/src/resource-scheduler/components/schedule-table/schedule-table-type.ts b/src/resource-scheduler/components/schedule-table/schedule-table-type.ts index 151f900e7c8664889ef19c616acc4da19e464a7f..f630e2e1e9001cd03623f34dd6056c323d41ca4f 100644 --- a/src/resource-scheduler/components/schedule-table/schedule-table-type.ts +++ b/src/resource-scheduler/components/schedule-table/schedule-table-type.ts @@ -281,6 +281,17 @@ export const ScheduleTableProps = { >, default: () => true, }, + + /** + * 标题属性 + * @description 用于显示标题 + * @type {String} 标题文本 + * @default Variables.default.caption 默认值来自全局变量配置 + */ + caption: { + type: String, + default: Variables.default.caption, + }, }; // 调度表格事件 diff --git a/src/resource-scheduler/components/schedule-table/schedule-table.scss b/src/resource-scheduler/components/schedule-table/schedule-table.scss index 1e894f39fffd5cd0f9a52dda973cc1cce72a7112..0d5ec9f0dfbd7cea3b3948112747b7882ffcb8a9 100644 --- a/src/resource-scheduler/components/schedule-table/schedule-table.scss +++ b/src/resource-scheduler/components/schedule-table/schedule-table.scss @@ -47,4 +47,23 @@ .schedule-toolbar-extra{ height: 40px; } + + .schedule-toolbar{ + display: flex; + gap: 16px; + align-items: center; + justify-content: space-between; + + } + + .schedule-table-title{ + flex-shrink: 0; + max-width: 200px; + padding-left:16px; + overflow: hidden; + font-size: 18px; + color: #1d1f23 ; + text-overflow: ellipsis; + white-space: nowrap; + } } \ No newline at end of file diff --git a/src/resource-scheduler/components/schedule-table/schedule-table.tsx b/src/resource-scheduler/components/schedule-table/schedule-table.tsx index a72680152c734be1af6cbdafebc346c4f7c04af0..d05e54a20f2d663392b8637e5c70954db2255179 100644 --- a/src/resource-scheduler/components/schedule-table/schedule-table.tsx +++ b/src/resource-scheduler/components/schedule-table/schedule-table.tsx @@ -12,7 +12,10 @@ import { useScheduleTableStyle, } from '../../hooks'; import { ScheduleTableEvent, ScheduleTableProps } from './schedule-table-type'; -import { useScheduleToolbarEvent } from '../../hooks/use-schedule-table'; +import { + useScheduleTableDataUpdate, + useScheduleToolbarEvent, +} from '../../hooks/use-schedule-table'; import './schedule-table.scss'; import { ScheduleToolbar } from '../schedule-toolbar/schedule-toolbar'; @@ -43,9 +46,11 @@ export const ScheduleTable = defineComponent({ coordinateElement, ); + useScheduleTableDataUpdate(props, coordinateElement); + // 处理工具栏事件 const { onRefresh, onSave, onSaveAs, onConfigRefresh } = - useScheduleToolbarEvent(coordinateElement); + useScheduleToolbarEvent(coordinateElement, emit); // 计算表格样式 const { headerStyle, bodyStyle } = useScheduleTableStyle( @@ -109,13 +114,18 @@ export const ScheduleTable = defineComponent({ render() { return (
-
- +
+
+
+ {this.caption} +
+ +
{this.$slots.extraToolbar ? (
{this.$slots.extraToolbar()} @@ -157,7 +167,9 @@ export const ScheduleTable = defineComponent({ left: `${resourceViewModel.left}px`, }} > - {resourceViewModel.data.name} + {this.$slots.resourceItem + ? this.$slots.resourceItem(resourceViewModel.data) + : resourceViewModel.data.name}
); }, @@ -193,7 +205,9 @@ export const ScheduleTable = defineComponent({ ) } > - {taskViewModel.data.name} + {this.$slots.taskItem + ? this.$slots.taskItem(taskViewModel.data) + : taskViewModel.data.name}
); }, diff --git a/src/resource-scheduler/components/schedule-toolbar/schedule-toolbar.scss b/src/resource-scheduler/components/schedule-toolbar/schedule-toolbar.scss index 79d419e2851bce7672683e538ffc30685d7e9e2a..c00df34eeb8a5357eabf67ae4f30e70ab2461e96 100644 --- a/src/resource-scheduler/components/schedule-toolbar/schedule-toolbar.scss +++ b/src/resource-scheduler/components/schedule-toolbar/schedule-toolbar.scss @@ -1,20 +1,19 @@ .schedule-toolbar-container { display: flex; + flex: 1; gap: 8px; align-items: center; - width: 100%; + min-width: calc(100% - 216px) ; } .schedule-toolbar-content { display: block; - flex: 1; - flex-wrap: nowrap; - gap: 8px; + flex: 1; align-items: center; - justify-content: left; height: 40px; - padding: 4px; + padding: 4px 0 ; overflow: hidden; + text-align: end; white-space: nowrap; transition: scroll 0.3s ease; scroll-behavior: smooth; @@ -41,6 +40,10 @@ border: 1px solid lightgray; } + &:last-child{ + margin-right: 0; + } + &.is-btn:hover { background-color: #f5f5f5; diff --git a/src/resource-scheduler/constant/vars.ts b/src/resource-scheduler/constant/vars.ts index 11a2bfc66c1897bb09b131e270b7217ef434c9cd..c0bb4a8d82380f63a36d26a6a123eb988986ee3f 100644 --- a/src/resource-scheduler/constant/vars.ts +++ b/src/resource-scheduler/constant/vars.ts @@ -17,6 +17,7 @@ export const Variables = { gridLineColor: '#eee', allowDrag: true, dragInterval: 1, + caption: '调度排程', }, time: { diff --git a/src/resource-scheduler/controller/render-layer.controller.ts b/src/resource-scheduler/controller/render-layer.controller.ts index 1f772603b08aa6a20a6bd1d95e5e89225a83de99..0446472a76637f6aa5ac793bdfe4b9b62ea32f2a 100644 --- a/src/resource-scheduler/controller/render-layer.controller.ts +++ b/src/resource-scheduler/controller/render-layer.controller.ts @@ -163,10 +163,10 @@ export class RenderLayerController { endDate.setHours(0, 0, 0, 0); // 检查任务的开始时间和结束时间是否有效 if (task.start >= task.end) return false; - // 如果任务的结束时间小于全局开始时间,则任务无效 - if (task.end < startDate) return false; - // 如果任务的开始时间大于全局结束时间,则任务无效 - if (task.start > endDate) return false; + // 如果任务的结束时间小于等于全局开始时间,则任务无效 + if (task.end <= startDate) return false; + // 如果任务的开始时间大于等于全局结束时间,则任务无效 + if (task.start >= endDate) return false; return true; } diff --git a/src/resource-scheduler/hooks/index.ts b/src/resource-scheduler/hooks/index.ts index bf6cca754ce85634a83421576f6e8f66a1a9001e..17f3a2d91be21e361b3c3c049adaa9fae2f945bf 100644 --- a/src/resource-scheduler/hooks/index.ts +++ b/src/resource-scheduler/hooks/index.ts @@ -4,6 +4,7 @@ export { useScheduleTableReSize, useScheduleTableStyle, useVirtualScroll, + useScheduleTableDataUpdate, } from './use-schedule-table'; export { diff --git a/src/resource-scheduler/hooks/use-schedule-table.ts b/src/resource-scheduler/hooks/use-schedule-table.ts index ee681dfa6acaacc2babd4cfb1a1ee90dcb62dfbf..18101cb68cf02f52716a29195af608c0f0b082ee 100644 --- a/src/resource-scheduler/hooks/use-schedule-table.ts +++ b/src/resource-scheduler/hooks/use-schedule-table.ts @@ -277,27 +277,40 @@ export function useVirtualScroll( // 工具栏事件 export function useScheduleToolbarEvent( coordinateElement: Ref, + emit: any, ): { onRefresh: (params: any) => void; onSave: (params: any) => void; onSaveAs: (params: any) => void; onConfigRefresh: (params: any) => void; } { - const { $config } = useStore(); + const { $config, $renderLayer } = useStore(); const { refresh } = useCommonAbility(coordinateElement); // 刷新 const onRefresh = (): void => { refresh(); }; - // 保存-todo + // 保存 const onSave = (): void => { - alert('点击触发保存'); + // alert('点击触发保存'); + emit( + 'save', + $config.getConfig(), + $renderLayer.getResources(), + $renderLayer.getTasks(), + ); }; - // 另存为-todo + // 另存为 const onSaveAs = (): void => { - alert('点击触发另存数据'); + // alert('点击触发另存数据'); + emit( + 'saveAs', + $config.getConfig(), + $renderLayer.getResources(), + $renderLayer.getTasks(), + ); }; // 配置刷新 @@ -316,3 +329,23 @@ export function useScheduleToolbarEvent( onConfigRefresh, }; } + +// 数据更新后刷新界面 +export function useScheduleTableDataUpdate( + props: any, + coordinateElement: Ref, +): void { + const { $config } = useStore(); + watch( + () => [props.resources, props.tasks], + () => { + console.log('触发刷新'); + const { refresh } = useCommonAbility(coordinateElement); + refresh($config.getConfig(), props.resources, props.tasks); + }, + { + deep: true, + immediate: true, + }, + ); +} diff --git a/src/resource-scheduler/hooks/use-schedule-toolbar.ts b/src/resource-scheduler/hooks/use-schedule-toolbar.ts index 10e76a60ef410bcf41e55df69bb9b7cd2c69bd9d..3d4afd34a429f58ff4eb8ea5afe982b3d63abf49 100644 --- a/src/resource-scheduler/hooks/use-schedule-toolbar.ts +++ b/src/resource-scheduler/hooks/use-schedule-toolbar.ts @@ -214,7 +214,7 @@ export function useScheduleToolbarReSize( if (scheduleToolbarContentRef.value) { visibleArrow.value = scheduleToolbarContentRef.value.scrollWidth > - scheduleToolbarContentRef.value.clientWidth; + scheduleToolbarContentRef.value.clientWidth + 64; } else { visibleArrow.value = false; } diff --git a/src/resource-scheduler/interface/i-ui-data.ts b/src/resource-scheduler/interface/i-ui-data.ts index 72a8e45309403e22ea41a9c6ebf62f805402280f..27ff016870be149f6b67ac749d97ce3555b0b2c6 100644 --- a/src/resource-scheduler/interface/i-ui-data.ts +++ b/src/resource-scheduler/interface/i-ui-data.ts @@ -73,7 +73,7 @@ export interface IScheduleResource { * @type {IScheduleTask[]} * @memberof IScheduleResource */ - tasks: IScheduleTask[]; + // tasks: IScheduleTask[]; /** * @description 原始数据