雨辰5日gantt

This commit is contained in:
严争鸣 2025-03-11 08:53:58 +08:00
parent a1bf52118e
commit a2b20d4b56
37 changed files with 5152 additions and 3308 deletions

186
public/baseConfig.js Normal file → Executable file
View File

@ -1,92 +1,94 @@
window['settings'] = { window['settings'] = {
apis: '/taishiView', apis: '/taishiView',
textConfigs: [], textConfigs: [],
baseMBDict: { baseMBDict: {
base: { base: {
icon: './images/icons/base/党政首脑机关.png', icon: './images/icons/base/党政首脑机关.png',
color: '#ea7354', color: '#ea7354',
model: './models/基地.glb', model: './models/基地.glb',
}, },
airport: { airport: {
icon: './images/icons/base/军用机场.png', icon: './images/icons/base/军用机场.png',
color: '#80b1d3', color: '#80b1d3',
model: './models/机场.glb', model: './models/机场.glb',
}, },
port: { port: {
icon: './images/icons/base/军用港口.png', icon: './images/icons/base/军用港口.png',
color: '#fcee82', color: '#fcee82',
model: './models/港口.glb', model: './models/港口.glb',
}, },
station: { station: {
icon: './images/icons/base/民用机场.png', icon: './images/icons/base/民用机场.png',
color: '#8dd3c7', color: '#8dd3c7',
model: './models/雷达.glb', model: './models/雷达.glb',
}, },
}, },
mbCountryDict: { mbCountryDict: {
美国: '#fff', 美国: '#fff',
中国: '#d00', 中国: '#d00',
日本: '#dd0', 日本: '#dd0',
韩国: '#00d', 韩国: '#00d',
}, },
mbDict: { mbDict: {
: { : {
icon: './images/icons/10-7600-0-侦察机.svg', icon: './images/icons/10-7600-0-侦察机.svg',
color: '#d00', color: '#d00',
model: './models/IDF.glb', model: './models/IDF.glb',
payload: 'airplaneConic', payload: 'airplaneConic',
}, },
: { : {
icon: './images/icons/10-5900-0-航空母舰.svg', icon: './images/icons/10-5900-0-航空母舰.svg',
color: '#ff0', color: '#ff0',
model: './models/驱逐舰2.glb', model: './models/驱逐舰2.glb',
payload: 'radar', payload: 'radar',
}, },
: { : {
icon: './images/icons/10-6100-0-驱逐舰.svg', icon: './images/icons/10-6100-0-驱逐舰.svg',
color: '#dd0', color: '#dd0',
model: './models/驱逐舰2.glb', model: './models/驱逐舰2.glb',
payload: 'radar', payload: 'radar',
}, },
}, },
map: { map: {
1: { 1: {
url: 'https://rt2.map.gtimg.com/tile?z={z}&x={x}&y={reverseY}&styleid=4&scene=0&version=347', url: 'https://rt2.map.gtimg.com/tile?z={z}&x={x}&y={reverseY}&styleid=4&scene=0&version=347',
tilingScheme: 'WebMercatorTilingScheme', tilingScheme: 'WebMercatorTilingScheme',
image: './images/map/1.png', image: './images/map/1.png',
name: '黑色', name: '黑色',
}, },
2: { 2: {
url: 'https://rt2.map.gtimg.com/tile?z={z}&x={x}&y={reverseY}&styleid=5&scene=0&version=347', url: 'https://rt2.map.gtimg.com/tile?z={z}&x={x}&y={reverseY}&styleid=5&scene=0&version=347',
tilingScheme: 'WebMercatorTilingScheme', tilingScheme: 'WebMercatorTilingScheme',
image: './images/map/2.png', image: './images/map/2.png',
name: '白色', name: '白色',
}, },
3: { 3: {
url: './resources/map/{z}/{x}/{y}.jpg', url: './resources/map/{z}/{x}/{y}.jpg',
tilingScheme: 'WebMercatorTilingScheme', tilingScheme: 'WebMercatorTilingScheme',
image: './images/map/3.jpg', image: './images/map/3.jpg',
name: 'Arcgis', name: 'Arcgis',
}, },
4: { 4: {
url: 'http://192.168.10.201:2022/api/maptilecache/service/tms/1.0.0/img_globle1to9-PNG-4326@EPSG:4326@png/{z}/{x}/{reverseY}.png', url: 'http://192.168.10.201:2022/api/maptilecache/service/tms/1.0.0/img_globle1to9-PNG-4326@EPSG:4326@png/{z}/{x}/{reverseY}.png',
tilingScheme: 'GeographicTilingScheme', tilingScheme: 'GeographicTilingScheme',
image: './images/map/4.png', image: './images/map/4.png',
name: '卫星影像', name: '卫星影像',
}, },
}, },
gantt: { gantt: {
task: { label: '任务', color: 'error' }, task: { label: '任务', color: 'error' },
dd: { label: '主体' }, dd: { label: '主体' },
mainEvent: { label: '主事件', color: 'success' }, 1: { label: '主事件', color: 'success' },
eventType: { label: '事件分类', color: 'info' }, 2: { label: '事件分类', color: 'info' },
subEvent: { label: '子事件', color: 'warning' }, 3: { label: '二级分类', color: 'warning' },
twoEvent: { label: '二级分类', color: 'warning' }, 4: { label: '子事件', color: 'warning' },
}, },
}
imgServer: 'http://minio.yc.com:29990/professional-system',
}

26
src/api/Gantt/gantt.js Executable file
View File

@ -0,0 +1,26 @@
import { defAxios as request } from '@/utils/http'
const baseUrl = `${window.settings.apis}/gantt`
export function getDDList() {
return request({
url: `${baseUrl}/gantt/target-info/simp-list`,
method: 'get',
})
}
export function getMainGantt(data = {}) {
return request({
url: `${baseUrl}/gantt/target-info/gantt-list`,
method: 'post',
data,
})
}
export function getSubGantt(params) {
return request({
url: `${baseUrl}/gantt/activity-event/gantt-list`,
method: 'get',
params,
})
}

838
src/api/Gantt/index.js Normal file → Executable file
View File

@ -1,372 +1,466 @@
const sub = [ const sub = [
{ {
id: 0, id: 0,
name: 'DD', name: 'DD',
// start: '2024-11-15', // start: '2024-11-15',
// end: '2024-11-21', // end: '2024-11-21',
type: 'eventType', type: 'eventType',
children: [ children: [
{ {
id: 122, id: 122,
name: '弹道DD', name: '弹道DD',
start: '2024-11-15', start: '2024-11-15',
end: '2024-11-17', end: '2024-11-17',
type: "twoEvent", type: 'twoEvent',
children: [ children: [
{ {
id: 1, id: 1,
name: '发射事件', name: '发射事件',
start: '2024-11-15', start: '2024-11-15',
end: '2024-11-17', end: '2024-11-17',
type: 'subEvent', type: 'subEvent',
trajData: {}, trajData: {},
avatar: '/images/影像.jpg', avatar: '/images/影像.jpg',
},
}, ],
], },
}, {
{ id: 188,
id: 188, name: '巡航DD',
name: '巡航DD', start: '2024-11-15',
start: '2024-11-15', end: '2024-11-17',
end: '2024-11-17', type: 'twoEvent',
type: "twoEvent", children: [
children: [ {
{ id: 1,
id: 1, name: '发射事件',
name: '发射事件', start: '2024-11-15',
start: '2024-11-15', end: '2024-11-17',
end: '2024-11-17', type: 'subEvent',
type: 'subEvent', trajData: {},
trajData: {}, avatar: '/images/影像.jpg',
avatar: '/images/影像.jpg', },
],
}, },
], ],
}, },
], {
id: 300,
}, name: '飞机',
{ type: 'eventType',
id: 300, children: [
name: '飞机', {
type: 'eventType', id: 5,
children: [ name: '起飞',
{ start: '2024-11-18',
id: 5, end: '2024-11-21',
name: '起飞', type: 'subEvent',
start: '2024-11-18', avatar: '/images/影像.jpg',
end: '2024-11-21', },
type: 'subEvent', ],
avatar: '/images/影像.jpg', },
}, {
], name: '舰船',
}, type: 'eventType',
{ children: [
name: '舰船', {
type: 'eventType', id: 6,
children: [ name: '停留',
{ start: '2024-11-20',
id: 6, end: '2024-11-22',
name: '停留', type: 'subEvent',
start: '2024-11-20', avatar: '/images/影像.jpg',
end: '2024-11-22', },
type: 'subEvent', {
avatar: '/images/影像.jpg', id: 7,
}, name: '扫描',
{ start: '2024-11-18',
id: 7, end: '2024-11-19',
name: '扫描', type: 'subEvent',
start: '2024-11-18', avatar: '/images/影像.jpg',
end: '2024-11-19', },
type: 'subEvent', ],
avatar: '/images/影像.jpg', },
},
], {
}, name: '航J',
type: 'eventType',
{ children: [
name: '航J', {
type: 'eventType', id: 8,
children: [ name: '航J事件',
{ start: '2024-11-20',
id: 8, end: '2024-11-21',
name: '航J事件', type: 'subEvent',
start: '2024-11-20', avatar: '/images/影像.jpg',
end: '2024-11-21', },
type: 'subEvent', {
avatar: '/images/影像.jpg', id: 9,
}, name: '航J事件-2',
{ start: '2024-11-22',
id: 9, end: '2024-11-26',
name: '航J事件-2', type: 'subEvent',
start: '2024-11-22', avatar: '/images/影像.jpg',
end: '2024-11-26', },
type: 'subEvent', ],
avatar: '/images/影像.jpg', },
}, {
], name: 'xx',
}, type: 'eventType',
{ children: [
name: 'xx', {
type: 'eventType', id: 13,
children: [ name: 'xx-事件-1',
{ start: '2024-11-22',
id: 13, end: '2024-11-25',
name: 'xx-事件-1', type: 'subEvent',
start: '2024-11-22', avatar: '/images/影像.jpg',
end: '2024-11-25', },
type: 'subEvent', {
avatar: '/images/影像.jpg', id: 14,
}, name: 'xx-事件-2',
{ start: '2024-11-27',
id: 14, end: '2024-11-30',
name: 'xx-事件-2', type: 'subEvent',
start: '2024-11-27', avatar: '/images/影像.jpg',
end: '2024-11-30', },
type: 'subEvent', ],
avatar: '/images/影像.jpg', },
}, ]
],
}, const main = [
] {
id: 0,
const main = [ name: 'DD-1',
{ type: 'dd',
id: 0, children: [
name: 'DD-1', {
type: 'dd', id: 1,
children: [ name: '事件1-1',
{ start: '2024-11-15',
id: 1, end: '2024-11-17',
name: '事件1-1', type: 'mainEvent',
start: '2024-11-15', // children: [
end: '2024-11-17', // {
type: 'mainEvent', // id: 122,
// children: [ // name: '发射',
// { // start: '2024-11-15',
// id: 122, // end: '2024-11-17',
// name: '发射', // type: 'DD',
// start: '2024-11-15', // avatar:
// end: '2024-11-17', // 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg',
// type: 'DD', // },
// avatar: // ],
// 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg', children: sub,
// }, },
// ], {
children: sub, id: 2,
}, name: '事件1-2',
{ start: '2024-11-17',
id: 2, end: '2024-11-18',
name: '事件1-2', type: 'mainEvent',
start: '2024-11-17', },
end: '2024-11-18', {
type: 'mainEvent', id: 3,
}, name: '事件1-3',
{ start: '2024-11-19',
id: 3, end: '2024-11-20',
name: '事件1-3', type: 'mainEvent',
start: '2024-11-19', },
end: '2024-11-20', {
type: 'mainEvent', id: 4,
}, name: '事件1-4',
{ start: '2024-11-18',
id: 4, end: '2024-11-19',
name: '事件1-4', type: 'mainEvent',
start: '2024-11-18', },
end: '2024-11-19', ],
type: 'mainEvent', },
}, {
], id: 300,
}, name: 'DD-2',
{ children: [
id: 300, {
name: 'DD-2', id: 5,
children: [ name: '事件-2-1',
{ start: '2024-11-18',
id: 5, end: '2024-11-21',
name: '事件-2-1', type: 'mainEvent',
start: '2024-11-18', },
end: '2024-11-21', ],
type: 'mainEvent', },
}, {
], name: 'DD-3',
}, children: [
{ {
name: 'DD-3', id: 6,
children: [ name: '事件-3-1',
{ start: '2024-11-21',
id: 6, end: '2024-11-22',
name: '事件-3-1', type: 'mainEvent',
start: '2024-11-21', },
end: '2024-11-22', {
type: 'mainEvent', id: 7,
}, name: '事件-3-2',
{ start: '2024-11-18',
id: 7, end: '2024-11-19',
name: '事件-3-2', type: 'mainEvent',
start: '2024-11-18', },
end: '2024-11-19', ],
type: 'mainEvent', },
},
], {
}, name: 'DD-4',
children: [
{ {
name: 'DD-4', id: 8,
children: [ name: '事件-4-1',
{ start: '2024-11-20',
id: 8, end: '2024-11-21',
name: '事件-4-1', type: 'mainEvent',
start: '2024-11-20', },
end: '2024-11-21', {
type: 'mainEvent', id: 9,
}, name: '事件-4-2',
{ start: '2024-11-25',
id: 9, end: '2024-11-26',
name: '事件-4-2', type: 'mainEvent',
start: '2024-11-25', },
end: '2024-11-26', {
type: 'mainEvent', id: 10,
}, name: '事件-4-3',
{ start: '2024-11-17',
id: 10, end: '2024-11-18',
name: '事件-4-3', type: 'mainEvent',
start: '2024-11-17', },
end: '2024-11-18', {
type: 'mainEvent', id: 11,
}, name: '事件-4-4',
{ start: '2024-11-22',
id: 11, end: '2024-11-25',
name: '事件-4-4', type: 'mainEvent',
start: '2024-11-22', },
end: '2024-11-25', {
type: 'mainEvent', id: 12,
}, name: '事件-4-5',
{ start: '2024-11-23',
id: 12, end: '2024-11-24',
name: '事件-4-5', type: 'mainEvent',
start: '2024-11-23', },
end: '2024-11-24', ],
type: 'mainEvent', },
}, {
], name: 'DD-5',
}, children: [
{ {
name: 'DD-5', id: 13,
children: [ name: '事件-5-1',
{ start: '2024-11-22',
id: 13, end: '2024-11-25',
name: '事件-5-1', type: 'mainEvent',
start: '2024-11-22', },
end: '2024-11-25', {
type: 'mainEvent', id: 14,
}, name: '事件-5-2',
{ start: '2024-11-27',
id: 14, end: '2024-11-30',
name: '事件-5-2', type: 'mainEvent',
start: '2024-11-27', },
end: '2024-11-30', {
type: 'mainEvent', id: 15,
}, name: '事件-5-3',
{ start: '2024-12-10',
id: 15, end: '2024-12-18',
name: '事件-5-3', type: 'mainEvent',
start: '2024-12-10', },
end: '2024-12-18', ],
type: 'mainEvent', },
}, {
], name: 'DD-6',
}, children: [
{ {
name: 'DD-6', id: 16,
children: [ name: '事件-6-1',
{ start: '2024-11-20',
id: 16, end: '2024-11-30',
name: '事件-6-1', type: 'mainEvent',
start: '2024-11-20', },
end: '2024-11-30', {
type: 'mainEvent', id: 17,
}, name: '事件-6-2',
{ start: '2024-12-02',
id: 17, end: '2024-12-18',
name: '事件-6-2', type: 'mainEvent',
start: '2024-12-02', },
end: '2024-12-18', ],
type: 'mainEvent', },
}, {
], name: 'DD-7',
}, children: [
{ {
name: 'DD-7', id: 18,
children: [ name: '事件-7-1',
{ start: '2024-12-22',
id: 18, end: '2024-12-28',
name: '事件-7-1', type: 'mainEvent',
start: '2024-12-22', },
end: '2024-12-28', ],
type: 'mainEvent', },
}, {
], name: 'DD-8',
}, children: [
{ {
name: 'DD-8', id: 19,
children: [ name: '事件-8-1',
{ start: '2024-11-25',
id: 19, end: '2024-11-30',
name: '事件-8-1', type: 'mainEvent',
start: '2024-11-25', },
end: '2024-11-30', {
type: 'mainEvent', id: 20,
}, name: '事件-8-2',
{ start: '2024-12-01',
id: 20, end: '2024-12-18',
name: '事件-8-2', type: 'mainEvent',
start: '2024-12-01', },
end: '2024-12-18', ],
type: 'mainEvent', },
}, ]
],
}, const task = [
] {
id: 1,
const task = [ name: '任务-1',
{ type: 'task',
id: 1, children: main,
name: '任务-1', },
type: 'task', ]
children: main,
}, export function getMainGantt(data = {}) {
] return new Promise((resolve, reject) => {
setTimeout(() => {
export function getMainGantt(data = {}) { resolve(main)
return new Promise((resolve, reject) => { }, 200)
setTimeout(() => { })
resolve(main) }
}, 200)
}) export function getEventListByDDType(ddType) {
} return new Promise((resolve, reject) => {
setTimeout(() => {
export function getEventListByDDType(ddType) { resolve(main.find(item => item.name === ddType))
return new Promise((resolve, reject) => { }, 200)
setTimeout(() => { })
resolve(main.find(item => item.name === ddType)) }
}, 200)
}) export function getSubGantt(subId) {
} return new Promise((resolve, reject) => {
setTimeout(() => {
export function getSubGantt(subId) { resolve(sub)
return new Promise((resolve, reject) => { }, 200)
setTimeout(() => { })
resolve(sub) }
}, 200)
}) export function getTask() {
} return new Promise((resolve, reject) => {
setTimeout(() => {
export function getTask() { resolve(task)
return new Promise((resolve, reject) => { }, 200)
setTimeout(() => { })
resolve(task) }
}, 200)
}) import { defAxios as request } from '@/utils/http'
} const baseUrl = window.settings.apis + '/gantt'
// export function getHangjing(data = {}) {
// return request({
// url: `${baseUrl}/hangjing/list`,
// method: 'post',
// data,
// })
// }
export function getSimpList() {
return request({
url: `${baseUrl}/gantt/target-info/simp-list`,
method: 'get',
})
}
export function getSimpTreeList(data) {
return request({
url: `${baseUrl}/gantt/target-activity/tree-list`,
method: 'post',
data,
})
}
export function addSimp(data) {
return request({
url: `${baseUrl}/gantt/target-activity/create`,
method: 'post',
data,
})
}
export function updateSimp(data) {
return request({
url: `${baseUrl}/gantt/target-activity/update`,
method: 'put',
data,
})
}
export function uploadImage(data) {
return request({
url: `${baseUrl}/platform/sys/file/sync/upload`,
method: 'post',
data,
})
}
export function deleteSimp(params) {
return request({
url: `${baseUrl}/gantt/target-activity/delete`,
method: 'delete',
params,
})
}
export function getSon(params) {
return request({
url: `${baseUrl}/gantt/activity-event/get`,
method: 'get',
params,
})
}
export function addSon(data) {
return request({
url: `${baseUrl}/gantt/activity-event/create`,
method: 'post',
data,
})
}
export function updateSon(data) {
return request({
url: `${baseUrl}/gantt/activity-event/update`,
method: 'put',
data,
})
}
export function getTwoClass(params) {
return request({
url: `${baseUrl}/gantt/data-type/list`,
method: 'get',
params,
})
}
export function deleteSon(params) {
return request({
url: `${baseUrl}/gantt/activity-event/delete`,
method: 'delete',
params,
})
}

View File

@ -38,6 +38,10 @@ const props = defineProps({
type: Boolean, type: Boolean,
default: true, default: true,
}, },
nodeClick: {
type: Function as (key: string) => void,
default: undefined,
},
}) })
const searchValue = ref<string>('') const searchValue = ref<string>('')
@ -48,6 +52,11 @@ const updateChecked = (keys: Array<string | number>) => {
checkedKeys.value = keys checkedKeys.value = keys
} }
const updateSelected = (key: Array<string | number>) => {
// nodeClick && nodeClick(props.data.find(item => item[props.keyField] === keys[0]))
props.nodeClick && props.nodeClick(key)
}
// watch( // watch(
// () => props.defaultCheckedKeys, // () => props.defaultCheckedKeys,
// val => { // val => {
@ -86,6 +95,7 @@ const updateChecked = (keys: Array<string | number>) => {
:node-props="nodeProps" :node-props="nodeProps"
:render-suffix="renderSuffix" :render-suffix="renderSuffix"
:render-label="renderLabel" :render-label="renderLabel"
@update:selected-keys="updateSelected"
/> />
<!-- :default-checked-keys="defaultCheckedKeys" --> <!-- :default-checked-keys="defaultCheckedKeys" -->
</n-scrollbar> </n-scrollbar>

View File

@ -132,8 +132,16 @@ export class Beam {
}) })
this.beamEntity = cylinder this.beamEntity = cylinder
// const that = this let currentTime = 0
this._listener = (scene, time) => { this._listener = (scene, time) => {
const timeStamp = Cesium.JulianDate.toDate(time).getTime()
const deltaTime = timeStamp - currentTime
if (deltaTime <= 100) {
return
}
currentTime = timeStamp
if (!this.beamEntity) { if (!this.beamEntity) {
return return
} }
@ -144,11 +152,8 @@ export class Beam {
if (!topPosition) { if (!topPosition) {
return return
} }
// console.log(topPosition, 'topPosition')
let bottomPosition = this.bottomEntity.position._value let bottomPosition = this.bottomEntity.position._value
// console.log(bottomPosition, 'bottomPosition')
this.centerPoint = this._getCenterPoint(topPosition, bottomPosition) this.centerPoint = this._getCenterPoint(topPosition, bottomPosition)
// console.log(this.centerPoint, 'centerPoint')
this.beamEntity.position = this.centerPoint this.beamEntity.position = this.centerPoint
this.distance = this._getDistance(topPosition, bottomPosition) this.distance = this._getDistance(topPosition, bottomPosition)
this.beamEntity.cylinder.length = this.distance this.beamEntity.cylinder.length = this.distance
@ -156,14 +161,12 @@ export class Beam {
const orientation = this._getOrientation(bottomPosition, topPosition) const orientation = this._getOrientation(bottomPosition, topPosition)
this.beamEntity.orientation = orientation this.beamEntity.orientation = orientation
console.log('1')
// throw new Error('TODO: Beam.js:26 Uncaught Error: TODO: Beam.js:26')
} }
// viewer.scene.preRender.addEventListener(this._listener) viewer.scene.preRender.addEventListener(this._listener)
setInterval(() => { // setInterval(() => {
this._listener(viewer.scene, viewer.clock.currentTime) // this._listener(viewer.scene, viewer.clock.currentTime)
}, 100) // }, 100)
} }
_getCenterPoint(t, b) { _getCenterPoint(t, b) {

View File

@ -1,94 +1,94 @@
import { NTabs, NTabPane, NButton, NScrollbar } from 'naive-ui' import { NTabs, NTabPane, NButton, NScrollbar } from 'naive-ui'
import TrajTable from '@/views/Daodan/components/TrajTable' import TrajTable from '@/views/Daodan/components/TrajTable'
import TrajUpload from '@/views/Daodan/components/TrajUpload' import TrajUpload from '@/views/Daodan/components/TrajUpload'
import { useEventDdConfig } from './useEventDdConfig' import { useEventDdConfig } from './useEventDdConfig'
const panels = ['手动配置', 'STK轨迹文件配置'] const panels = ['手动配置', 'STK轨迹文件配置']
export default defineComponent({ export default defineComponent({
name: 'EventDaodan', name: 'EventDaodan',
props: { props: {
data: { data: {
type: Object, type: Object,
default: () => ({}), default: () => ({}),
}, },
}, },
setup() { setup() {
const name = ref('手动配置') const name = ref('手动配置')
const { trajData, interceptData, addIntercept } = useEventDdConfig() const { trajData, interceptData, addIntercept } = useEventDdConfig()
// const { trajData, interceptData, updateInterceptData } = useEvent() // const { trajData, interceptData, updateInterceptData } = useEvent()
const removeIntercept = id => { const removeIntercept = id => {
interceptData.value.splice( interceptData.value.splice(
interceptData.value.findIndex(item => item.id === id), interceptData.value.findIndex(item => item.id === id),
1 1
) )
} }
const showOrHideDdConfig = () => {} const showOrHideDdConfig = () => {}
return () => ( return () => (
<div class="flex h-full w-[80vw] flex-col gap-2 p-2"> <div class="flex h-full w-[80vw] flex-col gap-2 p-2">
<NTabs <NTabs
class="flex h-[calc(100%-42px)] flex-col" class="flex h-[calc(100%-42px)] flex-col"
v-model:value={name.value} v-model:value={name.value}
type="card" type="card"
tab-style="min-width: 80px;" tab-style="min-width: 80px;"
> >
{panels.map(panel => ( {panels.map(panel => (
<NTabPane <NTabPane
class="flex-1 overflow-y-auto rounded-b-[var(--n-tab-border-radius)] border border-[var(--n-tab-border-color)] border-t-transparent" class="flex-1 overflow-y-auto rounded-b-[var(--n-tab-border-radius)] border border-[var(--n-tab-border-color)] border-t-transparent"
key={panel} key={panel}
tab={panel} tab={panel}
name={panel} name={panel}
> >
<NScrollbar> <NScrollbar>
<div class="px-4 pb-4"> <div class="px-4 pb-4">
<div class="detail-container"> <div class="detail-container">
{panel === '手动配置' ? ( {panel === '手动配置' ? (
<> <>
<div class="rounded border border-blue-500 p-4"> <div class="rounded border border-blue-500 p-4">
<TrajTable <TrajTable
title="轨迹点" title="轨迹点"
showPosIcon={false} showPosIcon={false}
data={trajData.value} data={trajData.value}
/> />
</div> </div>
<div class="flex flex-col gap-4 rounded border border-red-500 p-4"> <div class="flex flex-col gap-4 rounded border border-red-500 p-4">
{interceptData.value.map(data => ( {interceptData.value.map(data => (
<TrajTable <TrajTable
title="拦截" title="拦截"
data={data} data={data}
showPosIcon={false} showPosIcon={false}
onRemoveIntercept={removeIntercept} onRemoveIntercept={removeIntercept}
/> />
))} ))}
<div> <div>
<NButton type="primary" onClick={addIntercept}> <NButton type="primary" onClick={addIntercept}>
添加拦截 添加拦截
</NButton> </NButton>
</div> </div>
</div> </div>
</> </>
) : ( ) : (
<> <>
<TrajUpload title="轨迹点" /> <TrajUpload title="轨迹点" />
<TrajUpload title="拦截" /> <TrajUpload title="拦截" />
</> </>
)} )}
</div> </div>
</div> </div>
</NScrollbar> </NScrollbar>
</NTabPane> </NTabPane>
))} ))}
</NTabs> </NTabs>
<div class="flex justify-end gap-2"> <div class="flex justify-end gap-2">
<NButton type="primary" onClick={confirm}> <NButton type="primary" onClick={confirm}>
确认 确认
</NButton> </NButton>
<NButton onClick={() => showOrHideDdConfig({})}>取消</NButton> <NButton onClick={() => showOrHideDdConfig({})}>取消</NButton>
</div> </div>
</div> </div>
) )
}, },
}) })

View File

@ -1,80 +1,130 @@
import { import {
NForm, NForm,
NFormItem, NFormItem,
NInput, NInput,
NButton, NButton,
NDatePicker, NDatePicker,
NUpload, NUpload,
} from 'naive-ui' } from 'naive-ui'
import ModalCom from '@/components/Modal/index.vue' import ModalCom from '@/components/Modal/index.vue'
import { useEvent } from '../hooks' import { useEvent } from '../hooks'
export default defineComponent({ import { addSimp, updateSimp, uploadImage } from '@/api/Gantt'
// props: { export default defineComponent({
// show: { // props: {
// type: Boolean, // show: {
// default: false, // type: Boolean,
// }, // default: false,
// }, // },
setup() { // },
const { showMainEvent, mainEventData } = useEvent() setup() {
const imageValue = ref()
const close = () => { const {
showMainEvent.value = false universalRules,
} showMainEvent,
mainEventData,
watch( targetId,
[ searchTreeList,
() => mainEventData.value.start, } = useEvent()
() => mainEventData.value.end,
() => mainEventData.value.avatar, const close = () => {
], showMainEvent.value = false
([start, end, avatar]) => { }
timeRange.value = start ? [start, end] : null const changeFile = async file => {
uploadImg.value = [ console.log(uploadImg)
{ const formData = new FormData()
url: avatar, formData.append('file', file.file.file)
status: 'finished', const res = await uploadImage(formData)
}, imageValue.value = res.data.path
] }
} const sure = async () => {
) const data = {
...mainEventData.value,
const timeRange = ref(null) targetId: targetId.value,
const uploadImg = ref([]) fileUrl: imageValue.value,
startTime: new Date(timeRange.value[0]).toISOString(),
return () => ( endTime: new Date(timeRange.value[0]).toISOString(),
<ModalCom v-model:show={showMainEvent.value} title="编辑事件"> }
<NForm
class="w-[500px]" const res = mainEventData.value.id
model={mainEventData} ? await updateSimp(data)
label-placement="left" : await addSimp(data)
label-width="auto" if (res.code === 200) {
> showMainEvent.value = false
<NFormItem label="事件名称" path="name"> searchTreeList()
<NInput v-model:value={mainEventData.value.name} /> }
</NFormItem> }
<NFormItem label="事件时间" path="description">
<NDatePicker watch(
v-model:value={timeRange.value} [
type="daterange" () => mainEventData.value.startTime,
clearable () => mainEventData.value.endTime,
/> () => mainEventData.value.fileUrl,
</NFormItem> ],
<NFormItem label="上传图片" path="avatar"> ([start, end, fileUrl]) => {
<NUpload timeRange.value = start
default-file-list={uploadImg.value} ? [new Date(start).getTime(), new Date(end).getTime()]
list-type="image-card" : null
max={1} if (fileUrl) {
/> uploadImg.value = [
</NFormItem> {
</NForm> id: '1',
<div class="flex justify-end gap-2"> name: fileUrl,
<NButton onClick={close}>取消</NButton> url: `${window.settings.imgServer}${fileUrl}`,
<NButton type="primary" onClick={close}> status: 'finished',
确认 },
</NButton> ]
</div> }
</ModalCom> }
) )
},
}) const timeRange = ref(null)
const uploadImg = ref([])
// const desc = ref ()
return () => (
<ModalCom
v-model:show={showMainEvent.value}
title={`${mainEventData.value.id ? '编辑' : '添加'}事件`}
>
<NForm
class="w-[500px]"
model={mainEventData}
label-placement="left"
label-width="auto"
rules={universalRules}
>
<NFormItem label="事件名称" path="name">
<NInput v-model:value={mainEventData.value.name} />
</NFormItem>
<NFormItem label="事件时间" path="startTime">
<NDatePicker
v-model:value={timeRange.value}
type="daterange"
clearable
/>
</NFormItem>
<NFormItem label="事件描述" path="describe">
<NInput
v-model:value={mainEventData.value.describe}
type="textarea"
/>
</NFormItem>
<NFormItem label="上传图片" path="fileUrl">
<NUpload
default-file-list={uploadImg.value}
list-type="image-card"
max={1}
onChange={changeFile}
/>
</NFormItem>
</NForm>
<div class="flex justify-end gap-2">
<NButton onClick={close}>取消</NButton>
<NButton type="primary" onClick={sure}>
确认
</NButton>
</div>
</ModalCom>
)
},
})

View File

@ -1,96 +1,155 @@
import { import {
NForm, NForm,
NFormItem, NFormItem,
NInput, NInput,
NButton, NButton,
NDatePicker, NDatePicker,
NUpload, NUpload,
NSelect, NSelect,
} from 'naive-ui' } from 'naive-ui'
import ModalCom from '@/components/Modal/index.vue' import ModalCom from '@/components/Modal/index.vue'
import { useEvent } from '../hooks' import { useEvent } from '../hooks'
export default defineComponent({ import { addSon, updateSon, uploadImage } from '@/api/gantt'
// props: { export default defineComponent({
// show: { // props: {
// type: Boolean, // show: {
// default: false, // type: Boolean,
// }, // default: false,
// }, // },
setup() { // },
const { showNewEvent, eventData } = useEvent() setup() {
const {
const close = () => { showNewEvent,
showNewEvent.value = false eventData,
} sonOptions,
oneClassData,
watch( searchTreeList,
[ } = useEvent()
() => eventData.value.start, const imageValue = ref()
() => eventData.value.end, const close = () => {
() => eventData.value.avatar, showNewEvent.value = false
], }
([start, end, avatar]) => { const changeFile = async file => {
timeRange.value = start ? [start, end] : null const formData = new FormData()
uploadImg.value = [ formData.append('file', file.file.file)
{ const res = await uploadImage(formData)
url: avatar, imageValue.value = res.data.path
status: 'finished', }
}, const sure = async () => {
] const data = {
} // id: eventData.value.id ?? null,
) // activityId: oneClassData.value.activityId ?? null,
// name: eventData.value.name,
const timeRange = ref(null) activityId: oneClassData.value?.pid ?? null,
const uploadImg = ref([]) oneType: oneClassData.value?.id,
const value1 = ref(null) ...eventData.value,
const value2 = ref(null) twoType: twoTypeId.value,
const value3 = ref(null) fileUrl: imageValue.value,
startTime: new Date(timeRange.value[0]).toISOString(),
return () => ( endTime: new Date(timeRange.value[1]).toISOString(),
<ModalCom }
v-model:show={showNewEvent.value} // console.log(data, 'data')
title={`${eventData.value.id ? '编辑' : '添加'}子事件`} const res = eventData.value.id
> ? await updateSon(data)
<NForm : await addSon(data)
class="w-[500px]" if (res.code === 200) {
model={eventData} showNewEvent.value = false
label-placement="left" searchTreeList()
label-width="auto" }
> }
<NFormItem label="二级分类">
<NSelect v-model:value={value1.value}></NSelect> watch(
</NFormItem> [
<NFormItem label="步骤"> () => eventData.value.startTime,
<NSelect v-model:value={value2.value}></NSelect> () => eventData.value.endTime,
</NFormItem> () => eventData.value.fileUrl,
<NFormItem label="目标"> () => eventData.value.twoType,
<NSelect v-model:value={value3.value}></NSelect> ],
</NFormItem> ([start, end, fileUrl, twoType]) => {
<NFormItem label="事件名称" path="name"> timeRange.value = start
<NInput v-model:value={eventData.value.name} /> ? [new Date(start).getTime(), new Date(end).getTime()]
</NFormItem> : null
<NFormItem label="事件时间" path="description"> if (fileUrl) {
<NDatePicker uploadImg.value = [
v-model:value={timeRange.value} {
type="daterange" id: fileUrl,
clearable name: fileUrl,
/> url: `${window.settings.imgServer}${fileUrl}`,
</NFormItem> status: 'finished',
<NFormItem label="上传图片" path="avatar"> },
<NUpload ]
default-file-list={uploadImg.value} console.log(uploadImg.value)
list-type="image-card" }
max={1} console.log(twoType, 'twoType')
/>
</NFormItem> twoType && (twoTypeId.value = twoType)
</NForm> }
<div class="flex justify-end gap-2"> )
<NButton onClick={close}>取消</NButton>
<NButton type="primary" onClick={close}> const timeRange = ref(null)
确认 const uploadImg = ref([])
</NButton> const twoTypeId = ref(null)
</div>
</ModalCom> return () => (
) <ModalCom
}, v-model:show={showNewEvent.value}
}) title={`${eventData.value.id ? '编辑' : '添加'}子事件`}
>
<NForm
class="w-[500px]"
model={eventData}
label-placement="left"
label-width="auto"
>
<NFormItem label="二级分类">
<NSelect
v-model:value={twoTypeId.value}
options={sonOptions.value}
label-field="name"
value-field="id"
></NSelect>
</NFormItem>
{/* <NFormItem label="">
<NSelect v-model:value={value2.value}></NSelect>
</NFormItem>
<NFormItem label="目标">
<NSelect v-model:value={value3.value}></NSelect>
</NFormItem> */}
<NFormItem label="事件名称" path="name">
<NInput v-model:value={eventData.value.name} />
</NFormItem>
<NFormItem label="事件时间" path="startTime">
<NDatePicker
v-model:value={timeRange.value}
type="daterange"
clearable
/>
</NFormItem>
<NFormItem label="装备型号" path="equipModel">
<NInput v-model:value={eventData.value.equipModel} />
</NFormItem>
<NFormItem label="上报站点" path="reportSite">
<NInput v-model:value={eventData.value.reportSite} />
</NFormItem>
<NFormItem label="事件描述" path="describe">
<NInput v-model:value={eventData.value.describe} type="textarea" />
</NFormItem>
<NFormItem label="上传图片" path="fileUrl">
<NUpload
default-file-list={uploadImg.value}
list-type="image-card"
onChange={changeFile}
max={1}
/>
</NFormItem>
</NForm>
<div class="flex justify-end gap-2">
<NButton onClick={close}>取消</NButton>
<NButton type="primary" onClick={sure}>
确认
</NButton>
</div>
</ModalCom>
)
},
})

View File

@ -1,103 +1,103 @@
export const useEventDdConfig = () => { export const useEventDdConfig = () => {
const trajData = ref({ const trajData = ref({
id: 'dd', id: 'dd',
data: [ data: [
{ {
name: '起始点', name: '起始点',
lon: 120, lon: 120,
lat: 21, lat: 21,
alt: 0, alt: 0,
time: 1183135260000, time: 1183135260000,
}, },
{ {
name: '中间特征点', name: '中间特征点',
lon: 122, lon: 122,
lat: 21, lat: 21,
alt: 1000000, alt: 1000000,
time: 1183135265000, time: 1183135265000,
detached: true, detached: true,
}, },
{ {
name: '中间特征点', name: '中间特征点',
lon: 124, lon: 124,
lat: 21, lat: 21,
alt: 1500000, alt: 1500000,
time: 1183135270000, time: 1183135270000,
detached: true, detached: true,
}, },
{ {
name: '中间特征点', name: '中间特征点',
lon: 128, lon: 128,
lat: 21, lat: 21,
alt: 2000000, alt: 2000000,
time: 1183135280000, time: 1183135280000,
detached: true, detached: true,
}, },
{ {
name: '落点', name: '落点',
lon: 135, lon: 135,
lat: 21, lat: 21,
alt: 1500000, alt: 1500000,
time: 1183135290000, time: 1183135290000,
}, },
], ],
}) })
const interceptData = ref([ const interceptData = ref([
{ {
id: 'dd1', id: 'dd1',
data: [ data: [
{ {
name: '起始点', name: '起始点',
lon: 137, lon: 137,
lat: 25, lat: 25,
alt: 0, alt: 0,
time: 1183135270000, time: 1183135270000,
}, },
{ {
name: '中间特征点', name: '中间特征点',
lon: 138, lon: 138,
lat: 24, lat: 24,
alt: 1000000, alt: 1000000,
time: 1183135280000, time: 1183135280000,
detached: true, detached: true,
}, },
{ {
name: '落点', name: '落点',
lon: 135, lon: 135,
lat: 21, lat: 21,
alt: 1500000, alt: 1500000,
time: 1183135290000, time: 1183135290000,
}, },
], ],
}, },
]) ])
function addIntercept() { function addIntercept() {
// d // d
interceptData.value.push({ interceptData.value.push({
data: [ data: [
{ {
name: '起始点', name: '起始点',
lon: 120, lon: 120,
lat: 21, lat: 21,
alt: 0, alt: 0,
time: 1183135260000, time: 1183135260000,
}, },
{ {
name: '中间特征点', name: '中间特征点',
lon: 120, lon: 120,
lat: 21, lat: 21,
alt: 0, alt: 0,
time: 1183135260000, time: 1183135260000,
detached: false, detached: false,
}, },
trajData.value.data.at(-1), trajData.value.data.at(-1),
], ],
}) })
} }
return { return {
trajData, trajData,
interceptData, interceptData,
addIntercept, addIntercept,
} }
} }

136
src/views/Gantt/components/EventList/hooks.jsx Normal file → Executable file
View File

@ -1,30 +1,106 @@
import { generateId } from '@/utils/id' import { getSimpTreeList, getTwoClass } from '@/api/Gantt'
const showMainEvent = ref(false) const universalRules = {
name: { required: true, message: '请输入', trigger: 'blur' },
const mainEventData = ref({ startTime: { required: true, message: '请输入', trigger: 'blur' },
name: '', }
start: '', const showMainEvent = ref(false)
end: '',
type: 'mainEvent', watch(showMainEvent, show => {
avatar: '', if (!show) {
}) resetMainEventData()
}
const showNewEvent = ref(false) })
const eventData = ref({ const mainEventData = ref({
name: '', name: '',
start: '', startTime: '',
end: '', endTime: '',
avatar: '', // type: 'mainEvent',
type: 'subEvent', describe: '',
}) fileUrl: '',
})
export const useEvent = () => {
return { function resetMainEventData() {
showMainEvent, mainEventData.value = ref({
mainEventData, name: '',
showNewEvent, startTime: '',
eventData, endTime: '',
} // type: 'mainEvent',
} describe: '',
fileUrl: '',
})
}
const targetId = ref(null)
const range = ref([new Date('2000-01-01').getTime(), Date.now()])
const showNewEvent = ref(false)
watch(showNewEvent, show => {
if (!show) {
oneClassData.value = null
resetEventData()
}
})
const eventData = ref({
name: '',
startTime: '',
endTime: '',
fileUrl: '',
describe: '',
equipModel: '',
reportSite: '',
// type: 'subEvent',
})
function resetEventData() {
eventData.value = ref({
name: '',
startTime: '',
endTime: '',
fileUrl: '',
describe: '',
equipModel: '',
reportSite: '',
})
}
const tableData = ref([])
const oneClassData = ref(null)
async function searchTreeList() {
tableData.value = []
const res = await getSimpTreeList({
targetId: targetId.value,
startTime: new Date(range.value[0]).toISOString(),
endTime: new Date(range.value[1]).toISOString(),
})
tableData.value = res.data.list
// console.log('searchTreeList', tableData)
}
const sonOptions = ref([])
async function getTwoClassList(oneTypeId) {
const res = await getTwoClass({ oneType: oneTypeId })
sonOptions.value = res.data.list
}
// const getSonList = async()
export const useEvent = () => {
return {
universalRules,
showMainEvent,
mainEventData,
showNewEvent,
eventData,
targetId,
searchTreeList,
tableData,
range,
oneClassData,
sonOptions,
getTwoClassList,
}
}

556
src/views/Gantt/components/EventList/index.jsx Normal file → Executable file
View File

@ -1,260 +1,296 @@
import { NDataTable, NIcon, NButton, useDialog, NTag } from 'naive-ui' import { NDataTable, NIcon, NButton, useDialog, NTag } from 'naive-ui'
import { getEventListByDDType } from '@/api/gantt' import {
import { useTree } from '@/utils/tree' getEventListByDDType,
import { deleteSimp,
HelpCircleOutline, getTwoClass,
CreateOutline, deleteSon,
TrashBinOutline, } from '@/api/gantt'
AddCircleOutline, import { useTree } from '@/utils/tree'
EnterOutline, import {
} from '@vicons/ionicons5' HelpCircleOutline,
CreateOutline,
import MainEventEdit from './components/MainEventEdit' TrashBinOutline,
import SubEventEdit from './components/SubEventEdit' AddCircleOutline,
EnterOutline,
import EventDdConfig from './components/EventDdConfig' } from '@vicons/ionicons5'
import { useEvent } from './hooks' import { cloneDeep } from 'es-toolkit'
export default defineComponent({ import MainEventEdit from './components/MainEventEdit'
props: { import SubEventEdit from './components/SubEventEdit'
dd: {
type: String, import EventDdConfig from './components/EventDdConfig'
require: true,
}, import { useEvent } from './hooks'
},
setup(props) { export default defineComponent({
const { showMainEvent, mainEventData, showNewEvent, eventData } = useEvent() props: {
const dict = window.settings.gantt dd: {
const columns = [ type: Number,
{ require: true,
title: '事件名称', },
key: 'name', tableData: {
width: 'auto', type: Array,
render: row => { require: true,
return ( },
<div class="inline-flex items-center gap-2"> },
{/* <NIcon> setup(props) {
<HelpCircleOutline /> const {
</NIcon> */} showMainEvent,
<NTag mainEventData,
size="small" showNewEvent,
round eventData,
bordered={false} searchTreeList,
type={dict[row.type].color} oneClassData,
> // sonOptions,
{dict[row.type].label} getTwoClassList,
</NTag> } = useEvent()
{row.name} const dict = window.settings.gantt
</div> const columns = [
) {
}, title: '事件名称',
}, key: 'name',
{ // width: 'auto',
title: '开始时间', render: row => {
key: 'start', return (
}, <div class="inline-flex items-center gap-2">
{ {/* <NIcon>
title: '结束时间', <HelpCircleOutline />
key: 'end', </NIcon> */}
}, <NTag
// { size="small"
// title: '', round
// key: 'type', bordered={false}
// render(row) { type={dict[row.level].color}
// return ( >
// <NTag {dict[row.level].label}
// size="small" </NTag>
// round {row.name}
// bordered={false} </div>
// type={dict[row.type].color} )
// > },
// {dict[row.type].label} },
// </NTag> {
// ) title: '开始时间',
// }, key: 'startTime',
// }, width: '300',
{ },
title: '图片', {
key: 'avatar', title: '结束时间',
render(row) { key: 'endTime',
if (row.avatar) { width: '300',
return <img src={row.avatar} width="50" alt="" /> },
} else { // {
return <span>-</span> // title: '',
} // key: 'type',
}, // render(row) {
}, // return (
{ // <NTag
title: '操作', // size="small"
key: 'action', // round
render(row, rowIndex) { // bordered={false}
// console.log(row, rowIndex) // type={dict[row.type].color}
return ( // >
<div class="flex justify-end"> // {dict[row.type].label}
{row.type === 'mainEvent' && ( // </NTag>
<NButton // )
type="primary" // },
size="small" // },
quaternary {
onClick={() => editMainEvent(row)} title: '图片',
> key: 'filePath',
<NIcon> width: '200',
<CreateOutline /> render(row) {
</NIcon> if (row.fileUrl) {
编辑事件 return (
</NButton> <img
)} src={`${window.settings.imgServer}${row.fileUrl}`}
{row.type === 'twoEvent' ? ( width="50"
<NButton alt=""
type="success" />
size="small" )
quaternary } else {
onClick={() => addSubEvent(row)} return <span>-</span>
> }
<NIcon> },
<AddCircleOutline /> },
</NIcon> {
添加子事件 title: '操作',
</NButton> key: 'action',
) : null} width: '240',
{row.type === 'isDD' && ( render(row, rowIndex) {
<NButton // console.log(row, rowIndex)
type="primary" return (
size="small" <div class="flex justify-end">
quaternary {row.level === 1 && (
onClick={() => editSubEvent(row)} <NButton
> type="primary"
<NIcon> size="small"
<CreateOutline /> quaternary
</NIcon> onClick={() => editMainEvent(row)}
编辑子事件 >
</NButton> <NIcon>
)} <CreateOutline />
{Reflect.has(row, 'trajData') && ( </NIcon>
<NButton 编辑事件
type="primary" </NButton>
size="small" )}
quaternary {row.level === 2 ? (
onClick={() => ddConfig(row)} <NButton
> type="success"
<NIcon> size="small"
<CreateOutline /> quaternary
</NIcon> onClick={() => addSubEvent(row)}
编辑DD轨迹 >
</NButton> <NIcon>
)} <AddCircleOutline />
{row.type === 'subEvent' && ( </NIcon>
<NButton 添加子事件
type="primary" </NButton>
size="small" ) : null}
quaternary {row.level === 4 && (
onClick={() => editSubEvent(row)} <NButton
> type="primary"
<NIcon> size="small"
<CreateOutline /> quaternary
</NIcon> onClick={() => editSubEvent(row)}
编辑子事件 >
</NButton> <NIcon>
)} <CreateOutline />
{row.type !== 'eventType' ? ( </NIcon>
<NButton 编辑子事件
type="error" </NButton>
size="small" )}
quaternary {Reflect.has(row, 'trajData') && (
onClick={() => deleteEvent(row)} <NButton
> type="primary"
<NIcon> size="small"
<TrashBinOutline /> quaternary
</NIcon> onClick={() => ddConfig(row)}
</NButton> >
) : ( <NIcon>
<></> <CreateOutline />
)} </NIcon>
</div> 编辑DD轨迹
) </NButton>
}, )}
}, {/* {row.level == 4 && (
] <NButton
type="primary"
function editMainEvent(row) { size="small"
showMainEvent.value = true quaternary
mainEventData.value = row onClick={() => editSubEvent(row)}
} >
<NIcon>
function addSubEvent(row) { <CreateOutline />
showNewEvent.value = true </NIcon>
} 编辑子事件
</NButton>
function editSubEvent(row) { )} */}
showNewEvent.value = true {![2, 3].includes(row.level) ? (
eventData.value = row <NButton
} type="error"
size="small"
const dialog = useDialog() quaternary
function deleteEvent(row) { onClick={() => deleteEvent(row)}
dialog.warning({ >
title: '删除事件', <NIcon>
content: `确定删除事件 ${row.name} 吗?`, <TrashBinOutline />
positiveText: '确定', </NIcon>
negativeText: '取消', </NButton>
onPositiveClick: async () => { ) : (
// await deleteEventById(row.id) <></>
// getEventList() )}
}, </div>
}) )
} },
},
function ddConfig(row) { ]
console.log(row)
dialog.create({ function editMainEvent(row) {
style: 'width:auto;height:90vh', showMainEvent.value = true
maskClosable: false, mainEventData.value = cloneDeep(row)
class: 'flex flex-col', }
title: 'DD轨迹',
contentClass: 'flex-1 h-0', const addSubEvent = async row => {
content: () => <EventDdConfig />, console.log(row, 'row')
// positiveText: '', oneClassData.value = row
// negativeText: '', showNewEvent.value = true
// onPositiveClick: () => {}, // eventData.value = {}
}) await getTwoClassList(row.id)
} }
const tableData = ref([]) function editSubEvent(row) {
onMounted(async () => { showNewEvent.value = true
await getEventList() eventData.value = cloneDeep(row)
}) getTwoClassList(row.oneType)
console.log('子事件编辑:', row, 'onClassData', oneClassData)
const { getAllKeys } = useTree() }
const expandedRowKeys = ref([])
async function getEventList() { const dialog = useDialog()
const res = await getEventListByDDType(props.dd) function deleteEvent(row) {
tableData.value = res.children console.log(row, 'row')
expandedRowKeys.value = getAllKeys(tableData.value, 'name') dialog.warning({
} title: '删除事件',
content: `确定删除事件 ${row.name} 吗?`,
watch( positiveText: '确定',
() => props.dd, negativeText: '取消',
async () => { onPositiveClick: async () => {
getEventList() //await deleteEventById(row.id)
} if (row.level == 1) {
) await deleteSimp({ id: row.id })
} else if (row.level == 4) {
return () => ( await deleteSon({ id: row.id })
<> }
<NDataTable searchTreeList()
class="h-full" },
flex-height })
indent={30} }
v-model:expanded-row-keys={expandedRowKeys.value}
columns={columns} function ddConfig(row) {
data={tableData.value} console.log(row)
row-key={row => row.name} dialog.create({
/> style: 'width:auto;height:90vh',
<MainEventEdit v-model:show={showMainEvent.value} /> maskClosable: false,
class: 'flex flex-col',
<SubEventEdit v-model:show={showNewEvent.value} /> title: 'DD轨迹',
</> contentClass: 'flex-1 h-0',
) content: () => <EventDdConfig />,
}, // positiveText: '',
}) // negativeText: '',
// onPositiveClick: () => {},
})
}
const { getAllKeys } = useTree()
const expandedRowKeys = ref([])
watch(
() => props.tableData,
() => {
expandedRowKeys.value = getAllKeys(props.tableData, 'name') || []
},
{
immediate: true,
}
)
return () => (
<>
<NDataTable
remote
class="h-full"
flex-height
indent={20}
v-model:expanded-row-keys={expandedRowKeys.value}
columns={columns}
data={props.tableData}
row-key={row => row.name}
/>
<MainEventEdit v-model:show={showMainEvent.value} />
<SubEventEdit v-model:show={showNewEvent.value} />
</>
)
},
})

94
src/views/Gantt/components/Gantt/components/VRender.jsx Normal file → Executable file
View File

@ -1,47 +1,47 @@
import { VImage, VGroup, VRect, VText } from '@visactor/vtable/es/vrender' import { VImage, VGroup, VRect, VText } from '@visactor/vtable/es/vrender'
const textColor = '#65c5e7' const textColor = '#65c5e7'
export default defineComponent({ export default defineComponent({
props: { props: {
width: { width: {
type: Number, type: Number,
default: 0, default: 0,
}, },
height: { height: {
type: Number, type: Number,
default: 0, default: 0,
}, },
taskRecord: { taskRecord: {
type: Object, type: Object,
default: () => ({}), default: () => ({}),
}, },
}, },
setup(props) { setup(props) {
const { width, height, taskRecord } = props const { width, height, taskRecord } = props
return () => ( return () => (
<VGroup <VGroup
attribute={{ attribute={{
width, width,
height, height,
fill: 'transparent', fill: 'transparent',
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
justifyContent: 'center', justifyContent: 'center',
cursor: 'pointer', cursor: 'pointer',
}} }}
> >
<VText <VText
attribute={{ attribute={{
text: 'taskRecord.name', text: 'taskRecord.name',
fontSize: 16, fontSize: 16,
fontFamily: 'sans-serif', fontFamily: 'sans-serif',
fill: textColor, fill: textColor,
fontWeight: 'bold', fontWeight: 'bold',
maxLineWidth: width, maxLineWidth: width,
textAlign: 'center', textAlign: 'center',
}} }}
></VText> ></VText>
</VGroup> </VGroup>
) )
}, },
}) })

1061
src/views/Gantt/components/Gantt/hooks/gantt.ts Normal file → Executable file

File diff suppressed because it is too large Load Diff

139
src/views/Gantt/components/Gantt/index.jsx Normal file → Executable file
View File

@ -1,51 +1,88 @@
import { NImage } from 'naive-ui' import { NImage } from 'naive-ui'
import useGantt from './hooks/gantt' import useGantt from './hooks/gantt'
import { useRouter, useRoute } from 'vue-router' import { useRouter, useRoute } from 'vue-router'
export default defineComponent({ export default defineComponent({
props: { props: {
scale: { scale: {
type: String, type: String,
default: 'day', default: 'day',
}, },
}, dateRange: {
setup(props) { type: Array,
const router = useRouter() require: true,
const route = useRoute() },
const { renderMainTask, changeTimeScales, currentImage } = useGantt({ types: {
route, type: Array,
router, require: true,
}) },
},
onMounted(() => { setup(props, { expose }) {
nextTick(() => { const router = useRouter()
renderMainTask(document.querySelector('#tableContainer')) const route = useRoute()
}) const { renderMainTask, changeTimeScales, currentImage } = useGantt({
}) route,
watch( router,
() => props.scale, })
val => {
changeTimeScales(val) const refresh = ref(false)
}
) expose({ refresh })
const imgRef = ref(null) watch(refresh, val => {
watch(currentImage, imgUrl => { if (val) {
nextTick(() => { renderMainTask(document.querySelector('#tableContainer'), {
if (imgUrl) { ids: props.types,
imgRef.value.click() startTime: props.dateRange
} ? new Date(props.dateRange[0]).toISOString()
}) : null,
}) endTime: props.dateRange
return () => ( ? new Date(props.dateRange[1]).toISOString()
<> : null,
<div id="tableContainer" class="bg-[#1c202c] w-h-full"></div> })
<NImage refresh.value = false
class="absolute h-0" }
ref={imgRef} })
src={currentImage.value?.[0]}
></NImage> onMounted(() => {
</> nextTick(() => {
) // console.log(props);
}, renderMainTask(document.querySelector('#tableContainer'), {
}) ids: props.types,
startTime: props.dateRange
? new Date(props.dateRange[0]).toISOString()
: null,
endTime: props.dateRange
? new Date(props.dateRange[1]).toISOString()
: null,
})
refresh.value = false
})
})
watch(
() => props.scale,
val => {
changeTimeScales(val)
}
)
const imgRef = ref(null)
watch(currentImage, imgUrl => {
nextTick(() => {
if (imgUrl) {
imgRef.value.click()
}
})
})
return () => (
<>
<div id="tableContainer" class="bg-[#1c202c] w-h-full"></div>
<NImage
class="absolute h-0"
ref={imgRef}
src={currentImage.value?.[0]}
></NImage>
</>
)
},
})

View File

@ -0,0 +1,47 @@
import { VImage, VGroup, VRect, VText } from '@visactor/vtable/es/vrender'
const textColor = '#65c5e7'
export default defineComponent({
props: {
width: {
type: Number,
default: 0,
},
height: {
type: Number,
default: 0,
},
taskRecord: {
type: Object,
default: () => ({}),
},
},
setup(props) {
const { width, height, taskRecord } = props
return () => (
<VGroup
attribute={{
width,
height,
fill: 'transparent',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
cursor: 'pointer',
}}
>
<VText
attribute={{
text: 'taskRecord.name',
fontSize: 16,
fontFamily: 'sans-serif',
fill: textColor,
fontWeight: 'bold',
maxLineWidth: width,
textAlign: 'center',
}}
></VText>
</VGroup>
)
},
})

View File

@ -0,0 +1,452 @@
import { Group, Image, Text, CheckBox, Rect } from '@visactor/vtable/es/vrender'
import { Gantt, tools, TYPES } from '@visactor/vtable-gantt'
import { getMainGantt, getSubGantt } from '@/api/Gantt'
type GanttParams = {
route?: any
router?: any
}
let ganttInstance: null | Gantt = null
const bgColor = '#1c202c'
const headerBgColor = '#33566f22'
const textColor = '#65c5e7'
const textColorWithOp = '#75fbfd22'
const useGantt = ({ router, route }: GanttParams) => {
const currentImage = ref()
const { subId } = route.params
const records = ref([])
onMounted(() => {
getGanttData()
})
async function getGanttData() {
if (subId) {
const res = await getSubGantt(subId)
records.value = res
ganttInstance?.setRecords(records.value)
} else {
const res = await getMainGantt()
// console.log(res, '----')
records.value = res
ganttInstance?.setRecords(records.value)
}
}
function renderMainTask(dom: HTMLElement) {
const option = getOption()
ganttInstance = new Gantt(dom, option)
window['ganttInstance'] = ganttInstance
// console.log(ganttInstance)
}
function getOption(): TYPES.GanttConstructorOptions {
const option = {
records: records.value,
taskListTable: renderTaskListTable(),
tasksShowMode: TYPES.TasksShowMode.Sub_Tasks_Arrange,
// groupBy: 'name',
// groupField: 'name',
// widthMode: 'standard',
// groupTitleFieldFormat: (record, col, row, table) => {
// console.log(record, col, row, table, '----')
// const groupData = table.getGroupData(record); // 获取分组数据
// const count = groupData ? groupData.length : 0; // 计算分组下的记录数量
// return `${record.name} (${count})`; // 返回格式化的分组标题
// },
frame: {
outerFrameStyle: {
borderLineWidth: 2,
borderColor: textColor,
cornerRadius: 3,
},
// verticalSplitLineHighlight: {
// lineColor: 'green',
// lineWidth: 3,
// },
},
grid: {
// backgroundColor: bgColor,
horizontalLine: {
lineWidth: 1,
lineColor: textColorWithOp,
},
// verticalLine: {
// lineWidth: 1,
// lineColor: textColorWithOp,
// lineDash: [4, 8],
// },
},
taskList: {
// backgroundColor: bgColor,
headerStyle: {
borderColor: '#e1e4e8',
borderLineWidth: 0,
fontSize: 18,
fontWeight: 'bold',
color: 'red',
},
},
headerRowHeight: 59,
rowHeight: subId ? 200 : 100,
taskBar: renderTaskBar(subId),
timelineHeader: {
backgroundColor: headerBgColor,
colWidth: 140,
// colWidth: 1040,
// verticalLine: {
// lineColor: textColorWithOp,
// lineWidth: 1,
// lineDash: [4, 2],
// },
horizontalLine: {
lineColor: textColorWithOp,
lineWidth: 1,
lineDash: [4, 2],
},
scales: getTimeScales('day'),
},
minDate: '2024-11-14',
maxDate: '2024-12-30',
markLine: [
{
date: '2024-07-29',
style: {
lineWidth: 1,
lineColor: 'blue',
lineDash: [8, 4],
},
},
// {
// date: '2024-08-17',
// style: {
// lineWidth: 2,
// lineColor: 'red',
// lineDash: [8, 4],
// },
// },
],
scrollStyle: {
scrollRailColor: 'RGBA(246,246,246,0)',
visible: 'focus',
width: 6,
scrollSliderCornerRadius: 2,
scrollSliderColor: 'rgba(255,255,255,0.25)',
},
underlayBackgroundColor: bgColor,
}
return option as TYPES.GanttConstructorOptions
}
function renderColumn() {
const columns = [
{
field: 'name',
title: subId ? '事件类型' : '事件主体',
width: '120',
mergeCell: true,
customLayout: args => {
const { table, row, col, rect, dataValue } = args
// console.log(
// table,
// '1',
// row,
// '2',
// col,
// '3',
// rect,
// '4',
// dataValue,
// '5',
// '-----------'
// )
const { height, width } = rect ?? table.getCellRect(col, row)
const container = new Group({
width,
height,
fill: 'transparent',
// fill: textColor,
// fillOpacity: 0.1,
// stroke: textColor,
// strokeOpacity: 0.2,
// lineWidth: 4,
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
cursor: 'pointer',
})
console.log(args)
const count = table.records.find(r => dataValue === r.name)?.children
?.length
const values = new Text({
text: `${dataValue}`,
fontSize: 16,
fontFamily: 'sans-serif',
fill: textColor,
textAlign: 'center',
// boundsPadding: [10, 0, 0, 0],
})
const counts = new Text({
text: `( ${count} )`,
fontSize: 13,
fontFamily: 'sans-serif',
fill: textColor,
boundsPadding: [10, 0, 0, 0],
})
container.add(values)
container.add(counts)
return {
rootContainer: container,
}
},
},
]
if (subId) {
columns.unshift({
field: 'isChecked',
title: '',
width: '60',
headerType: 'checkbox',
cellType: 'checkbox',
})
}
return columns
}
function renderTaskListTable() {
const taskListTable = {
columns: renderColumn(),
// tableWidth: 'auto',
theme: {
underlayBackgroundColor: bgColor,
headerStyle: {
borderColor: textColorWithOp,
borderLineWidth: 1,
fontWeight: 'bold',
color: textColor,
bgColor: headerBgColor,
textAlign: 'center',
fontSize: 20,
hover: {
cellBgColor: 'transparent',
},
},
bodyStyle: {
borderColor: textColorWithOp,
textAlign: 'center',
borderLineWidth: 1,
autoWrapText: true,
fontSize: 16,
color: textColor,
bgColor: bgColor,
hover: {
cellBgColor: textColorWithOp,
},
},
},
}
return taskListTable
}
function renderTaskBar() {
const taskBar = {
resizable: false,
moveable: false,
startDateField: 'start',
endDateField: 'end',
// progressField: 'progress',
barStyle: { width: subId ? 180 : 60 },
customLayout: args => {
const { width, height, startDate, endDate, taskRecord } = args
const container = new Group({
width,
height,
fill: 'transparent',
// fill: textColor,
// fillOpacity: 0.1,
// stroke: textColor,
// strokeOpacity: 0.2,
// lineWidth: 4,
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
cursor: 'pointer',
})
if (!subId) {
container.addEventListener('click', () => {
console.log(taskRecord, 'ooooooo')
router.push({
path: `/gantt/sub/${taskRecord.id}`,
})
})
}
if (subId) {
const image = new Image({
image: taskRecord.avatar,
width: 100,
height: 100,
x: 10,
y: 10,
boundsPadding: [0, 0, 10, 0],
cursor: 'pointer',
})
container.add(image)
image.addEventListener('click', () => {
currentImage.value = [taskRecord.avatar, Date.now()]
// console.log(currentImage, 'currentImage')
})
}
// const checkbox = new CheckBox({
// width: 20,
// height: 20,
// checked: false,
// })
// container.add(checkbox)
// checkbox.addEventListener('click', event => {
// console.log(event, 'event')
// })
// console.log(taskRecord, 'taskRecord')
const nameContainer = new Group({
fill: 'transparent',
display: 'flex',
// flexDirection: 'column',
// justifyContent: 'center',
alignItems: 'center',
})
container.add(nameContainer)
if ('trajData' in taskRecord && taskRecord.trajData) {
const taskRecordSymbol = new Image({
width: 20,
height: 20,
fill: '#ff0',
image:
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10s10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8s8 3.59 8 8s-3.59 8-8 8zm0-12.5c-2.49 0-4.5 2.01-4.5 4.5s2.01 4.5 4.5 4.5s4.5-2.01 4.5-4.5s-2.01-4.5-4.5-4.5zm0 5.5c-.55 0-1-.45-1-1s.45-1 1-1s1 .45 1 1s-.45 1-1 1z" fill="#ff0"></path></svg>',
boundsPadding: [-3, 10, 0, 0],
cursor: 'pointer',
})
nameContainer.add(taskRecordSymbol)
}
const name = new Text({
text: taskRecord.name,
fontSize: 16,
fontFamily: 'sans-serif',
fill: textColor,
fontWeight: 'bold',
maxLineWidth: width,
textAlign: 'center',
// boundsPadding: [10, 0, 0, 0],
})
nameContainer.add(name)
const days = new Text({
text: `${startDate.toLocaleDateString()} ~ ${endDate.toLocaleDateString()}`,
fontSize: 13,
fontFamily: 'sans-serif',
fill: textColor,
boundsPadding: [10, 0, 0, 0],
})
container.add(days)
const rect = new Rect({
width: width,
height: 7,
fill: {
gradient: 'linear',
x0: 0,
y0: 0,
x1: 1,
y1: 0,
stops: [
{
offset: 0,
color: textColor,
},
{
offset: 1,
color: textColorWithOp,
},
],
},
boundsPadding: [10, 0, 0, 0],
})
container.add(rect)
return {
rootContainer: container,
}
},
hoverBarStyle: {
cornerRadius: 2,
barOverlayColor: textColorWithOp,
},
selectedBarStyle: {
// cornerRadius: 2,
borderColor: textColorWithOp,
borderLineWidth: 2,
},
}
return taskBar
}
function renderGroup(opt: IGroupGraphicAttribute) {
return new Group(opt)
}
function renderText(opt: ITextGraphicAttribute) {
return new Text(opt)
}
function renderImage(opt: IImageGraphicAttribute) {
return new Image(opt)
}
function changeTimeScales(scale: TYPES.ITimelineScale['unit']) {
const scales = getTimeScales(scale)
ganttInstance && ganttInstance.updateScales(scales)
}
function getTimeScales(
scale: TYPES.ITimelineScale['unit']
): TYPES.ITimelineScale[] {
return [
{
unit: scale,
step: 1,
customLayout: args => {
const { width, height, startDate } = args
const container = new Group({
width,
height,
display: 'flex',
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
flexWrap: 'nowrap',
})
const day = new Text({
text: startDate.toLocaleDateString(),
// scale === 'day'
// ? startDate.toLocaleDateString()
// : startDate.toLocaleTimeString(),
fontSize: 14,
fontWeight: 'bold',
fontFamily: 'sans-serif',
fill: textColor,
textAlign: 'right',
maxLineWidth: width,
})
container.add(day)
return {
rootContainer: container,
}
},
},
]
}
return { renderMainTask, changeTimeScales, currentImage }
}
export default useGantt

View File

@ -0,0 +1,51 @@
import { NImage } from 'naive-ui'
import useGantt from './hooks/gantt'
import { useRouter, useRoute } from 'vue-router'
export default defineComponent({
props: {
scale: {
type: String,
default: 'day',
},
},
setup(props) {
const router = useRouter()
const route = useRoute()
const { renderMainTask, changeTimeScales, currentImage } = useGantt({
route,
router,
})
onMounted(() => {
nextTick(() => {
renderMainTask(document.querySelector('#tableContainer'))
})
})
watch(
() => props.scale,
val => {
changeTimeScales(val)
}
)
const imgRef = ref(null)
watch(currentImage, imgUrl => {
nextTick(() => {
if (imgUrl) {
imgRef.value.click()
}
})
})
return () => (
<>
<div id="tableContainer" class="bg-[#1c202c] w-h-full"></div>
<NImage
class="absolute h-0"
ref={imgRef}
src={currentImage.value?.[0]}
></NImage>
</>
)
},
})

View File

@ -0,0 +1,47 @@
import { VImage, VGroup, VRect, VText } from '@visactor/vtable/es/vrender'
const textColor = '#65c5e7'
export default defineComponent({
props: {
width: {
type: Number,
default: 0,
},
height: {
type: Number,
default: 0,
},
taskRecord: {
type: Object,
default: () => ({}),
},
},
setup(props) {
const { width, height, taskRecord } = props
return () => (
<VGroup
attribute={{
width,
height,
fill: 'transparent',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
cursor: 'pointer',
}}
>
<VText
attribute={{
text: 'taskRecord.name',
fontSize: 16,
fontFamily: 'sans-serif',
fill: textColor,
fontWeight: 'bold',
maxLineWidth: width,
textAlign: 'center',
}}
></VText>
</VGroup>
)
},
})

View File

@ -0,0 +1,461 @@
import { Group, Image, Text, CheckBox, Rect } from '@visactor/vtable/es/vrender'
import { Gantt, tools, TYPES } from '@visactor/vtable-gantt'
import { getMainGantt, getSubGantt } from '@/api/Gantt/gantt'
type GanttParams = {
route?: any
router?: any
}
let ganttInstance: null | Gantt = null
const bgColor = '#1c202c'
const headerBgColor = '#33566f22'
const textColor = '#65c5e7'
const textColorWithOp = '#75fbfd22'
const useGantt = ({ router, route }: GanttParams) => {
const currentImage = ref()
const { subId } = route.params
const records = ref([])
const timeRange = ref([])
// onMounted(() => {
// getGanttData()
// })
async function getGanttData(params: Record<string, string>) {
timeRange.value = [params.startTime, params.endTime]
console.log('%csubId', 'color:red;font-size:20px', subId);
const { code, data } = subId ? await getSubGantt({ activityId: subId }) : await getMainGantt(params)
if (code === 200) {
records.value = data.list
ganttInstance?.setRecords(records.value)
}
}
async function renderMainTask(dom: HTMLElement, params: Record<string, string>) {
await getGanttData(params)
const option = getOption()
ganttInstance = new Gantt(dom, option)
window['ganttInstance'] = ganttInstance
}
function getOption(): TYPES.GanttConstructorOptions {
console.log(records.value);
const option = {
records: records.value,
taskListTable: renderTaskListTable(),
tasksShowMode: TYPES.TasksShowMode.Sub_Tasks_Arrange,
// groupBy: 'name',
// groupField: 'name',
// widthMode: 'standard',
// groupTitleFieldFormat: (record, col, row, table) => {
// console.log(record, col, row, table, '----')
// const groupData = table.getGroupData(record); // 获取分组数据
// const count = groupData ? groupData.length : 0; // 计算分组下的记录数量
// return `${record.name} (${count})`; // 返回格式化的分组标题
// },
frame: {
outerFrameStyle: {
borderLineWidth: 2,
borderColor: textColor,
cornerRadius: 3,
},
// verticalSplitLineHighlight: {
// lineColor: 'green',
// lineWidth: 3,
// },
},
grid: {
// backgroundColor: bgColor,
horizontalLine: {
lineWidth: 1,
lineColor: textColorWithOp,
},
// verticalLine: {
// lineWidth: 1,
// lineColor: textColorWithOp,
// lineDash: [4, 8],
// },
},
taskList: {
// backgroundColor: bgColor,
headerStyle: {
borderColor: '#e1e4e8',
borderLineWidth: 0,
fontSize: 18,
fontWeight: 'bold',
color: 'red',
},
},
headerRowHeight: 59,
rowHeight: subId ? 200 : 100,
taskBar: renderTaskBar(subId),
timelineHeader: {
backgroundColor: headerBgColor,
colWidth: 4000,
// colWidth: 1040,
// verticalLine: {
// lineColor: textColorWithOp,
// lineWidth: 1,
// lineDash: [4, 2],
// },
horizontalLine: {
lineColor: textColorWithOp,
lineWidth: 1,
lineDash: [4, 2],
},
scales: getTimeScales('month'),
},
minDate: timeRange.value[0],
maxDate: timeRange.value[1],
markLine: [
{
date: '2024-03-13T13:15:10',
style: {
lineWidth: 1,
lineColor: 'blue',
lineDash: [8, 4],
},
},
// {
// date: '2024-08-17',
// style: {
// lineWidth: 2,
// lineColor: 'red',
// lineDash: [8, 4],
// },
// },
],
scrollStyle: {
scrollRailColor: 'RGBA(246,246,246,0)',
visible: 'focus',
width: 10,
scrollSliderCornerRadius: 2,
scrollSliderColor: 'rgba(255,255,255,0.25)',
},
underlayBackgroundColor: bgColor,
}
return option as TYPES.GanttConstructorOptions
}
function renderColumn() {
const columns = [
{
field: 'name',
title: subId ? '事件类型' : '事件主体',
width: '120',
mergeCell: true,
customLayout: args => {
// console.log(args, 'srgs')
const { table, row, col, rect, dataValue } = args
// console.log(
// table,
// '1',
// row,
// '2',
// col,
// '3',
// rect,
// '4',
// dataValue,
// '5',
// '-----------'
// )
const { height, width } = rect ?? table.getCellRect(col, row)
const container = new Group({
width,
height,
fill: 'transparent',
// fill: textColor,
// fillOpacity: 0.1,
// stroke: textColor,
// strokeOpacity: 0.2,
// lineWidth: 4,
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
cursor: 'pointer',
boundsPadding: [10, 0, 0, 0],
})
// console.log('args-----', args);
const count = table.records.find(r => dataValue === r.name)?.children
?.length || 0
const values = new Text({
text: `${dataValue}`,
fontSize: 16,
fontFamily: 'sans-serif',
fill: textColor,
textAlign: 'center',
maxLineWidth: width,
whiteSpace: 'normal'
// boundsPadding: [10, 10, 10, 10],
})
const counts = new Text({
text: `( ${count} )`,
fontSize: 13,
fontFamily: 'sans-serif',
fill: textColor,
boundsPadding: [10, 0, 0, 0],
})
container.add(values)
container.add(counts)
return {
rootContainer: container,
}
},
},
]
if (subId) {
columns.unshift({
field: 'isChecked',
title: '',
width: '60',
headerType: 'checkbox',
cellType: 'checkbox',
})
}
return columns
}
function renderTaskListTable() {
const taskListTable = {
columns: renderColumn(),
// tableWidth: 'auto',
theme: {
underlayBackgroundColor: bgColor,
headerStyle: {
borderColor: textColorWithOp,
borderLineWidth: 1,
fontWeight: 'bold',
color: textColor,
bgColor: headerBgColor,
textAlign: 'center',
fontSize: 20,
hover: {
cellBgColor: 'transparent',
},
},
bodyStyle: {
borderColor: textColorWithOp,
textAlign: 'center',
borderLineWidth: 1,
autoWrapText: true,
fontSize: 16,
color: textColor,
bgColor: bgColor,
hover: {
cellBgColor: textColorWithOp,
},
},
},
}
return taskListTable
}
function renderTaskBar(subId: string | number) {
// console.log(subId, '------');
const taskBar = {
resizable: false,
moveable: false,
startDateField: 'startTime',
endDateField: 'endTime',
// progressField: 'progress',
barStyle: { width: subId ? 180 : 60 },
customLayout: args => {
const { width, height, startDate, endDate, taskRecord } = args
// console.log(taskRecord, 'taskRecord');
const container = new Group({
width,
height,
fill: 'transparent',
// fill: textColor,
// fillOpacity: 0.1,
// stroke: textColor,
// strokeOpacity: 0.2,
// lineWidth: 4,
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
cursor: 'pointer',
})
if (!subId) {
container.addEventListener('click', () => {
console.log(taskRecord, 'ooooooo')
router.push({
path: `/gantt/sub/${taskRecord.id}`,
})
})
}
if (subId) {
const image = new Image({
image: taskRecord.avatar,
width: 100,
height: 100,
x: 10,
y: 10,
boundsPadding: [0, 0, 10, 0],
cursor: 'pointer',
})
container.add(image)
image.addEventListener('click', () => {
currentImage.value = [taskRecord.avatar, Date.now()]
// console.log(currentImage, 'currentImage')
})
}
// const checkbox = new CheckBox({
// width: 20,
// height: 20,
// checked: false,
// })
// container.add(checkbox)
// checkbox.addEventListener('click', event => {
// console.log(event, 'event')
// })
// console.log(taskRecord, 'taskRecord')
const nameContainer = new Group({
fill: 'transparent',
display: 'flex',
// flexDirection: 'column',
// justifyContent: 'center',
alignItems: 'center',
})
container.add(nameContainer)
if ('trajData' in taskRecord && taskRecord.trajData) {
const taskRecordSymbol = new Image({
width: 20,
height: 20,
fill: '#ff0',
image:
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10s10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8s8 3.59 8 8s-3.59 8-8 8zm0-12.5c-2.49 0-4.5 2.01-4.5 4.5s2.01 4.5 4.5 4.5s4.5-2.01 4.5-4.5s-2.01-4.5-4.5-4.5zm0 5.5c-.55 0-1-.45-1-1s.45-1 1-1s1 .45 1 1s-.45 1-1 1z" fill="#ff0"></path></svg>',
boundsPadding: [-3, 10, 0, 0],
cursor: 'pointer',
})
nameContainer.add(taskRecordSymbol)
}
const name = new Text({
text: taskRecord.name,
fontSize: 16,
fontFamily: 'sans-serif',
fill: textColor,
fontWeight: 'bold',
maxLineWidth: width,
textAlign: 'center',
// boundsPadding: [10, 0, 0, 0],
})
nameContainer.add(name)
const days = new Text({
text: `${startDate.toLocaleDateString()} ~ ${endDate.toLocaleDateString()}`,
fontSize: 13,
fontFamily: 'sans-serif',
fill: textColor,
boundsPadding: [10, 0, 0, 0],
})
container.add(days)
const rect = new Rect({
width: width,
height: 7,
fill: {
gradient: 'linear',
x0: 0,
y0: 0,
x1: 1,
y1: 0,
stops: [
{
offset: 0,
color: textColor,
},
{
offset: 1,
color: textColorWithOp,
},
],
},
boundsPadding: [10, 0, 0, 0],
})
container.add(rect)
return {
rootContainer: container,
}
},
hoverBarStyle: {
cornerRadius: 2,
barOverlayColor: textColorWithOp,
},
selectedBarStyle: {
// cornerRadius: 2,
borderColor: textColorWithOp,
borderLineWidth: 2,
},
}
console.log(taskBar, '0000000');
return taskBar
}
function renderGroup(opt: IGroupGraphicAttribute) {
return new Group(opt)
}
function renderText(opt: ITextGraphicAttribute) {
return new Text(opt)
}
function renderImage(opt: IImageGraphicAttribute) {
return new Image(opt)
}
function changeTimeScales(scale: TYPES.ITimelineScale['unit']) {
const scales = getTimeScales(scale)
ganttInstance && ganttInstance.updateScales(scales)
}
function getTimeScales(
scale: TYPES.ITimelineScale['unit']
): TYPES.ITimelineScale[] {
return [
{
unit: scale,
step: 1,
customLayout: args => {
const { width, height, startDate } = args
const container = new Group({
width,
height,
display: 'flex',
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
flexWrap: 'nowrap',
})
const day = new Text({
text: startDate.toLocaleDateString(),
// scale === 'day'
// ? startDate.toLocaleDateString()
// : startDate.toLocaleTimeString(),
fontSize: 14,
fontWeight: 'bold',
fontFamily: 'sans-serif',
fill: textColor,
textAlign: 'right',
maxLineWidth: width,
})
container.add(day)
return {
rootContainer: container,
}
},
},
]
}
return { renderMainTask, changeTimeScales, currentImage }
}
export default useGantt

View File

@ -0,0 +1,66 @@
import { NImage } from 'naive-ui'
import useGantt from './hooks/gantt'
import { useRouter, useRoute } from 'vue-router'
export default defineComponent({
props: {
scale: {
type: String,
default: 'day',
},
dateRange: {
type: Array,
require: true,
},
types: {
type: Array,
require: true,
}
},
setup(props, { expose }) {
const router = useRouter()
const route = useRoute()
const { renderMainTask, changeTimeScales, currentImage } = useGantt({
route,
router,
})
expose({ renderMainTask })
onMounted(() => {
nextTick(() => {
// console.log(props);
renderMainTask(document.querySelector('#tableContainer'), {
ids: props.types,
startTime: props.dateRange ? new Date(props.dateRange[0]).toISOString() : null,
endTime: props.dateRange ? new Date(props.dateRange[1]).toISOString() : null
})
})
})
watch(
() => props.scale,
val => {
changeTimeScales(val)
}
)
const imgRef = ref(null)
watch(currentImage, imgUrl => {
nextTick(() => {
if (imgUrl) {
imgRef.value.click()
}
})
})
return () => (
<>
<div id="tableContainer" class="bg-[#1c202c] w-h-full"></div>
<NImage
class="absolute h-0"
ref={imgRef}
src={currentImage.value?.[0]}
></NImage>
</>
)
},
})

778
src/views/Gantt/components/GanttEdit/hooks/ganttEdit.ts Normal file → Executable file
View File

@ -1,389 +1,389 @@
import { Group, Image, Text, CheckBox, Rect } from '@visactor/vtable/es/vrender' import { Group, Image, Text, CheckBox, Rect } from '@visactor/vtable/es/vrender'
import { useDialog } from 'naive-ui' import { useDialog } from 'naive-ui'
import { Gantt, tools, TYPES } from '@visactor/vtable-gantt' import { Gantt, tools, TYPES } from '@visactor/vtable-gantt'
import { getMainGantt, getSubGantt } from '@/api/Gantt' import { getMainGantt, getSubGantt } from '@/api/Gantt'
type GanttParams = { type GanttParams = {
route?: any route?: any
router?: any router?: any
} }
let ganttInstance: null | Gantt = null let ganttInstance: null | Gantt = null
const bgColor = '#1c202c' const bgColor = '#1c202c'
const headerBgColor = '#33566f22' const headerBgColor = '#33566f22'
const textColor = '#65c5e7' const textColor = '#65c5e7'
const textColorWithOp = '#75fbfd22' const textColorWithOp = '#75fbfd22'
const useGanttEdit = ({ router, route }: GanttParams) => { const useGanttEdit = ({ router, route }: GanttParams) => {
const { subId } = route.params const { subId } = route.params
const records = ref([]) const records = ref([])
const dialog = useDialog() const dialog = useDialog()
onMounted(() => { onMounted(() => {
getGanttData() getGanttData()
}) })
async function getGanttData() { async function getGanttData() {
if (subId) { if (subId) {
const res = await getSubGantt(subId) const res = await getSubGantt(subId)
records.value = res records.value = res
ganttInstance?.setRecords(records.value) ganttInstance?.setRecords(records.value)
} else { } else {
const res = await getMainGantt() const res = await getMainGantt()
// console.log(res, '----') // console.log(res, '----')
records.value = res records.value = res
ganttInstance?.setRecords(records.value) ganttInstance?.setRecords(records.value)
} }
} }
function renderMainTask(dom: HTMLElement) { function renderMainTask(dom: HTMLElement) {
const option = getOption() const option = getOption()
ganttInstance = new Gantt(dom, option) ganttInstance = new Gantt(dom, option)
window['ganttInstance'] = ganttInstance window['ganttInstance'] = ganttInstance
console.log(ganttInstance) console.log(ganttInstance)
} }
function getOption(): TYPES.GanttConstructorOptions { function getOption(): TYPES.GanttConstructorOptions {
const option = { const option = {
records: records.value, records: records.value,
taskListTable: renderTaskListTable(), taskListTable: renderTaskListTable(),
tasksShowMode: TYPES.TasksShowMode.Sub_Tasks_Arrange, tasksShowMode: TYPES.TasksShowMode.Sub_Tasks_Arrange,
frame: { frame: {
outerFrameStyle: { outerFrameStyle: {
borderLineWidth: 2, borderLineWidth: 2,
borderColor: textColor, borderColor: textColor,
cornerRadius: 3, cornerRadius: 3,
}, },
// verticalSplitLineHighlight: { // verticalSplitLineHighlight: {
// lineColor: 'green', // lineColor: 'green',
// lineWidth: 3 // lineWidth: 3
// } // }
}, },
grid: { grid: {
// backgroundColor: bgColor, // backgroundColor: bgColor,
horizontalLine: { horizontalLine: {
lineWidth: 1, lineWidth: 1,
lineColor: textColorWithOp, lineColor: textColorWithOp,
}, },
verticalLine: { verticalLine: {
lineWidth: 1, lineWidth: 1,
lineColor: textColorWithOp, lineColor: textColorWithOp,
lineDash: [4, 8], lineDash: [4, 8],
}, },
}, },
taskList: { taskList: {
// backgroundColor: bgColor, // backgroundColor: bgColor,
headerStyle: { headerStyle: {
borderColor: '#e1e4e8', borderColor: '#e1e4e8',
borderLineWidth: 0, borderLineWidth: 0,
fontSize: 18, fontSize: 18,
fontWeight: 'bold', fontWeight: 'bold',
color: 'red', color: 'red',
}, },
}, },
headerRowHeight: 59, headerRowHeight: 59,
rowHeight: subId ? 200 : 100, rowHeight: subId ? 200 : 100,
taskBar: renderTaskBar(), taskBar: renderTaskBar(),
timelineHeader: { timelineHeader: {
backgroundColor: headerBgColor, backgroundColor: headerBgColor,
colWidth: 150, colWidth: 150,
verticalLine: { verticalLine: {
lineColor: textColorWithOp, lineColor: textColorWithOp,
lineWidth: 1, lineWidth: 1,
lineDash: [4, 2], lineDash: [4, 2],
}, },
horizontalLine: { horizontalLine: {
lineColor: textColorWithOp, lineColor: textColorWithOp,
lineWidth: 1, lineWidth: 1,
lineDash: [4, 2], lineDash: [4, 2],
}, },
scales: getTimeScales('day'), scales: getTimeScales('day'),
}, },
minDate: '2024-11-14', minDate: '2024-11-14',
maxDate: '2024-12-30', maxDate: '2024-12-30',
scrollStyle: { scrollStyle: {
scrollRailColor: 'RGBA(246,246,246,0)', scrollRailColor: 'RGBA(246,246,246,0)',
visible: 'focus', visible: 'focus',
width: 6, width: 6,
scrollSliderCornerRadius: 2, scrollSliderCornerRadius: 2,
scrollSliderColor: 'rgba(255,255,255,0.25)', scrollSliderColor: 'rgba(255,255,255,0.25)',
}, },
underlayBackgroundColor: bgColor, underlayBackgroundColor: bgColor,
} }
return option as TYPES.GanttConstructorOptions return option as TYPES.GanttConstructorOptions
} }
function renderColumn() { function renderColumn() {
const columns = [ const columns = [
{ {
field: 'name', field: 'name',
title: subId ? '事件类型' : '事件主体', title: subId ? '事件类型' : '事件主体',
width: '120', width: '120',
mergeCell: true, mergeCell: true,
}, },
] ]
// if (subId) { // if (subId) {
// columns.unshift({ // columns.unshift({
// field: 'isChecked', // field: 'isChecked',
// title: '', // title: '',
// width: '60', // width: '60',
// headerType: 'checkbox', // headerType: 'checkbox',
// cellType: 'checkbox', // cellType: 'checkbox',
// }) // })
// } // }
return columns return columns
} }
function renderTaskListTable() { function renderTaskListTable() {
const taskListTable = { const taskListTable = {
columns: renderColumn(), columns: renderColumn(),
// tableWidth: 'auto', // tableWidth: 'auto',
theme: { theme: {
underlayBackgroundColor: bgColor, underlayBackgroundColor: bgColor,
headerStyle: { headerStyle: {
borderColor: textColorWithOp, borderColor: textColorWithOp,
borderLineWidth: 1, borderLineWidth: 1,
fontWeight: 'bold', fontWeight: 'bold',
color: textColor, color: textColor,
bgColor: headerBgColor, bgColor: headerBgColor,
textAlign: 'center', textAlign: 'center',
fontSize: 20, fontSize: 20,
hover: { hover: {
cellBgColor: 'transparent', cellBgColor: 'transparent',
}, },
}, },
bodyStyle: { bodyStyle: {
borderColor: textColorWithOp, borderColor: textColorWithOp,
textAlign: 'center', textAlign: 'center',
borderLineWidth: 1, borderLineWidth: 1,
autoWrapText: true, autoWrapText: true,
fontSize: 16, fontSize: 16,
color: textColor, color: textColor,
bgColor: bgColor, bgColor: bgColor,
hover: { hover: {
cellBgColor: textColorWithOp, cellBgColor: textColorWithOp,
}, },
}, },
}, },
} }
return taskListTable return taskListTable
} }
function renderTaskBar() { function renderTaskBar() {
const taskBar = { const taskBar = {
resizable: false, resizable: false,
moveable: false, moveable: false,
startDateField: 'start', startDateField: 'start',
endDateField: 'end', endDateField: 'end',
// progressField: 'progress', // progressField: 'progress',
barStyle: { width: subId ? 180 : 60 }, barStyle: { width: subId ? 180 : 60 },
customLayout: args => { customLayout: args => {
const { width, height, startDate, endDate, taskRecord } = args const { width, height, startDate, endDate, taskRecord } = args
const container = new Group({ const container = new Group({
width, width,
height, height,
fill: 'transparent', fill: 'transparent',
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
justifyContent: 'center', justifyContent: 'center',
// alignItems: 'center', // alignItems: 'center',
cursor: 'pointer', cursor: 'pointer',
}) })
if (!subId) { if (!subId) {
container.addEventListener('click', event => { container.addEventListener('click', event => {
router.push({ router.push({
path: `/gantt/subEdit/${taskRecord.id}`, path: `/gantt/subEdit/${taskRecord.id}`,
}) })
}) })
} }
if (subId) { if (subId) {
const image = new Image({ const image = new Image({
image: taskRecord.avatar, image: taskRecord.avatar,
width: 100, width: 100,
height: 100, height: 100,
x: 10, x: 10,
y: 10, y: 10,
boundsPadding: [0, 0, 10, 0], boundsPadding: [0, 0, 10, 0],
}) })
container.add(image) container.add(image)
} }
const checkBoxGroup = new Group({ const checkBoxGroup = new Group({
width, width,
//height, //height,
fill: 'transparent', fill: 'transparent',
display: 'flex', display: 'flex',
}) })
container.add(checkBoxGroup) container.add(checkBoxGroup)
// const checkbox = new CheckBox({ // const checkbox = new CheckBox({
// id: taskRecord.id, // id: taskRecord.id,
// text: '', // text: '',
// checked: false, // checked: false,
// }) // })
// checkBoxGroup.add(checkbox) // checkBoxGroup.add(checkbox)
// checkbox.addEventListener('click', event => { // checkbox.addEventListener('click', event => {
// event.stopPropagation() // event.stopPropagation()
// console.log(checkbox, 'checkbox') // console.log(checkbox, 'checkbox')
// }) // })
const name = new Text({ const name = new Text({
text: taskRecord.name, text: taskRecord.name,
fontSize: 16, fontSize: 16,
fontFamily: 'sans-serif', fontFamily: 'sans-serif',
fill: textColor, fill: textColor,
fontWeight: 'bold', fontWeight: 'bold',
maxLineWidth: width, maxLineWidth: width,
textAlign: 'center', textAlign: 'center',
// boundsPadding: [0, 0, 0, 10], // boundsPadding: [0, 0, 0, 10],
}) })
checkBoxGroup.add(name) checkBoxGroup.add(name)
const editSymbol = new Image({ const editSymbol = new Image({
width: 20, width: 20,
height: 20, height: 20,
fill: '#fff', fill: '#fff',
image: image:
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 16 16"><g fill="none"><path d="M4.5 2A2.5 2.5 0 0 0 2 4.5v7A2.5 2.5 0 0 0 4.5 14h1.547l.25-1H4.5A1.5 1.5 0 0 1 3 11.5V5h10v1.036c.331-.058.671-.05 1 .023V4.5A2.5 2.5 0 0 0 11.5 2h-7zM3.085 4A1.5 1.5 0 0 1 4.5 3h7a1.5 1.5 0 0 1 1.415 1h-9.83zm11.46 3.455a1.56 1.56 0 0 0-2.207 0l-4.289 4.288a2.777 2.777 0 0 0-.73 1.29l-.303 1.212a.61.61 0 0 0 .739.739l1.211-.303a2.777 2.777 0 0 0 1.29-.73l4.289-4.289a1.56 1.56 0 0 0 0-2.207z" fill="#5c8e9e"></path></g></svg>', '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 16 16"><g fill="none"><path d="M4.5 2A2.5 2.5 0 0 0 2 4.5v7A2.5 2.5 0 0 0 4.5 14h1.547l.25-1H4.5A1.5 1.5 0 0 1 3 11.5V5h10v1.036c.331-.058.671-.05 1 .023V4.5A2.5 2.5 0 0 0 11.5 2h-7zM3.085 4A1.5 1.5 0 0 1 4.5 3h7a1.5 1.5 0 0 1 1.415 1h-9.83zm11.46 3.455a1.56 1.56 0 0 0-2.207 0l-4.289 4.288a2.777 2.777 0 0 0-.73 1.29l-.303 1.212a.61.61 0 0 0 .739.739l1.211-.303a2.777 2.777 0 0 0 1.29-.73l4.289-4.289a1.56 1.56 0 0 0 0-2.207z" fill="#5c8e9e"></path></g></svg>',
boundsPadding: [-2, 0, 0, 10], boundsPadding: [-2, 0, 0, 10],
cursor: 'pointer', cursor: 'pointer',
}) })
checkBoxGroup.add(editSymbol) checkBoxGroup.add(editSymbol)
editSymbol.addEventListener('click', event => { editSymbol.addEventListener('click', event => {
event.stopPropagation() event.stopPropagation()
console.log(editSymbol, 'editSymbol') console.log(editSymbol, 'editSymbol')
}) })
const deleteSymbol = new Image({ const deleteSymbol = new Image({
width: 20, width: 20,
height: 20, height: 20,
fill: '#fff', fill: '#fff',
image: image:
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24"><g fill="none"><path d="M16.5 12a5.5 5.5 0 1 1 0 11a5.5 5.5 0 0 1 0-11zM12 1.75a3.25 3.25 0 0 1 3.245 3.066L15.25 5h5.25a.75.75 0 0 1 .102 1.493L20.5 6.5h-.796l-.5 5.087c-.46-.21-.95-.37-1.46-.468l.453-4.619H5.802l1.267 12.872a1.25 1.25 0 0 0 1.117 1.122l.127.006h2.42c.286.551.65 1.056 1.076 1.5H8.313a2.75 2.75 0 0 1-2.714-2.307l-.023-.174L4.295 6.5H3.5a.75.75 0 0 1-.743-.648L2.75 5.75a.75.75 0 0 1 .648-.743L3.5 5h5.25A3.25 3.25 0 0 1 12 1.75zm1.716 12.839l-.07.057l-.057.07a.5.5 0 0 0 0 .568l.057.07l2.147 2.146l-2.147 2.146l-.057.07a.5.5 0 0 0 0 .568l.057.07l.07.057a.5.5 0 0 0 .568 0l.07-.057l2.146-2.147l2.146 2.147l.07.057a.5.5 0 0 0 .568 0l.07-.057l.057-.07a.5.5 0 0 0 0-.568l-.057-.07l-2.147-2.146l2.147-2.146l.057-.07a.5.5 0 0 0 0-.568l-.057-.07l-.07-.057a.5.5 0 0 0-.568 0l-.07.057l-2.146 2.147l-2.146-2.147l-.07-.057a.5.5 0 0 0-.492-.044l-.076.044zM12 3.25a1.75 1.75 0 0 0-1.744 1.606L10.25 5h3.5A1.75 1.75 0 0 0 12 3.25z" fill="#933"></path></g></svg>', '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24"><g fill="none"><path d="M16.5 12a5.5 5.5 0 1 1 0 11a5.5 5.5 0 0 1 0-11zM12 1.75a3.25 3.25 0 0 1 3.245 3.066L15.25 5h5.25a.75.75 0 0 1 .102 1.493L20.5 6.5h-.796l-.5 5.087c-.46-.21-.95-.37-1.46-.468l.453-4.619H5.802l1.267 12.872a1.25 1.25 0 0 0 1.117 1.122l.127.006h2.42c.286.551.65 1.056 1.076 1.5H8.313a2.75 2.75 0 0 1-2.714-2.307l-.023-.174L4.295 6.5H3.5a.75.75 0 0 1-.743-.648L2.75 5.75a.75.75 0 0 1 .648-.743L3.5 5h5.25A3.25 3.25 0 0 1 12 1.75zm1.716 12.839l-.07.057l-.057.07a.5.5 0 0 0 0 .568l.057.07l2.147 2.146l-2.147 2.146l-.057.07a.5.5 0 0 0 0 .568l.057.07l.07.057a.5.5 0 0 0 .568 0l.07-.057l2.146-2.147l2.146 2.147l.07.057a.5.5 0 0 0 .568 0l.07-.057l.057-.07a.5.5 0 0 0 0-.568l-.057-.07l-2.147-2.146l2.147-2.146l.057-.07a.5.5 0 0 0 0-.568l-.057-.07l-.07-.057a.5.5 0 0 0-.568 0l-.07.057l-2.146 2.147l-2.146-2.147l-.07-.057a.5.5 0 0 0-.492-.044l-.076.044zM12 3.25a1.75 1.75 0 0 0-1.744 1.606L10.25 5h3.5A1.75 1.75 0 0 0 12 3.25z" fill="#933"></path></g></svg>',
boundsPadding: [-2, 0, 0, 10], boundsPadding: [-2, 0, 0, 10],
cursor: 'pointer', cursor: 'pointer',
}) })
checkBoxGroup.add(deleteSymbol) checkBoxGroup.add(deleteSymbol)
deleteSymbol.addEventListener('click', event => { deleteSymbol.addEventListener('click', event => {
event.stopPropagation() event.stopPropagation()
// console.log(dialog, '----') // console.log(dialog, '----')
dialog.warning({ dialog.warning({
title: '警告', title: '警告',
content: '确认删除当前事件?', content: '确认删除当前事件?',
positiveText: '确定', positiveText: '确定',
negativeText: '取消', negativeText: '取消',
onPositiveClick: () => {}, onPositiveClick: () => {},
onNegativeClick: () => {}, onNegativeClick: () => {},
}) })
console.log(deleteSymbol, 'deleteSymbol') console.log(deleteSymbol, 'deleteSymbol')
}) })
const days = new Text({ const days = new Text({
text: `${startDate.toLocaleDateString()} ~ ${endDate.toLocaleDateString()}`, text: `${startDate.toLocaleDateString()} ~ ${endDate.toLocaleDateString()}`,
fontSize: 13, fontSize: 13,
fontFamily: 'sans-serif', fontFamily: 'sans-serif',
fill: textColor, fill: textColor,
boundsPadding: [10, 0, 0, 0], boundsPadding: [10, 0, 0, 0],
}) })
container.add(days) container.add(days)
const rect = new Rect({ const rect = new Rect({
width: width, width: width,
height: 7, height: 7,
fill: { fill: {
gradient: 'linear', gradient: 'linear',
x0: 0, x0: 0,
y0: 0, y0: 0,
x1: 1, x1: 1,
y1: 0, y1: 0,
stops: [ stops: [
{ {
offset: 0, offset: 0,
color: textColor, color: textColor,
}, },
{ {
offset: 1, offset: 1,
color: textColorWithOp, color: textColorWithOp,
}, },
], ],
}, },
boundsPadding: [10, 0, 0, 0], boundsPadding: [10, 0, 0, 0],
}) })
container.add(rect) container.add(rect)
return { return {
rootContainer: container, rootContainer: container,
} }
}, },
hoverBarStyle: { hoverBarStyle: {
cornerRadius: 2, cornerRadius: 2,
barOverlayColor: textColorWithOp, barOverlayColor: textColorWithOp,
}, },
selectedBarStyle: { selectedBarStyle: {
// cornerRadius: 2, // cornerRadius: 2,
borderColor: textColorWithOp, borderColor: textColorWithOp,
borderLineWidth: 2, borderLineWidth: 2,
}, },
} }
return taskBar return taskBar
} }
function renderGroup(opt: IGroupGraphicAttribute) { function renderGroup(opt: IGroupGraphicAttribute) {
return new Group(opt) return new Group(opt)
} }
function renderText(opt: ITextGraphicAttribute) { function renderText(opt: ITextGraphicAttribute) {
return new Text(opt) return new Text(opt)
} }
function renderImage(opt: IImageGraphicAttribute) { function renderImage(opt: IImageGraphicAttribute) {
return new Image(opt) return new Image(opt)
} }
function changeTimeScales(scale: TYPES.ITimelineScale['unit']) { function changeTimeScales(scale: TYPES.ITimelineScale['unit']) {
const scales = getTimeScales(scale) const scales = getTimeScales(scale)
ganttInstance && ganttInstance.updateScales(scales) ganttInstance && ganttInstance.updateScales(scales)
} }
function getTimeScales( function getTimeScales(
scale: TYPES.ITimelineScale['unit'] scale: TYPES.ITimelineScale['unit']
): TYPES.ITimelineScale[] { ): TYPES.ITimelineScale[] {
return [ return [
{ {
unit: scale, unit: scale,
step: 1, step: 1,
customLayout: args => { customLayout: args => {
const { width, height, startDate } = args const { width, height, startDate } = args
const container = new Group({ const container = new Group({
width, width,
height, height,
display: 'flex', display: 'flex',
flexDirection: 'row', flexDirection: 'row',
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
flexWrap: 'nowrap', flexWrap: 'nowrap',
}) })
const day = new Text({ const day = new Text({
text: text:
scale === 'day' scale === 'day'
? startDate.toLocaleDateString() ? startDate.toLocaleDateString()
: startDate.toLocaleTimeString(), : startDate.toLocaleTimeString(),
fontSize: 14, fontSize: 14,
fontWeight: 'bold', fontWeight: 'bold',
fontFamily: 'sans-serif', fontFamily: 'sans-serif',
fill: textColor, fill: textColor,
textAlign: 'center', textAlign: 'center',
maxLineWidth: width, maxLineWidth: width,
// boundsPadding: [25, 12, 10, 12], // boundsPadding: [25, 12, 10, 12],
}) })
container.add(day) container.add(day)
return { return {
rootContainer: container, rootContainer: container,
} }
}, },
}, },
] ]
} }
return { renderMainTask, changeTimeScales } return { renderMainTask, changeTimeScales }
} }
export default useGanttEdit export default useGanttEdit

58
src/views/Gantt/components/GanttEdit/index.jsx Normal file → Executable file
View File

@ -1,29 +1,29 @@
import useGanttEdit from './hooks/ganttEdit' import useGanttEdit from './hooks/ganttEdit'
import { useRouter, useRoute } from 'vue-router' import { useRouter, useRoute } from 'vue-router'
export default defineComponent({ export default defineComponent({
props: { props: {
scale: { scale: {
type: String, type: String,
default: 'day', default: 'day',
}, },
}, },
setup(props) { setup(props) {
const router = useRouter() const router = useRouter()
const route = useRoute() const route = useRoute()
const { renderMainTask, changeTimeScales } = useGanttEdit({ route, router }) const { renderMainTask, changeTimeScales } = useGanttEdit({ route, router })
onMounted(() => { onMounted(() => {
nextTick(() => { nextTick(() => {
renderMainTask(document.querySelector('#tableContainer')) renderMainTask(document.querySelector('#tableContainer'))
}) })
}) })
watch( watch(
() => props.scale, () => props.scale,
val => { val => {
changeTimeScales(val) changeTimeScales(val)
} }
) )
return () => <div id="tableContainer" class="bg-[#1c202c] w-h-full"></div> return () => <div id="tableContainer" class="bg-[#1c202c] w-h-full"></div>
}, },
}) })

123
src/views/Gantt/components/MainGantt/index.jsx Normal file → Executable file
View File

@ -1,48 +1,75 @@
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { import {
NDatePicker, NDatePicker,
NRadioButton, NRadioButton,
NRadioGroup, NRadioGroup,
NButton, NButton,
NSelect, NSelect,
} from 'naive-ui' } from 'naive-ui'
import GanttCom from '../Gantt' import GanttCom from '../Gantt'
import { getDDList } from '@/api/Gantt/gantt'
export default defineComponent({ import { onBeforeMount } from 'vue'
setup() {
const range = ref() export default defineComponent({
const value = ref('day') setup() {
const type = ref() const range = ref([new Date('2000-01-01').getTime(), Date.now()])
const value = ref('year')
const router = useRouter() const types = ref([])
const editEvent = () => {
console.log(router) const router = useRouter()
router.push('/gantt/mainEdit') const editEvent = () => {
} console.log(router)
return () => ( router.push('/gantt/mainEdit')
<> }
<div class="flex gap-2">
<NDatePicker onBeforeMount(async () => {
v-model:value={range.value} await getDDOptions()
type="daterange" })
clearable
disabled const ddOptions = ref([])
/> async function getDDOptions() {
<NRadioGroup v-model:value={value.value} name="radiobuttongroup"> const { code, data } = await getDDList()
<NRadioButton value="hour" label="日" /> if (code === 200) {
<NRadioButton value="day" label="月" /> ddOptions.value = data.list
</NRadioGroup> // types.value = ddOptions.value.map(item => item.id)
<NSelect }
v-model:value={type.value} }
mode="multiple"
style="width: 200px" const ganttRef = ref(null)
></NSelect> function searchGanttData() {
{/* <NButton class="ml-auto" type="primary" onClick={editEvent}> ganttRef.value.refresh = true
编辑事件 }
</NButton> */}
</div> return () => (
<GanttCom scale={value.value} /> <>
</> <div class="flex gap-2">
) <NDatePicker v-model:value={range.value} type="daterange" clearable />
}, {/* <NRadioGroup v-model:value={value.value} name="radiobuttongroup">
}) <NRadioButton value="hour" label="日" />
<NRadioButton value="day" label="月" />
</NRadioGroup> */}
<NSelect
v-model:value={types.value}
multiple
options={ddOptions.value}
label-field="name"
clearable
value-field="id"
></NSelect>
{/* <NButton class="ml-auto" type="primary" onClick={editEvent}>
编辑事件
</NButton> */}
<NButton class="ml-auto" type="primary" onClick={searchGanttData}>
搜索
</NButton>
</div>
<GanttCom
ref={ganttRef}
scale={value.value}
dateRange={range.value}
types={types.value}
/>
</>
)
},
})

View File

@ -0,0 +1,48 @@
import { useRouter } from 'vue-router'
import {
NDatePicker,
NRadioButton,
NRadioGroup,
NButton,
NSelect,
} from 'naive-ui'
import GanttCom from '../Gantt'
export default defineComponent({
setup() {
const range = ref()
const value = ref('day')
const type = ref()
const router = useRouter()
const editEvent = () => {
console.log(router)
router.push('/gantt/mainEdit')
}
return () => (
<>
<div class="flex gap-2">
<NDatePicker
v-model:value={range.value}
type="daterange"
clearable
disabled
/>
<NRadioGroup v-model:value={value.value} name="radiobuttongroup">
<NRadioButton value="hour" label="日" />
<NRadioButton value="day" label="月" />
</NRadioGroup>
<NSelect
v-model:value={type.value}
mode="multiple"
style="width: 200px"
></NSelect>
{/* <NButton class="ml-auto" type="primary" onClick={editEvent}>
编辑事件
</NButton> */}
</div>
<GanttCom scale={value.value} />
</>
)
},
})

View File

@ -0,0 +1,83 @@
import { useRouter } from 'vue-router'
import {
NDatePicker,
NRadioButton,
NRadioGroup,
NButton,
NSelect,
} from 'naive-ui'
import GanttCom from '../Gantt'
import {getDDList}from '@/api/Gantt/gantt'
import { onBeforeMount } from 'vue'
export default defineComponent({
setup() {
const range = ref([new Date('2000-01-01').getTime(), Date.now()])
const value = ref('year')
const types = ref([])
const router = useRouter()
const editEvent = () => {
console.log(router)
router.push('/gantt/mainEdit')
}
onBeforeMount(async ()=>{
await getDDOptions()
})
const ddOptions = ref([])
async function getDDOptions (){
const {code,data} = await getDDList()
if(code === 200) {
ddOptions.value = data.list
types.value = ddOptions.value.map(item=>item.id)
}
}
const ganttRef = ref(null)
function searchGanttData(){
// console.log(ganttRef);
renderMainTask(document.querySelector('#tableContainer'),{
ids: types.value,
startTime: new Date(range.value[0]).toISOString(),
endTime: new Date(range.value[1]).toISOString()
})
}
return () => (
<>
<div class="flex gap-2">
<NDatePicker
v-model:value={range.value}
type="daterange"
clearable
/>
{/* <NRadioGroup v-model:value={value.value} name="radiobuttongroup">
<NRadioButton value="hour" label="日" />
<NRadioButton value="day" label="月" />
</NRadioGroup> */}
<NSelect
v-model:value={types.value}
multiple
options={ddOptions.value}
label-field='name'
clearable
value-field='id'
></NSelect>
{/* <NButton class="ml-auto" type="primary" onClick={editEvent}>
编辑事件
</NButton> */}
<NButton class="ml-auto" type="primary" onClick={searchGanttData}>
搜索
</NButton>
</div>
<GanttCom ref={ganttRef} scale={value.value} dateRange={range.value} types={types.value}/>
</>
)
},
})

90
src/views/Gantt/components/MainGanttEdit/index.jsx Normal file → Executable file
View File

@ -1,45 +1,45 @@
import { NDatePicker, NRadioButton, NRadioGroup, NButton } from 'naive-ui' import { NDatePicker, NRadioButton, NRadioGroup, NButton } from 'naive-ui'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import GanttCom from '../GanttEdit' import GanttCom from '../GanttEdit'
export default defineComponent({ export default defineComponent({
setup() { setup() {
const range = ref() const range = ref()
const value = ref('day') const value = ref('day')
const addEvent = () => { const addEvent = () => {
console.log('tianjia --- event') console.log('tianjia --- event')
} }
const addTask = () => { const addTask = () => {
console.log('tianjiarenwu') console.log('tianjiarenwu')
} }
const router = useRouter() const router = useRouter()
const ok = () => { const ok = () => {
router.push('/gantt') router.push('/gantt')
} }
return () => ( return () => (
<> <>
<div class="flex gap-2"> <div class="flex gap-2">
<NDatePicker v-model:value={range.value} type="daterange" clearable /> <NDatePicker v-model:value={range.value} type="daterange" clearable />
<NRadioGroup v-model:value={value.value} name="radiobuttongroup"> <NRadioGroup v-model:value={value.value} name="radiobuttongroup">
<NRadioButton value="hour" label="日" /> <NRadioButton value="hour" label="日" />
<NRadioButton value="day" label="月" /> <NRadioButton value="day" label="月" />
</NRadioGroup> </NRadioGroup>
<div class="ml-auto flex gap-2"> <div class="ml-auto flex gap-2">
<NButton type="primary" onClick={addEvent}> <NButton type="primary" onClick={addEvent}>
添加事件 添加事件
</NButton> </NButton>
{/* <NButton type="primary" onClick={addTask}> {/* <NButton type="primary" onClick={addTask}>
添加任务 添加任务
</NButton> */} </NButton> */}
<NButton type="primary" onClick={ok}> <NButton type="primary" onClick={ok}>
完成编辑 完成编辑
</NButton> </NButton>
</div> </div>
</div> </div>
<GanttCom scale={value.value} /> <GanttCom scale={value.value} />
</> </>
) )
}, },
}) })

66
src/views/Gantt/components/SubGantt/index.jsx Normal file → Executable file
View File

@ -1,33 +1,33 @@
import { NButton } from 'naive-ui' import { NButton } from 'naive-ui'
import GanttCom from '../Gantt' import GanttCom from '../Gantt'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
export default defineComponent({ export default defineComponent({
setup() { setup() {
const router = useRouter() const router = useRouter()
const value = ref('day') const value = ref('day')
const showView = () => { const showView = () => {
// getCheckboxState // getCheckboxState
const checked = ganttInstance.taskListTableInstance?.getCheckboxState() const checked = ganttInstance.taskListTableInstance?.getCheckboxState()
console.log(checked) console.log(checked)
} }
return () => ( return () => (
<> <>
<div class="flex gap-2"> <div class="flex gap-2">
<NButton onClick={showView}>态势展示</NButton> <NButton onClick={showView}>态势展示</NButton>
<NButton <NButton
onClick={() => { onClick={() => {
router.go(-1) router.go(-1)
}} }}
> >
返回 返回
</NButton> </NButton>
</div> </div>
<GanttCom scale={value.value} /> <GanttCom scale={value.value} />
</> </>
) )
}, },
}) })

66
src/views/Gantt/components/SubGanttEdit/index.jsx Normal file → Executable file
View File

@ -1,33 +1,33 @@
import { NButton } from 'naive-ui' import { NButton } from 'naive-ui'
import GanttCom from '../GanttEdit' import GanttCom from '../GanttEdit'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
export default defineComponent({ export default defineComponent({
setup() { setup() {
const router = useRouter() const router = useRouter()
const value = ref('day') const value = ref('day')
const showView = () => { const showView = () => {
// getCheckboxState // getCheckboxState
const checked = ganttInstance.taskListTableInstance?.getCheckboxState() const checked = ganttInstance.taskListTableInstance?.getCheckboxState()
console.log(checked) console.log(checked)
} }
return () => ( return () => (
<> <>
<div class="flex gap-2"> <div class="flex gap-2">
{/* <NButton onClick={showView}>态势展示</NButton> */} {/* <NButton onClick={showView}>态势展示</NButton> */}
<NButton <NButton
onClick={() => { onClick={() => {
router.go(-1) router.go(-1)
}} }}
> >
返回 返回
</NButton> </NButton>
</div> </div>
<GanttCom scale={value.value} /> <GanttCom scale={value.value} />
</> </>
) )
}, },
}) })

View File

@ -1,15 +1,15 @@
const showNewTask = ref(false) const showNewTask = ref(false)
const curTaskData = ref({ const curTaskData = ref({
name: '', name: '',
description: '', description: '',
checkedEvent: [], checkedEvent: [],
}) })
const useTask = () => { const useTask = () => {
return { return {
showNewTask, showNewTask,
curTaskData, curTaskData,
} }
} }
export default useTask export default useTask

View File

@ -1,118 +1,118 @@
import { NForm, NFormItem, NInput, NDataTable, NButton } from 'naive-ui' import { NForm, NFormItem, NInput, NDataTable, NButton } from 'naive-ui'
import ModalCom from '@/components/Modal/index.vue' import ModalCom from '@/components/Modal/index.vue'
import { getMainGantt } from '@/api/gantt' import { getMainGantt } from '@/api/gantt'
import useTask from './hooks' import useTask from './hooks'
export default defineComponent({ export default defineComponent({
// props: { // props: {
// show: { // show: {
// type: Boolean, // type: Boolean,
// default: false, // default: false,
// }, // },
// }, // },
setup() { setup() {
// const checkedRowKeys = ref([]) // const checkedRowKeys = ref([])
// watch(checkedRowKeys, newval => { // watch(checkedRowKeys, newval => {
// console.log(newval, '-----') // console.log(newval, '-----')
// }) // })
const columns = [ const columns = [
{ type: 'selection' }, { type: 'selection' },
{ {
title: '事件名称', title: '事件名称',
key: 'name', key: 'name',
width: 220, width: 220,
// render: row => { // render: row => {
// return ( // return (
// <div class="inline-flex items-center gap-2"> // <div class="inline-flex items-center gap-2">
// <NIcon> // <NIcon>
// <HelpCircleOutline /> // <HelpCircleOutline />
// </NIcon> // </NIcon>
// {row.name} // {row.name}
// </div> // </div>
// ) // )
// }, // },
}, },
{ {
title: '开始时间', title: '开始时间',
key: 'start', key: 'start',
}, },
{ {
title: '结束时间', title: '结束时间',
key: 'end', key: 'end',
}, },
// { // {
// title: '', // title: '',
// key: 'type', // key: 'type',
// }, // },
{ {
title: '图片', title: '图片',
key: 'avatar', key: 'avatar',
render(row) { render(row) {
if (row.avatar) { if (row.avatar) {
return <img src={row.avatar} width="50" alt="" /> return <img src={row.avatar} width="50" alt="" />
} else { } else {
return <span>-</span> return <span>-</span>
} }
}, },
}, },
] ]
const tableData = ref([]) const tableData = ref([])
onMounted(async () => { onMounted(async () => {
const res = await getMainGantt() const res = await getMainGantt()
tableData.value = res tableData.value = res
}) })
const { showNewTask, curTaskData } = useTask() const { showNewTask, curTaskData } = useTask()
const close = () => { const close = () => {
showNewTask.value = false showNewTask.value = false
console.log(curTaskData, '---') console.log(curTaskData, '---')
} }
// watch( // watch(
// showNewTask, // showNewTask,
// show => { // show => {
// if (show) { // if (show) {
// taskData.value = { // taskData.value = {
// name: '', // name: '',
// description: '', // description: '',
// checkedEvent: [], // checkedEvent: [],
// } // }
// } // }
// }, // },
// { immediate: true } // { immediate: true }
// ) // )
return () => ( return () => (
<ModalCom v-model:show={showNewTask.value} title="新增任务"> <ModalCom v-model:show={showNewTask.value} title="新增任务">
<NForm <NForm
class="w-[900px]" class="w-[900px]"
model={curTaskData} model={curTaskData}
label-placement="left" label-placement="left"
label-width="auto" label-width="auto"
> >
<NFormItem label="任务名称" path="name"> <NFormItem label="任务名称" path="name">
<NInput v-model:value={curTaskData.value.name} /> <NInput v-model:value={curTaskData.value.name} />
</NFormItem> </NFormItem>
<NFormItem label="任务描述" path="description"> <NFormItem label="任务描述" path="description">
<NInput v-model:value={curTaskData.value.description} /> <NInput v-model:value={curTaskData.value.description} />
</NFormItem> </NFormItem>
<NFormItem label="事件" path="event"> <NFormItem label="事件" path="event">
<NDataTable <NDataTable
v-model:checked-row-keys={curTaskData.value.checkedEvent} v-model:checked-row-keys={curTaskData.value.checkedEvent}
class="h-[400px]" class="h-[400px]"
flex-height flex-height
columns={columns} columns={columns}
data={tableData.value} data={tableData.value}
row-key={row => row.name} row-key={row => row.name}
/> />
</NFormItem> </NFormItem>
</NForm> </NForm>
<div class="flex justify-end gap-2"> <div class="flex justify-end gap-2">
<NButton onClick={close}>取消</NButton> <NButton onClick={close}>取消</NButton>
<NButton type="primary">确认</NButton> <NButton type="primary">确认</NButton>
</div> </div>
</ModalCom> </ModalCom>
) )
}, },
}) })

344
src/views/Gantt/components/TaskList/index.jsx Normal file → Executable file
View File

@ -1,172 +1,172 @@
import { NDataTable, NIcon, NButton, NTag } from 'naive-ui' import { NDataTable, NIcon, NButton, NTag } from 'naive-ui'
import { useTree } from '@/utils/tree' import { useTree } from '@/utils/tree'
import { getTask } from '@/api/gantt' import { getTask } from '@/api/gantt'
import { import {
HelpCircleOutline, HelpCircleOutline,
CreateOutline, CreateOutline,
TrashBinOutline, TrashBinOutline,
AddCircleOutline, AddCircleOutline,
EnterOutline, EnterOutline,
} from '@vicons/ionicons5' } from '@vicons/ionicons5'
import useTask from './components/NewTask/hooks' import useTask from './components/NewTask/hooks'
export default defineComponent({ export default defineComponent({
setup() { setup() {
const dict = window.settings.gantt const dict = window.settings.gantt
const columns = [ const columns = [
{ {
title: '任务名称/事件名称', title: '任务名称/事件名称',
key: 'name', key: 'name',
width: 'auto', width: 'auto',
render: row => { render: row => {
return ( return (
<div class="inline-flex items-center gap-2"> <div class="inline-flex items-center gap-2">
{row.type && ( {row.type && (
<NTag <NTag
size="small" size="small"
round round
bordered={false} bordered={false}
type={dict[row.type]?.color} type={dict[row.type]?.color}
> >
{dict[row.type].label} {dict[row.type].label}
</NTag> </NTag>
)} )}
{row.name} {row.name}
</div> </div>
) )
}, },
}, },
{ {
title: '开始时间', title: '开始时间',
key: 'start', key: 'start',
}, },
{ {
title: '结束时间', title: '结束时间',
key: 'end', key: 'end',
}, },
// { // {
// title: '', // title: '',
// key: 'type', // key: 'type',
// render(row) { // render(row) {
// return ( // return (
// row.type && ( // row.type && (
// <NTag // <NTag
// size="small" // size="small"
// round // round
// bordered={false} // bordered={false}
// type={dict[row.type]?.color} // type={dict[row.type]?.color}
// > // >
// {dict[row.type].label} // {dict[row.type].label}
// </NTag> // </NTag>
// ) // )
// ) // )
// }, // },
// }, // },
{ {
title: '图片', title: '图片',
key: 'avatar', key: 'avatar',
render(row) { render(row) {
if (row.avatar) { if (row.avatar) {
return <img src={row.avatar} width="50" alt="" /> return <img src={row.avatar} width="50" alt="" />
} else { } else {
return <span>-</span> return <span>-</span>
} }
}, },
}, },
{ {
title: '操作', title: '操作',
key: 'action', key: 'action',
render(row) { render(row) {
return ( return (
<div class="flex justify-end"> <div class="flex justify-end">
{row.type === 'task' ? ( {row.type === 'task' ? (
<> <>
<NButton <NButton
type="primary" type="primary"
size="small" size="small"
quaternary quaternary
onClick={() => editTask(row)} onClick={() => editTask(row)}
> >
<NIcon> <NIcon>
<EnterOutline /> <EnterOutline />
</NIcon> </NIcon>
进入任务 进入任务
</NButton> </NButton>
<NButton <NButton
type="primary" type="primary"
size="small" size="small"
quaternary quaternary
onClick={() => editTask(row)} onClick={() => editTask(row)}
> >
<NIcon> <NIcon>
<CreateOutline /> <CreateOutline />
</NIcon> </NIcon>
编辑 编辑
</NButton> </NButton>
</> </>
) : null} ) : null}
{/* {!row.avatar ? ( {/* {!row.avatar ? (
<NButton <NButton
type="success" type="success"
size="small" size="small"
quaternary quaternary
onClick={() => handleEdit(row)} onClick={() => handleEdit(row)}
> >
<NIcon> <NIcon>
<AddCircleOutline /> <AddCircleOutline />
</NIcon> </NIcon>
</NButton> </NButton>
) : ( ) : (
<></> <></>
)} */} )} */}
{/* <NButton {/* <NButton
type="error" type="error"
size="small" size="small"
quaternary quaternary
onClick={() => handleEdit(row)} onClick={() => handleEdit(row)}
> >
<NIcon> <NIcon>
<TrashBinOutline /> <TrashBinOutline />
</NIcon> </NIcon>
</NButton> */} </NButton> */}
</div> </div>
) )
}, },
}, },
] ]
const tableData = ref([]) const tableData = ref([])
const { getAllKeys } = useTree() const { getAllKeys } = useTree()
const expandedRowKeys = ref([]) const expandedRowKeys = ref([])
onMounted(async () => { onMounted(async () => {
await getTaskList() await getTaskList()
}) })
async function getTaskList() { async function getTaskList() {
const res = await getTask() const res = await getTask()
tableData.value = res tableData.value = res
expandedRowKeys.value = getAllKeys(tableData.value, 'name') expandedRowKeys.value = getAllKeys(tableData.value, 'name')
} }
const { showNewTask, curTaskData } = useTask() const { showNewTask, curTaskData } = useTask()
function editTask(row) { function editTask(row) {
showNewTask.value = true showNewTask.value = true
curTaskData.value = { curTaskData.value = {
...row, ...row,
checkedEvent: getAllKeys(row.children, 'name'), checkedEvent: getAllKeys(row.children, 'name'),
} }
} }
return () => ( return () => (
<NDataTable <NDataTable
class="h-full" class="h-full"
flex-height flex-height
indent={30} indent={30}
v-model:expanded-row-keys={expandedRowKeys.value} v-model:expanded-row-keys={expandedRowKeys.value}
columns={columns} columns={columns}
data={tableData.value} data={tableData.value}
row-key={row => row.name} row-key={row => row.name}
/> />
) )
}, },
}) })

1062
src/views/Gantt/index copy.vue Normal file → Executable file

File diff suppressed because it is too large Load Diff

333
src/views/Gantt/index.jsx Normal file → Executable file
View File

@ -1,154 +1,179 @@
import { RouterView } from 'vue-router' import { RouterView } from 'vue-router'
import { import {
NDatePicker, NDatePicker,
NButton, NButton,
NFloatButton, NFloatButton,
NIcon, NIcon,
NDrawer, NDrawer,
NDrawerContent, NDrawerContent,
NTabs, NTabs,
NTabPane, NTabPane,
NSelect, NSelect,
} from 'naive-ui' } from 'naive-ui'
import { ArrowForward } from '@vicons/ionicons5' import { ArrowForward } from '@vicons/ionicons5'
import HeaderCom from '../Content/components/Header/index.vue' import HeaderCom from '../Content/components/Header/index.vue'
import TaskList from './components/TaskList' import TaskList from './components/TaskList'
import EventList from './components/EventList' import EventList from './components/EventList'
import NewTask from './components/TaskList/components/NewTask' import NewTask from './components/TaskList/components/NewTask'
import useTask from './components/TaskList/components/NewTask/hooks' import useTask from './components/TaskList/components/NewTask/hooks'
import { useEvent } from './components/EventList/hooks' import { useEvent } from './components/EventList/hooks'
export default defineComponent({ import { getSimpList, getSimpTreeList } from '@/api/Gantt'
setup() { import { onBeforeMount, nextTick } from 'vue'
const show = ref(false) export default defineComponent({
const range = ref() setup() {
const show = ref(false)
const { showNewTask } = useTask()
const { showNewTask } = useTask()
const addNewTask = () => {
showNewTask.value = true const addNewTask = () => {
} showNewTask.value = true
}
const ddList = Array.from({ length: 8 }, (_, i) => ({ async function getSimpListData() {
label: `DD-${i + 1}`, const res = await getSimpList()
value: `DD-${i + 1}`, ddList.value = res.data.list
})) targetId.value = res.data.list[0].id
}
const dd = ref(`DD-1`)
// const ddList = Array.from({ length: 8 }, (_, i) => ({
const paneClass = `border-1 h-full border-l-0 border-[var(--n-tab-border-color)] !p-2` // label: `DD-${i + 1}`,
// value: `DD-${i + 1}`,
const { showMainEvent, mainEventData } = useEvent() // }))
const addNewMainEvent = () => { const ddList = ref([])
showMainEvent.value = true
mainEventData.value = {} const paneClass = `border-1 h-full border-l-0 border-[var(--n-tab-border-color)] !p-2`
}
const {
return () => ( showMainEvent,
<div class="flex flex-col bg-[#1c202c] w-h-full"> mainEventData,
<div class="relative h-[60px]"> targetId,
<HeaderCom /> range,
</div> searchTreeList,
<div class="z-30 flex flex-1 flex-col gap-4 p-5"> tableData,
<RouterView /> } = useEvent()
</div> const addNewMainEvent = async () => {
<NFloatButton showMainEvent.value = true
class="z-40" // const res = await addNewMainEvent({ })
left={-10} mainEventData.value = {}
bottom={document.body.clientHeight / 2} }
shape="square"
onClick={() => { onBeforeMount(() => {
show.value = true nextTick(async () => {
}} await getSimpListData()
> await searchTreeList()
<NIcon> })
<ArrowForward /> })
</NIcon>
</NFloatButton> return () => (
<NDrawer <div class="flex flex-col bg-[#1c202c] w-h-full">
class="h-[100vh] bg-[#1c202cee]" <div class="relative h-[60px]">
v-model:show={show.value} <HeaderCom />
width={document.body.clientWidth - 200} </div>
placement="left" <div class="z-30 flex flex-1 flex-col gap-4 p-5">
display-directive={'show'} <RouterView />
> </div>
<NDrawerContent title="事件管理" closable> <NFloatButton
<div class="flex h-full flex-col gap-2"> class="z-40"
<NTabs left={-10}
class="h-full" bottom={document.body.clientHeight / 2}
pane-wrapper-class="h-full" shape="square"
type="card" onClick={() => {
animated show.value = true
placement="left" }}
defaultValue={'事件列表'} >
> <NIcon>
{/* <NTabPane <ArrowForward />
class={paneClass} </NIcon>
name="任务列表" </NFloatButton>
tab="任务列表" <NDrawer
display-directive={'show'} class="h-[100vh] bg-[#1c202cee]"
> v-model:show={show.value}
<div class="flex h-full flex-col gap-2"> width={document.body.clientWidth - 200}
<div class="flex justify-end gap-2"> placement="left"
<NDatePicker display-directive={'show'}
v-model:value={range.value} >
type="daterange" <NDrawerContent title="事件管理" closable>
clearable <div class="flex h-full flex-col gap-2">
/> {/* <NTabs
<NButton type="primary" onClick={addNewTask}> class="h-full"
添加任务 pane-wrapper-class="h-full"
</NButton> type="card"
</div> animated
<div class="flex-1"> placement="left"
<TaskList /> defaultValue={'事件列表'}
</div> >
</div> <NTabPane
</NTabPane> */} class={paneClass}
<NTabPane name="任务列表"
class={paneClass} tab="任务列表"
name="事件列表" display-directive={'show'}
tab="事件列表" >
display-directive={'show'} <div class="flex h-full flex-col gap-2">
> <div class="flex justify-end gap-2">
<div class="flex h-full flex-col gap-2"> <NDatePicker
<div class="flex justify-end gap-2 "> v-model:value={range.value}
<NSelect type="daterange"
class="w-[200px]" clearable
v-model:value={dd.value} />
options={ddList} <NButton type="primary" onClick={addNewTask}>
></NSelect> 添加任务
<NDatePicker </NButton>
v-model:value={range.value} </div>
type="daterange" <div class="flex-1">
clearable <TaskList />
/> </div>
<NButton type="primary" onClick={addNewMainEvent}> </div>
添加事件 </NTabPane>
</NButton> <NTabPane
</div> class={paneClass}
<div class="flex-1"> name="事件列表"
<EventList dd={dd.value} /> tab="事件列表"
</div> display-directive={'show'}
</div> > */}
</NTabPane> <div class="flex h-full flex-col gap-2">
</NTabs> <div class="flex justify-end gap-2 ">
{/* <div class="flex justify-end gap-2 "> <NSelect
<NDatePicker class="w-[200px]"
v-model:value={range.value} v-model:value={targetId.value}
type="daterange" options={ddList.value}
clearable label-field="name"
/> value-field="id"
></NSelect>
</div> <NDatePicker
<div class="flex-1"> v-model:value={range.value}
<TaskList /> type="daterange"
</div> */} clearable
</div> />
</NDrawerContent> <NButton type="primary" onClick={searchTreeList}>
</NDrawer> 搜索
</NButton>
<NewTask v-model:show={showNewTask.value} /> <NButton type="primary" onClick={addNewMainEvent}>
</div> 添加事件
) </NButton>
}, </div>
}) <div class="flex-1">
<EventList dd={targetId.value} tableData={tableData.value} />
</div>
</div>
{/* </NTabPane>
</NTabs> */}
{/* <div class="flex justify-end gap-2 ">
<NDatePicker
v-model:value={range.value}
type="daterange"
clearable
/>
</div>
<div class="flex-1">
<TaskList />
</div> */}
</div>
</NDrawerContent>
</NDrawer>
<NewTask v-model:show={showNewTask.value} />
</div>
)
},
})

14
src/views/Gantt/index.vue Normal file → Executable file
View File

@ -1,7 +1,7 @@
<script setup> <script setup>
import TestJsx from './index.jsx' import TestJsx from './index.jsx'
</script> </script>
<template> <template>
<test-jsx /> <test-jsx />
</template> </template>

View File

@ -39,28 +39,19 @@ const { mubiaoMap } = useEntity()
// const { showOrHideDdConfig } = useDaodan() // const { showOrHideDdConfig } = useDaodan()
const { flyTo } = useEarth() const { flyTo } = useEarth()
const latValue = {}
const nodeProps = ({ option }: { option: TreeOption }) => { const nodeProps = ({ option }: { option: TreeOption }) => {
return { return {
onclick: () => { onclick: () => {
if (option.children) { if (option.children) {
return return
} }
setTimeout(() => { const { dataId } = option
if (mubiaoMap.size > 0) { if (mubiaoMap.has(dataId)) {
mubiaoMap.forEach((entity, key) => { const [lon, lat, height] = cartesian32LonLat(
const positions = entity.position._value mubiaoMap.get(dataId).position._value
const [lon, lat, height] = cartesian32LonLat(positions) )
const { flyTo({ lon, lat })
dataId, }
// data: { geom },
} = option
if (checkedKeys.value.includes(dataId as string)) {
flyTo({ lon, lat })
}
})
}
}, 500)
}, },
} }
} }

View File

@ -13,11 +13,6 @@ interface ISatellite {
tle: string tle: string
} }
interface IBaseFilterParam {
treeData: Array<any>
params: Array<string | number>
paramName: string
}
const { filterTreeNodeByField } = useTree() const { filterTreeNodeByField } = useTree()
const satelliteList = ref<ISatellite[]>([]) const satelliteList = ref<ISatellite[]>([])
const checkedKeys = ref<Array<string | number>>([]) const checkedKeys = ref<Array<string | number>>([])
@ -121,6 +116,7 @@ export function useSatellite() {
addSatellites, addSatellites,
showPoint, showPoint,
showPointUnderSat, showPointUnderSat,
flyToSatellite,
} }
} }
@ -147,8 +143,6 @@ function createSatelliteCommunicationPayload(satId: string) {
beam.create() beam.create()
console.log(beam)
if (!satelliteBeamMap.has(satId)) { if (!satelliteBeamMap.has(satId)) {
satelliteBeamMap.set(satId, new Map()) satelliteBeamMap.set(satId, new Map())
} }
@ -198,6 +192,16 @@ async function getSatelliteList() {
const checked = JSON.parse(JSON.stringify(checkedKeys.value)) const checked = JSON.parse(JSON.stringify(checkedKeys.value))
const sateRes = await getSatellite() const sateRes = await getSatellite()
// const sateRes = {
// code: '200',
// data: [
// {
// id: '1',
// name: 'CALSPHERE 1',
// tle: 'CALSPHERE 1\n 1 00900U 64063C 25062.49198022 .00001185 00000+0 12127-2 0 9997\n2 00900 90.2081 61.0113 0023240 278.3857 208.6718 13.75857600 6688',
// },
// ],
// }
const { data, code } = sateRes const { data, code } = sateRes
if (code === '200') { if (code === '200') {
checkedKeys.value = [] checkedKeys.value = []
@ -235,3 +239,17 @@ function getAllNodesToPayload() {
}) })
console.log('satellitePayloadShowMap', satellitePayloadShowMap) console.log('satellitePayloadShowMap', satellitePayloadShowMap)
} }
function flyToSatellite(satId: string) {
const sat = satelliteMap.get(satId)?.entity
if (sat) {
viewer.flyTo(sat, {
duration: 2,
// offset: {
// heading: Cesium.Math.toRadians(option.heading),
// pitch: Cesium.Math.toRadians(option.pitch),
// range: option.range,
// },
})
}
}

View File

@ -4,14 +4,13 @@ import { NButton } from 'naive-ui'
import Tree from '@/components/Tree/index.vue' import Tree from '@/components/Tree/index.vue'
import { useSatellite } from './hooks/satellite' import { useSatellite } from './hooks/satellite'
import { showDetailsSatellite } from './components/SatDetail' import { showDetailsSatellite } from './components/SatDetail'
import { useEarth } from '../Earth/hooks/earth'
import { useEntity } from '@/hooks/entity'
const { const {
satelliteList, satelliteList,
checkedKeys, checkedKeys,
getSatelliteList, getSatelliteList,
addSatellites, addSatellites,
flyToSatellite,
} = useSatellite() } = useSatellite()
onMounted(async () => { onMounted(async () => {
@ -21,32 +20,13 @@ onMounted(async () => {
watch(checkedKeys, val => { watch(checkedKeys, val => {
addSatellites(val) addSatellites(val)
}) })
const { flyTo } = useEarth()
const { satelliteMap } = useEntity()
const nodeProps = ({ option }: { option: TreeOption }) => { const nodeProps = ({ option }: { option: TreeOption }) => {
// console.log(option, 'option')
return { return {
onclick: () => { onclick: () => {
// console.log(satelliteMap, 'satelliteMap') const { id } = option
id && flyToSatellite(id as string)
}, },
} }
// return {
// onclick: () => {
// if (option.children) {
// return
// }
// const {
// dataId,
// data: { geom },
// } = option
// const [lon, lat] = parseWKT(geom).coordinates
// if (checkedKeys.value.includes(dataId as string)) {
// flyTo({ lon, lat })
// }
// },
// },
} }
const renderSuffix = ({ option }: { option: TreeOption }) => { const renderSuffix = ({ option }: { option: TreeOption }) => {
// console.log(option) // console.log(option)