雨辰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'] = {
apis: '/taishiView',
textConfigs: [],
baseMBDict: {
base: {
icon: './images/icons/base/党政首脑机关.png',
color: '#ea7354',
model: './models/基地.glb',
},
airport: {
icon: './images/icons/base/军用机场.png',
color: '#80b1d3',
model: './models/机场.glb',
},
port: {
icon: './images/icons/base/军用港口.png',
color: '#fcee82',
model: './models/港口.glb',
},
station: {
icon: './images/icons/base/民用机场.png',
color: '#8dd3c7',
model: './models/雷达.glb',
},
},
mbCountryDict: {
美国: '#fff',
中国: '#d00',
日本: '#dd0',
韩国: '#00d',
},
mbDict: {
: {
icon: './images/icons/10-7600-0-侦察机.svg',
color: '#d00',
model: './models/IDF.glb',
payload: 'airplaneConic',
},
: {
icon: './images/icons/10-5900-0-航空母舰.svg',
color: '#ff0',
model: './models/驱逐舰2.glb',
payload: 'radar',
},
: {
icon: './images/icons/10-6100-0-驱逐舰.svg',
color: '#dd0',
model: './models/驱逐舰2.glb',
payload: 'radar',
},
},
map: {
1: {
url: 'https://rt2.map.gtimg.com/tile?z={z}&x={x}&y={reverseY}&styleid=4&scene=0&version=347',
tilingScheme: 'WebMercatorTilingScheme',
image: './images/map/1.png',
name: '黑色',
},
2: {
url: 'https://rt2.map.gtimg.com/tile?z={z}&x={x}&y={reverseY}&styleid=5&scene=0&version=347',
tilingScheme: 'WebMercatorTilingScheme',
image: './images/map/2.png',
name: '白色',
},
3: {
url: './resources/map/{z}/{x}/{y}.jpg',
tilingScheme: 'WebMercatorTilingScheme',
image: './images/map/3.jpg',
name: 'Arcgis',
},
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',
tilingScheme: 'GeographicTilingScheme',
image: './images/map/4.png',
name: '卫星影像',
},
},
gantt: {
task: { label: '任务', color: 'error' },
dd: { label: '主体' },
mainEvent: { label: '主事件', color: 'success' },
eventType: { label: '事件分类', color: 'info' },
subEvent: { label: '子事件', color: 'warning' },
twoEvent: { label: '二级分类', color: 'warning' },
},
}
window['settings'] = {
apis: '/taishiView',
textConfigs: [],
baseMBDict: {
base: {
icon: './images/icons/base/党政首脑机关.png',
color: '#ea7354',
model: './models/基地.glb',
},
airport: {
icon: './images/icons/base/军用机场.png',
color: '#80b1d3',
model: './models/机场.glb',
},
port: {
icon: './images/icons/base/军用港口.png',
color: '#fcee82',
model: './models/港口.glb',
},
station: {
icon: './images/icons/base/民用机场.png',
color: '#8dd3c7',
model: './models/雷达.glb',
},
},
mbCountryDict: {
美国: '#fff',
中国: '#d00',
日本: '#dd0',
韩国: '#00d',
},
mbDict: {
: {
icon: './images/icons/10-7600-0-侦察机.svg',
color: '#d00',
model: './models/IDF.glb',
payload: 'airplaneConic',
},
: {
icon: './images/icons/10-5900-0-航空母舰.svg',
color: '#ff0',
model: './models/驱逐舰2.glb',
payload: 'radar',
},
: {
icon: './images/icons/10-6100-0-驱逐舰.svg',
color: '#dd0',
model: './models/驱逐舰2.glb',
payload: 'radar',
},
},
map: {
1: {
url: 'https://rt2.map.gtimg.com/tile?z={z}&x={x}&y={reverseY}&styleid=4&scene=0&version=347',
tilingScheme: 'WebMercatorTilingScheme',
image: './images/map/1.png',
name: '黑色',
},
2: {
url: 'https://rt2.map.gtimg.com/tile?z={z}&x={x}&y={reverseY}&styleid=5&scene=0&version=347',
tilingScheme: 'WebMercatorTilingScheme',
image: './images/map/2.png',
name: '白色',
},
3: {
url: './resources/map/{z}/{x}/{y}.jpg',
tilingScheme: 'WebMercatorTilingScheme',
image: './images/map/3.jpg',
name: 'Arcgis',
},
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',
tilingScheme: 'GeographicTilingScheme',
image: './images/map/4.png',
name: '卫星影像',
},
},
gantt: {
task: { label: '任务', color: 'error' },
dd: { label: '主体' },
1: { label: '主事件', color: 'success' },
2: { label: '事件分类', color: 'info' },
3: { 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 = [
{
id: 0,
name: 'DD',
// start: '2024-11-15',
// end: '2024-11-21',
type: 'eventType',
children: [
{
id: 122,
name: '弹道DD',
start: '2024-11-15',
end: '2024-11-17',
type: "twoEvent",
children: [
{
id: 1,
name: '发射事件',
start: '2024-11-15',
end: '2024-11-17',
type: 'subEvent',
trajData: {},
avatar: '/images/影像.jpg',
},
],
},
{
id: 188,
name: '巡航DD',
start: '2024-11-15',
end: '2024-11-17',
type: "twoEvent",
children: [
{
id: 1,
name: '发射事件',
start: '2024-11-15',
end: '2024-11-17',
type: 'subEvent',
trajData: {},
avatar: '/images/影像.jpg',
},
],
},
],
},
{
id: 300,
name: '飞机',
type: 'eventType',
children: [
{
id: 5,
name: '起飞',
start: '2024-11-18',
end: '2024-11-21',
type: 'subEvent',
avatar: '/images/影像.jpg',
},
],
},
{
name: '舰船',
type: 'eventType',
children: [
{
id: 6,
name: '停留',
start: '2024-11-20',
end: '2024-11-22',
type: 'subEvent',
avatar: '/images/影像.jpg',
},
{
id: 7,
name: '扫描',
start: '2024-11-18',
end: '2024-11-19',
type: 'subEvent',
avatar: '/images/影像.jpg',
},
],
},
{
name: '航J',
type: 'eventType',
children: [
{
id: 8,
name: '航J事件',
start: '2024-11-20',
end: '2024-11-21',
type: 'subEvent',
avatar: '/images/影像.jpg',
},
{
id: 9,
name: '航J事件-2',
start: '2024-11-22',
end: '2024-11-26',
type: 'subEvent',
avatar: '/images/影像.jpg',
},
],
},
{
name: 'xx',
type: 'eventType',
children: [
{
id: 13,
name: 'xx-事件-1',
start: '2024-11-22',
end: '2024-11-25',
type: 'subEvent',
avatar: '/images/影像.jpg',
},
{
id: 14,
name: 'xx-事件-2',
start: '2024-11-27',
end: '2024-11-30',
type: 'subEvent',
avatar: '/images/影像.jpg',
},
],
},
]
const main = [
{
id: 0,
name: 'DD-1',
type: 'dd',
children: [
{
id: 1,
name: '事件1-1',
start: '2024-11-15',
end: '2024-11-17',
type: 'mainEvent',
// children: [
// {
// id: 122,
// name: '发射',
// start: '2024-11-15',
// end: '2024-11-17',
// type: 'DD',
// avatar:
// 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg',
// },
// ],
children: sub,
},
{
id: 2,
name: '事件1-2',
start: '2024-11-17',
end: '2024-11-18',
type: 'mainEvent',
},
{
id: 3,
name: '事件1-3',
start: '2024-11-19',
end: '2024-11-20',
type: 'mainEvent',
},
{
id: 4,
name: '事件1-4',
start: '2024-11-18',
end: '2024-11-19',
type: 'mainEvent',
},
],
},
{
id: 300,
name: 'DD-2',
children: [
{
id: 5,
name: '事件-2-1',
start: '2024-11-18',
end: '2024-11-21',
type: 'mainEvent',
},
],
},
{
name: 'DD-3',
children: [
{
id: 6,
name: '事件-3-1',
start: '2024-11-21',
end: '2024-11-22',
type: 'mainEvent',
},
{
id: 7,
name: '事件-3-2',
start: '2024-11-18',
end: '2024-11-19',
type: 'mainEvent',
},
],
},
{
name: 'DD-4',
children: [
{
id: 8,
name: '事件-4-1',
start: '2024-11-20',
end: '2024-11-21',
type: 'mainEvent',
},
{
id: 9,
name: '事件-4-2',
start: '2024-11-25',
end: '2024-11-26',
type: 'mainEvent',
},
{
id: 10,
name: '事件-4-3',
start: '2024-11-17',
end: '2024-11-18',
type: 'mainEvent',
},
{
id: 11,
name: '事件-4-4',
start: '2024-11-22',
end: '2024-11-25',
type: 'mainEvent',
},
{
id: 12,
name: '事件-4-5',
start: '2024-11-23',
end: '2024-11-24',
type: 'mainEvent',
},
],
},
{
name: 'DD-5',
children: [
{
id: 13,
name: '事件-5-1',
start: '2024-11-22',
end: '2024-11-25',
type: 'mainEvent',
},
{
id: 14,
name: '事件-5-2',
start: '2024-11-27',
end: '2024-11-30',
type: 'mainEvent',
},
{
id: 15,
name: '事件-5-3',
start: '2024-12-10',
end: '2024-12-18',
type: 'mainEvent',
},
],
},
{
name: 'DD-6',
children: [
{
id: 16,
name: '事件-6-1',
start: '2024-11-20',
end: '2024-11-30',
type: 'mainEvent',
},
{
id: 17,
name: '事件-6-2',
start: '2024-12-02',
end: '2024-12-18',
type: 'mainEvent',
},
],
},
{
name: 'DD-7',
children: [
{
id: 18,
name: '事件-7-1',
start: '2024-12-22',
end: '2024-12-28',
type: 'mainEvent',
},
],
},
{
name: 'DD-8',
children: [
{
id: 19,
name: '事件-8-1',
start: '2024-11-25',
end: '2024-11-30',
type: 'mainEvent',
},
{
id: 20,
name: '事件-8-2',
start: '2024-12-01',
end: '2024-12-18',
type: 'mainEvent',
},
],
},
]
const task = [
{
id: 1,
name: '任务-1',
type: 'task',
children: main,
},
]
export function getMainGantt(data = {}) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(main)
}, 200)
})
}
export function getEventListByDDType(ddType) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(main.find(item => item.name === ddType))
}, 200)
})
}
export function getSubGantt(subId) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(sub)
}, 200)
})
}
export function getTask() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(task)
}, 200)
})
}
const sub = [
{
id: 0,
name: 'DD',
// start: '2024-11-15',
// end: '2024-11-21',
type: 'eventType',
children: [
{
id: 122,
name: '弹道DD',
start: '2024-11-15',
end: '2024-11-17',
type: 'twoEvent',
children: [
{
id: 1,
name: '发射事件',
start: '2024-11-15',
end: '2024-11-17',
type: 'subEvent',
trajData: {},
avatar: '/images/影像.jpg',
},
],
},
{
id: 188,
name: '巡航DD',
start: '2024-11-15',
end: '2024-11-17',
type: 'twoEvent',
children: [
{
id: 1,
name: '发射事件',
start: '2024-11-15',
end: '2024-11-17',
type: 'subEvent',
trajData: {},
avatar: '/images/影像.jpg',
},
],
},
],
},
{
id: 300,
name: '飞机',
type: 'eventType',
children: [
{
id: 5,
name: '起飞',
start: '2024-11-18',
end: '2024-11-21',
type: 'subEvent',
avatar: '/images/影像.jpg',
},
],
},
{
name: '舰船',
type: 'eventType',
children: [
{
id: 6,
name: '停留',
start: '2024-11-20',
end: '2024-11-22',
type: 'subEvent',
avatar: '/images/影像.jpg',
},
{
id: 7,
name: '扫描',
start: '2024-11-18',
end: '2024-11-19',
type: 'subEvent',
avatar: '/images/影像.jpg',
},
],
},
{
name: '航J',
type: 'eventType',
children: [
{
id: 8,
name: '航J事件',
start: '2024-11-20',
end: '2024-11-21',
type: 'subEvent',
avatar: '/images/影像.jpg',
},
{
id: 9,
name: '航J事件-2',
start: '2024-11-22',
end: '2024-11-26',
type: 'subEvent',
avatar: '/images/影像.jpg',
},
],
},
{
name: 'xx',
type: 'eventType',
children: [
{
id: 13,
name: 'xx-事件-1',
start: '2024-11-22',
end: '2024-11-25',
type: 'subEvent',
avatar: '/images/影像.jpg',
},
{
id: 14,
name: 'xx-事件-2',
start: '2024-11-27',
end: '2024-11-30',
type: 'subEvent',
avatar: '/images/影像.jpg',
},
],
},
]
const main = [
{
id: 0,
name: 'DD-1',
type: 'dd',
children: [
{
id: 1,
name: '事件1-1',
start: '2024-11-15',
end: '2024-11-17',
type: 'mainEvent',
// children: [
// {
// id: 122,
// name: '发射',
// start: '2024-11-15',
// end: '2024-11-17',
// type: 'DD',
// avatar:
// 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg',
// },
// ],
children: sub,
},
{
id: 2,
name: '事件1-2',
start: '2024-11-17',
end: '2024-11-18',
type: 'mainEvent',
},
{
id: 3,
name: '事件1-3',
start: '2024-11-19',
end: '2024-11-20',
type: 'mainEvent',
},
{
id: 4,
name: '事件1-4',
start: '2024-11-18',
end: '2024-11-19',
type: 'mainEvent',
},
],
},
{
id: 300,
name: 'DD-2',
children: [
{
id: 5,
name: '事件-2-1',
start: '2024-11-18',
end: '2024-11-21',
type: 'mainEvent',
},
],
},
{
name: 'DD-3',
children: [
{
id: 6,
name: '事件-3-1',
start: '2024-11-21',
end: '2024-11-22',
type: 'mainEvent',
},
{
id: 7,
name: '事件-3-2',
start: '2024-11-18',
end: '2024-11-19',
type: 'mainEvent',
},
],
},
{
name: 'DD-4',
children: [
{
id: 8,
name: '事件-4-1',
start: '2024-11-20',
end: '2024-11-21',
type: 'mainEvent',
},
{
id: 9,
name: '事件-4-2',
start: '2024-11-25',
end: '2024-11-26',
type: 'mainEvent',
},
{
id: 10,
name: '事件-4-3',
start: '2024-11-17',
end: '2024-11-18',
type: 'mainEvent',
},
{
id: 11,
name: '事件-4-4',
start: '2024-11-22',
end: '2024-11-25',
type: 'mainEvent',
},
{
id: 12,
name: '事件-4-5',
start: '2024-11-23',
end: '2024-11-24',
type: 'mainEvent',
},
],
},
{
name: 'DD-5',
children: [
{
id: 13,
name: '事件-5-1',
start: '2024-11-22',
end: '2024-11-25',
type: 'mainEvent',
},
{
id: 14,
name: '事件-5-2',
start: '2024-11-27',
end: '2024-11-30',
type: 'mainEvent',
},
{
id: 15,
name: '事件-5-3',
start: '2024-12-10',
end: '2024-12-18',
type: 'mainEvent',
},
],
},
{
name: 'DD-6',
children: [
{
id: 16,
name: '事件-6-1',
start: '2024-11-20',
end: '2024-11-30',
type: 'mainEvent',
},
{
id: 17,
name: '事件-6-2',
start: '2024-12-02',
end: '2024-12-18',
type: 'mainEvent',
},
],
},
{
name: 'DD-7',
children: [
{
id: 18,
name: '事件-7-1',
start: '2024-12-22',
end: '2024-12-28',
type: 'mainEvent',
},
],
},
{
name: 'DD-8',
children: [
{
id: 19,
name: '事件-8-1',
start: '2024-11-25',
end: '2024-11-30',
type: 'mainEvent',
},
{
id: 20,
name: '事件-8-2',
start: '2024-12-01',
end: '2024-12-18',
type: 'mainEvent',
},
],
},
]
const task = [
{
id: 1,
name: '任务-1',
type: 'task',
children: main,
},
]
export function getMainGantt(data = {}) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(main)
}, 200)
})
}
export function getEventListByDDType(ddType) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(main.find(item => item.name === ddType))
}, 200)
})
}
export function getSubGantt(subId) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(sub)
}, 200)
})
}
export function getTask() {
return new Promise((resolve, reject) => {
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,
default: true,
},
nodeClick: {
type: Function as (key: string) => void,
default: undefined,
},
})
const searchValue = ref<string>('')
@ -48,6 +52,11 @@ const updateChecked = (keys: Array<string | number>) => {
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(
// () => props.defaultCheckedKeys,
// val => {
@ -86,6 +95,7 @@ const updateChecked = (keys: Array<string | number>) => {
:node-props="nodeProps"
:render-suffix="renderSuffix"
:render-label="renderLabel"
@update:selected-keys="updateSelected"
/>
<!-- :default-checked-keys="defaultCheckedKeys" -->
</n-scrollbar>

View File

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

View File

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

View File

@ -1,80 +1,130 @@
import {
NForm,
NFormItem,
NInput,
NButton,
NDatePicker,
NUpload,
} from 'naive-ui'
import ModalCom from '@/components/Modal/index.vue'
import { useEvent } from '../hooks'
export default defineComponent({
// props: {
// show: {
// type: Boolean,
// default: false,
// },
// },
setup() {
const { showMainEvent, mainEventData } = useEvent()
const close = () => {
showMainEvent.value = false
}
watch(
[
() => mainEventData.value.start,
() => mainEventData.value.end,
() => mainEventData.value.avatar,
],
([start, end, avatar]) => {
timeRange.value = start ? [start, end] : null
uploadImg.value = [
{
url: avatar,
status: 'finished',
},
]
}
)
const timeRange = ref(null)
const uploadImg = ref([])
return () => (
<ModalCom v-model:show={showMainEvent.value} title="编辑事件">
<NForm
class="w-[500px]"
model={mainEventData}
label-placement="left"
label-width="auto"
>
<NFormItem label="事件名称" path="name">
<NInput v-model:value={mainEventData.value.name} />
</NFormItem>
<NFormItem label="事件时间" path="description">
<NDatePicker
v-model:value={timeRange.value}
type="daterange"
clearable
/>
</NFormItem>
<NFormItem label="上传图片" path="avatar">
<NUpload
default-file-list={uploadImg.value}
list-type="image-card"
max={1}
/>
</NFormItem>
</NForm>
<div class="flex justify-end gap-2">
<NButton onClick={close}>取消</NButton>
<NButton type="primary" onClick={close}>
确认
</NButton>
</div>
</ModalCom>
)
},
})
import {
NForm,
NFormItem,
NInput,
NButton,
NDatePicker,
NUpload,
} from 'naive-ui'
import ModalCom from '@/components/Modal/index.vue'
import { useEvent } from '../hooks'
import { addSimp, updateSimp, uploadImage } from '@/api/Gantt'
export default defineComponent({
// props: {
// show: {
// type: Boolean,
// default: false,
// },
// },
setup() {
const imageValue = ref()
const {
universalRules,
showMainEvent,
mainEventData,
targetId,
searchTreeList,
} = useEvent()
const close = () => {
showMainEvent.value = false
}
const changeFile = async file => {
console.log(uploadImg)
const formData = new FormData()
formData.append('file', file.file.file)
const res = await uploadImage(formData)
imageValue.value = res.data.path
}
const sure = async () => {
const data = {
...mainEventData.value,
targetId: targetId.value,
fileUrl: imageValue.value,
startTime: new Date(timeRange.value[0]).toISOString(),
endTime: new Date(timeRange.value[0]).toISOString(),
}
const res = mainEventData.value.id
? await updateSimp(data)
: await addSimp(data)
if (res.code === 200) {
showMainEvent.value = false
searchTreeList()
}
}
watch(
[
() => mainEventData.value.startTime,
() => mainEventData.value.endTime,
() => mainEventData.value.fileUrl,
],
([start, end, fileUrl]) => {
timeRange.value = start
? [new Date(start).getTime(), new Date(end).getTime()]
: null
if (fileUrl) {
uploadImg.value = [
{
id: '1',
name: fileUrl,
url: `${window.settings.imgServer}${fileUrl}`,
status: 'finished',
},
]
}
}
)
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 {
NForm,
NFormItem,
NInput,
NButton,
NDatePicker,
NUpload,
NSelect,
} from 'naive-ui'
import ModalCom from '@/components/Modal/index.vue'
import { useEvent } from '../hooks'
export default defineComponent({
// props: {
// show: {
// type: Boolean,
// default: false,
// },
// },
setup() {
const { showNewEvent, eventData } = useEvent()
const close = () => {
showNewEvent.value = false
}
watch(
[
() => eventData.value.start,
() => eventData.value.end,
() => eventData.value.avatar,
],
([start, end, avatar]) => {
timeRange.value = start ? [start, end] : null
uploadImg.value = [
{
url: avatar,
status: 'finished',
},
]
}
)
const timeRange = ref(null)
const uploadImg = ref([])
const value1 = ref(null)
const value2 = ref(null)
const value3 = ref(null)
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={value1.value}></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="description">
<NDatePicker
v-model:value={timeRange.value}
type="daterange"
clearable
/>
</NFormItem>
<NFormItem label="上传图片" path="avatar">
<NUpload
default-file-list={uploadImg.value}
list-type="image-card"
max={1}
/>
</NFormItem>
</NForm>
<div class="flex justify-end gap-2">
<NButton onClick={close}>取消</NButton>
<NButton type="primary" onClick={close}>
确认
</NButton>
</div>
</ModalCom>
)
},
})
import {
NForm,
NFormItem,
NInput,
NButton,
NDatePicker,
NUpload,
NSelect,
} from 'naive-ui'
import ModalCom from '@/components/Modal/index.vue'
import { useEvent } from '../hooks'
import { addSon, updateSon, uploadImage } from '@/api/gantt'
export default defineComponent({
// props: {
// show: {
// type: Boolean,
// default: false,
// },
// },
setup() {
const {
showNewEvent,
eventData,
sonOptions,
oneClassData,
searchTreeList,
} = useEvent()
const imageValue = ref()
const close = () => {
showNewEvent.value = false
}
const changeFile = async file => {
const formData = new FormData()
formData.append('file', file.file.file)
const res = await uploadImage(formData)
imageValue.value = res.data.path
}
const sure = async () => {
const data = {
// id: eventData.value.id ?? null,
// activityId: oneClassData.value.activityId ?? null,
// name: eventData.value.name,
activityId: oneClassData.value?.pid ?? null,
oneType: oneClassData.value?.id,
...eventData.value,
twoType: twoTypeId.value,
fileUrl: imageValue.value,
startTime: new Date(timeRange.value[0]).toISOString(),
endTime: new Date(timeRange.value[1]).toISOString(),
}
// console.log(data, 'data')
const res = eventData.value.id
? await updateSon(data)
: await addSon(data)
if (res.code === 200) {
showNewEvent.value = false
searchTreeList()
}
}
watch(
[
() => eventData.value.startTime,
() => eventData.value.endTime,
() => eventData.value.fileUrl,
() => eventData.value.twoType,
],
([start, end, fileUrl, twoType]) => {
timeRange.value = start
? [new Date(start).getTime(), new Date(end).getTime()]
: null
if (fileUrl) {
uploadImg.value = [
{
id: fileUrl,
name: fileUrl,
url: `${window.settings.imgServer}${fileUrl}`,
status: 'finished',
},
]
console.log(uploadImg.value)
}
console.log(twoType, 'twoType')
twoType && (twoTypeId.value = twoType)
}
)
const timeRange = ref(null)
const uploadImg = ref([])
const twoTypeId = ref(null)
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 = () => {
const trajData = ref({
id: 'dd',
data: [
{
name: '起始点',
lon: 120,
lat: 21,
alt: 0,
time: 1183135260000,
},
{
name: '中间特征点',
lon: 122,
lat: 21,
alt: 1000000,
time: 1183135265000,
detached: true,
},
{
name: '中间特征点',
lon: 124,
lat: 21,
alt: 1500000,
time: 1183135270000,
detached: true,
},
{
name: '中间特征点',
lon: 128,
lat: 21,
alt: 2000000,
time: 1183135280000,
detached: true,
},
{
name: '落点',
lon: 135,
lat: 21,
alt: 1500000,
time: 1183135290000,
},
],
})
const interceptData = ref([
{
id: 'dd1',
data: [
{
name: '起始点',
lon: 137,
lat: 25,
alt: 0,
time: 1183135270000,
},
{
name: '中间特征点',
lon: 138,
lat: 24,
alt: 1000000,
time: 1183135280000,
detached: true,
},
{
name: '落点',
lon: 135,
lat: 21,
alt: 1500000,
time: 1183135290000,
},
],
},
])
function addIntercept() {
// d
interceptData.value.push({
data: [
{
name: '起始点',
lon: 120,
lat: 21,
alt: 0,
time: 1183135260000,
},
{
name: '中间特征点',
lon: 120,
lat: 21,
alt: 0,
time: 1183135260000,
detached: false,
},
trajData.value.data.at(-1),
],
})
}
return {
trajData,
interceptData,
addIntercept,
}
}
export const useEventDdConfig = () => {
const trajData = ref({
id: 'dd',
data: [
{
name: '起始点',
lon: 120,
lat: 21,
alt: 0,
time: 1183135260000,
},
{
name: '中间特征点',
lon: 122,
lat: 21,
alt: 1000000,
time: 1183135265000,
detached: true,
},
{
name: '中间特征点',
lon: 124,
lat: 21,
alt: 1500000,
time: 1183135270000,
detached: true,
},
{
name: '中间特征点',
lon: 128,
lat: 21,
alt: 2000000,
time: 1183135280000,
detached: true,
},
{
name: '落点',
lon: 135,
lat: 21,
alt: 1500000,
time: 1183135290000,
},
],
})
const interceptData = ref([
{
id: 'dd1',
data: [
{
name: '起始点',
lon: 137,
lat: 25,
alt: 0,
time: 1183135270000,
},
{
name: '中间特征点',
lon: 138,
lat: 24,
alt: 1000000,
time: 1183135280000,
detached: true,
},
{
name: '落点',
lon: 135,
lat: 21,
alt: 1500000,
time: 1183135290000,
},
],
},
])
function addIntercept() {
// d
interceptData.value.push({
data: [
{
name: '起始点',
lon: 120,
lat: 21,
alt: 0,
time: 1183135260000,
},
{
name: '中间特征点',
lon: 120,
lat: 21,
alt: 0,
time: 1183135260000,
detached: false,
},
trajData.value.data.at(-1),
],
})
}
return {
trajData,
interceptData,
addIntercept,
}
}

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

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

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 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>
</>
)
},
})
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,
})
const refresh = ref(false)
expose({ refresh })
watch(refresh, val => {
if (val) {
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
}
})
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 { useDialog } from 'naive-ui'
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 useGanttEdit = ({ router, route }: GanttParams) => {
const { subId } = route.params
const records = ref([])
const dialog = useDialog()
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,
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(),
timelineHeader: {
backgroundColor: headerBgColor,
colWidth: 150,
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',
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,
},
]
// 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',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
// alignItems: 'center',
cursor: 'pointer',
})
if (!subId) {
container.addEventListener('click', event => {
router.push({
path: `/gantt/subEdit/${taskRecord.id}`,
})
})
}
if (subId) {
const image = new Image({
image: taskRecord.avatar,
width: 100,
height: 100,
x: 10,
y: 10,
boundsPadding: [0, 0, 10, 0],
})
container.add(image)
}
const checkBoxGroup = new Group({
width,
//height,
fill: 'transparent',
display: 'flex',
})
container.add(checkBoxGroup)
// const checkbox = new CheckBox({
// id: taskRecord.id,
// text: '',
// checked: false,
// })
// checkBoxGroup.add(checkbox)
// checkbox.addEventListener('click', event => {
// event.stopPropagation()
// console.log(checkbox, 'checkbox')
// })
const name = new Text({
text: taskRecord.name,
fontSize: 16,
fontFamily: 'sans-serif',
fill: textColor,
fontWeight: 'bold',
maxLineWidth: width,
textAlign: 'center',
// boundsPadding: [0, 0, 0, 10],
})
checkBoxGroup.add(name)
const editSymbol = new Image({
width: 20,
height: 20,
fill: '#fff',
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>',
boundsPadding: [-2, 0, 0, 10],
cursor: 'pointer',
})
checkBoxGroup.add(editSymbol)
editSymbol.addEventListener('click', event => {
event.stopPropagation()
console.log(editSymbol, 'editSymbol')
})
const deleteSymbol = new Image({
width: 20,
height: 20,
fill: '#fff',
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>',
boundsPadding: [-2, 0, 0, 10],
cursor: 'pointer',
})
checkBoxGroup.add(deleteSymbol)
deleteSymbol.addEventListener('click', event => {
event.stopPropagation()
// console.log(dialog, '----')
dialog.warning({
title: '警告',
content: '确认删除当前事件?',
positiveText: '确定',
negativeText: '取消',
onPositiveClick: () => {},
onNegativeClick: () => {},
})
console.log(deleteSymbol, 'deleteSymbol')
})
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:
scale === 'day'
? startDate.toLocaleDateString()
: startDate.toLocaleTimeString(),
fontSize: 14,
fontWeight: 'bold',
fontFamily: 'sans-serif',
fill: textColor,
textAlign: 'center',
maxLineWidth: width,
// boundsPadding: [25, 12, 10, 12],
})
container.add(day)
return {
rootContainer: container,
}
},
},
]
}
return { renderMainTask, changeTimeScales }
}
export default useGanttEdit
import { Group, Image, Text, CheckBox, Rect } from '@visactor/vtable/es/vrender'
import { useDialog } from 'naive-ui'
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 useGanttEdit = ({ router, route }: GanttParams) => {
const { subId } = route.params
const records = ref([])
const dialog = useDialog()
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,
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(),
timelineHeader: {
backgroundColor: headerBgColor,
colWidth: 150,
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',
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,
},
]
// 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',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
// alignItems: 'center',
cursor: 'pointer',
})
if (!subId) {
container.addEventListener('click', event => {
router.push({
path: `/gantt/subEdit/${taskRecord.id}`,
})
})
}
if (subId) {
const image = new Image({
image: taskRecord.avatar,
width: 100,
height: 100,
x: 10,
y: 10,
boundsPadding: [0, 0, 10, 0],
})
container.add(image)
}
const checkBoxGroup = new Group({
width,
//height,
fill: 'transparent',
display: 'flex',
})
container.add(checkBoxGroup)
// const checkbox = new CheckBox({
// id: taskRecord.id,
// text: '',
// checked: false,
// })
// checkBoxGroup.add(checkbox)
// checkbox.addEventListener('click', event => {
// event.stopPropagation()
// console.log(checkbox, 'checkbox')
// })
const name = new Text({
text: taskRecord.name,
fontSize: 16,
fontFamily: 'sans-serif',
fill: textColor,
fontWeight: 'bold',
maxLineWidth: width,
textAlign: 'center',
// boundsPadding: [0, 0, 0, 10],
})
checkBoxGroup.add(name)
const editSymbol = new Image({
width: 20,
height: 20,
fill: '#fff',
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>',
boundsPadding: [-2, 0, 0, 10],
cursor: 'pointer',
})
checkBoxGroup.add(editSymbol)
editSymbol.addEventListener('click', event => {
event.stopPropagation()
console.log(editSymbol, 'editSymbol')
})
const deleteSymbol = new Image({
width: 20,
height: 20,
fill: '#fff',
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>',
boundsPadding: [-2, 0, 0, 10],
cursor: 'pointer',
})
checkBoxGroup.add(deleteSymbol)
deleteSymbol.addEventListener('click', event => {
event.stopPropagation()
// console.log(dialog, '----')
dialog.warning({
title: '警告',
content: '确认删除当前事件?',
positiveText: '确定',
negativeText: '取消',
onPositiveClick: () => {},
onNegativeClick: () => {},
})
console.log(deleteSymbol, 'deleteSymbol')
})
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:
scale === 'day'
? startDate.toLocaleDateString()
: startDate.toLocaleTimeString(),
fontSize: 14,
fontWeight: 'bold',
fontFamily: 'sans-serif',
fill: textColor,
textAlign: 'center',
maxLineWidth: width,
// boundsPadding: [25, 12, 10, 12],
})
container.add(day)
return {
rootContainer: container,
}
},
},
]
}
return { renderMainTask, changeTimeScales }
}
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 { 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 } = useGanttEdit({ route, router })
onMounted(() => {
nextTick(() => {
renderMainTask(document.querySelector('#tableContainer'))
})
})
watch(
() => props.scale,
val => {
changeTimeScales(val)
}
)
return () => <div id="tableContainer" class="bg-[#1c202c] w-h-full"></div>
},
})
import useGanttEdit from './hooks/ganttEdit'
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 } = useGanttEdit({ route, router })
onMounted(() => {
nextTick(() => {
renderMainTask(document.querySelector('#tableContainer'))
})
})
watch(
() => props.scale,
val => {
changeTimeScales(val)
}
)
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 {
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} />
</>
)
},
})
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() {
ganttRef.value.refresh = true
}
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}
/>
</>
)
},
})

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 { useRouter } from 'vue-router'
import GanttCom from '../GanttEdit'
export default defineComponent({
setup() {
const range = ref()
const value = ref('day')
const addEvent = () => {
console.log('tianjia --- event')
}
const addTask = () => {
console.log('tianjiarenwu')
}
const router = useRouter()
const ok = () => {
router.push('/gantt')
}
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>
<div class="ml-auto flex gap-2">
<NButton type="primary" onClick={addEvent}>
添加事件
</NButton>
{/* <NButton type="primary" onClick={addTask}>
添加任务
</NButton> */}
<NButton type="primary" onClick={ok}>
完成编辑
</NButton>
</div>
</div>
<GanttCom scale={value.value} />
</>
)
},
})
import { NDatePicker, NRadioButton, NRadioGroup, NButton } from 'naive-ui'
import { useRouter } from 'vue-router'
import GanttCom from '../GanttEdit'
export default defineComponent({
setup() {
const range = ref()
const value = ref('day')
const addEvent = () => {
console.log('tianjia --- event')
}
const addTask = () => {
console.log('tianjiarenwu')
}
const router = useRouter()
const ok = () => {
router.push('/gantt')
}
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>
<div class="ml-auto flex gap-2">
<NButton type="primary" onClick={addEvent}>
添加事件
</NButton>
{/* <NButton type="primary" onClick={addTask}>
添加任务
</NButton> */}
<NButton type="primary" onClick={ok}>
完成编辑
</NButton>
</div>
</div>
<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 GanttCom from '../Gantt'
import { useRouter } from 'vue-router'
export default defineComponent({
setup() {
const router = useRouter()
const value = ref('day')
const showView = () => {
// getCheckboxState
const checked = ganttInstance.taskListTableInstance?.getCheckboxState()
console.log(checked)
}
return () => (
<>
<div class="flex gap-2">
<NButton onClick={showView}>态势展示</NButton>
<NButton
onClick={() => {
router.go(-1)
}}
>
返回
</NButton>
</div>
<GanttCom scale={value.value} />
</>
)
},
})
import { NButton } from 'naive-ui'
import GanttCom from '../Gantt'
import { useRouter } from 'vue-router'
export default defineComponent({
setup() {
const router = useRouter()
const value = ref('day')
const showView = () => {
// getCheckboxState
const checked = ganttInstance.taskListTableInstance?.getCheckboxState()
console.log(checked)
}
return () => (
<>
<div class="flex gap-2">
<NButton onClick={showView}>态势展示</NButton>
<NButton
onClick={() => {
router.go(-1)
}}
>
返回
</NButton>
</div>
<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 GanttCom from '../GanttEdit'
import { useRouter } from 'vue-router'
export default defineComponent({
setup() {
const router = useRouter()
const value = ref('day')
const showView = () => {
// getCheckboxState
const checked = ganttInstance.taskListTableInstance?.getCheckboxState()
console.log(checked)
}
return () => (
<>
<div class="flex gap-2">
{/* <NButton onClick={showView}>态势展示</NButton> */}
<NButton
onClick={() => {
router.go(-1)
}}
>
返回
</NButton>
</div>
<GanttCom scale={value.value} />
</>
)
},
})
import { NButton } from 'naive-ui'
import GanttCom from '../GanttEdit'
import { useRouter } from 'vue-router'
export default defineComponent({
setup() {
const router = useRouter()
const value = ref('day')
const showView = () => {
// getCheckboxState
const checked = ganttInstance.taskListTableInstance?.getCheckboxState()
console.log(checked)
}
return () => (
<>
<div class="flex gap-2">
{/* <NButton onClick={showView}>态势展示</NButton> */}
<NButton
onClick={() => {
router.go(-1)
}}
>
返回
</NButton>
</div>
<GanttCom scale={value.value} />
</>
)
},
})

View File

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

View File

@ -1,118 +1,118 @@
import { NForm, NFormItem, NInput, NDataTable, NButton } from 'naive-ui'
import ModalCom from '@/components/Modal/index.vue'
import { getMainGantt } from '@/api/gantt'
import useTask from './hooks'
export default defineComponent({
// props: {
// show: {
// type: Boolean,
// default: false,
// },
// },
setup() {
// const checkedRowKeys = ref([])
// watch(checkedRowKeys, newval => {
// console.log(newval, '-----')
// })
const columns = [
{ type: 'selection' },
{
title: '事件名称',
key: 'name',
width: 220,
// render: row => {
// return (
// <div class="inline-flex items-center gap-2">
// <NIcon>
// <HelpCircleOutline />
// </NIcon>
// {row.name}
// </div>
// )
// },
},
{
title: '开始时间',
key: 'start',
},
{
title: '结束时间',
key: 'end',
},
// {
// title: '',
// key: 'type',
// },
{
title: '图片',
key: 'avatar',
render(row) {
if (row.avatar) {
return <img src={row.avatar} width="50" alt="" />
} else {
return <span>-</span>
}
},
},
]
const tableData = ref([])
onMounted(async () => {
const res = await getMainGantt()
tableData.value = res
})
const { showNewTask, curTaskData } = useTask()
const close = () => {
showNewTask.value = false
console.log(curTaskData, '---')
}
// watch(
// showNewTask,
// show => {
// if (show) {
// taskData.value = {
// name: '',
// description: '',
// checkedEvent: [],
// }
// }
// },
// { immediate: true }
// )
return () => (
<ModalCom v-model:show={showNewTask.value} title="新增任务">
<NForm
class="w-[900px]"
model={curTaskData}
label-placement="left"
label-width="auto"
>
<NFormItem label="任务名称" path="name">
<NInput v-model:value={curTaskData.value.name} />
</NFormItem>
<NFormItem label="任务描述" path="description">
<NInput v-model:value={curTaskData.value.description} />
</NFormItem>
<NFormItem label="事件" path="event">
<NDataTable
v-model:checked-row-keys={curTaskData.value.checkedEvent}
class="h-[400px]"
flex-height
columns={columns}
data={tableData.value}
row-key={row => row.name}
/>
</NFormItem>
</NForm>
<div class="flex justify-end gap-2">
<NButton onClick={close}>取消</NButton>
<NButton type="primary">确认</NButton>
</div>
</ModalCom>
)
},
})
import { NForm, NFormItem, NInput, NDataTable, NButton } from 'naive-ui'
import ModalCom from '@/components/Modal/index.vue'
import { getMainGantt } from '@/api/gantt'
import useTask from './hooks'
export default defineComponent({
// props: {
// show: {
// type: Boolean,
// default: false,
// },
// },
setup() {
// const checkedRowKeys = ref([])
// watch(checkedRowKeys, newval => {
// console.log(newval, '-----')
// })
const columns = [
{ type: 'selection' },
{
title: '事件名称',
key: 'name',
width: 220,
// render: row => {
// return (
// <div class="inline-flex items-center gap-2">
// <NIcon>
// <HelpCircleOutline />
// </NIcon>
// {row.name}
// </div>
// )
// },
},
{
title: '开始时间',
key: 'start',
},
{
title: '结束时间',
key: 'end',
},
// {
// title: '',
// key: 'type',
// },
{
title: '图片',
key: 'avatar',
render(row) {
if (row.avatar) {
return <img src={row.avatar} width="50" alt="" />
} else {
return <span>-</span>
}
},
},
]
const tableData = ref([])
onMounted(async () => {
const res = await getMainGantt()
tableData.value = res
})
const { showNewTask, curTaskData } = useTask()
const close = () => {
showNewTask.value = false
console.log(curTaskData, '---')
}
// watch(
// showNewTask,
// show => {
// if (show) {
// taskData.value = {
// name: '',
// description: '',
// checkedEvent: [],
// }
// }
// },
// { immediate: true }
// )
return () => (
<ModalCom v-model:show={showNewTask.value} title="新增任务">
<NForm
class="w-[900px]"
model={curTaskData}
label-placement="left"
label-width="auto"
>
<NFormItem label="任务名称" path="name">
<NInput v-model:value={curTaskData.value.name} />
</NFormItem>
<NFormItem label="任务描述" path="description">
<NInput v-model:value={curTaskData.value.description} />
</NFormItem>
<NFormItem label="事件" path="event">
<NDataTable
v-model:checked-row-keys={curTaskData.value.checkedEvent}
class="h-[400px]"
flex-height
columns={columns}
data={tableData.value}
row-key={row => row.name}
/>
</NFormItem>
</NForm>
<div class="flex justify-end gap-2">
<NButton onClick={close}>取消</NButton>
<NButton type="primary">确认</NButton>
</div>
</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 { useTree } from '@/utils/tree'
import { getTask } from '@/api/gantt'
import {
HelpCircleOutline,
CreateOutline,
TrashBinOutline,
AddCircleOutline,
EnterOutline,
} from '@vicons/ionicons5'
import useTask from './components/NewTask/hooks'
export default defineComponent({
setup() {
const dict = window.settings.gantt
const columns = [
{
title: '任务名称/事件名称',
key: 'name',
width: 'auto',
render: row => {
return (
<div class="inline-flex items-center gap-2">
{row.type && (
<NTag
size="small"
round
bordered={false}
type={dict[row.type]?.color}
>
{dict[row.type].label}
</NTag>
)}
{row.name}
</div>
)
},
},
{
title: '开始时间',
key: 'start',
},
{
title: '结束时间',
key: 'end',
},
// {
// title: '',
// key: 'type',
// render(row) {
// return (
// row.type && (
// <NTag
// size="small"
// round
// bordered={false}
// type={dict[row.type]?.color}
// >
// {dict[row.type].label}
// </NTag>
// )
// )
// },
// },
{
title: '图片',
key: 'avatar',
render(row) {
if (row.avatar) {
return <img src={row.avatar} width="50" alt="" />
} else {
return <span>-</span>
}
},
},
{
title: '操作',
key: 'action',
render(row) {
return (
<div class="flex justify-end">
{row.type === 'task' ? (
<>
<NButton
type="primary"
size="small"
quaternary
onClick={() => editTask(row)}
>
<NIcon>
<EnterOutline />
</NIcon>
进入任务
</NButton>
<NButton
type="primary"
size="small"
quaternary
onClick={() => editTask(row)}
>
<NIcon>
<CreateOutline />
</NIcon>
编辑
</NButton>
</>
) : null}
{/* {!row.avatar ? (
<NButton
type="success"
size="small"
quaternary
onClick={() => handleEdit(row)}
>
<NIcon>
<AddCircleOutline />
</NIcon>
</NButton>
) : (
<></>
)} */}
{/* <NButton
type="error"
size="small"
quaternary
onClick={() => handleEdit(row)}
>
<NIcon>
<TrashBinOutline />
</NIcon>
</NButton> */}
</div>
)
},
},
]
const tableData = ref([])
const { getAllKeys } = useTree()
const expandedRowKeys = ref([])
onMounted(async () => {
await getTaskList()
})
async function getTaskList() {
const res = await getTask()
tableData.value = res
expandedRowKeys.value = getAllKeys(tableData.value, 'name')
}
const { showNewTask, curTaskData } = useTask()
function editTask(row) {
showNewTask.value = true
curTaskData.value = {
...row,
checkedEvent: getAllKeys(row.children, 'name'),
}
}
return () => (
<NDataTable
class="h-full"
flex-height
indent={30}
v-model:expanded-row-keys={expandedRowKeys.value}
columns={columns}
data={tableData.value}
row-key={row => row.name}
/>
)
},
})
import { NDataTable, NIcon, NButton, NTag } from 'naive-ui'
import { useTree } from '@/utils/tree'
import { getTask } from '@/api/gantt'
import {
HelpCircleOutline,
CreateOutline,
TrashBinOutline,
AddCircleOutline,
EnterOutline,
} from '@vicons/ionicons5'
import useTask from './components/NewTask/hooks'
export default defineComponent({
setup() {
const dict = window.settings.gantt
const columns = [
{
title: '任务名称/事件名称',
key: 'name',
width: 'auto',
render: row => {
return (
<div class="inline-flex items-center gap-2">
{row.type && (
<NTag
size="small"
round
bordered={false}
type={dict[row.type]?.color}
>
{dict[row.type].label}
</NTag>
)}
{row.name}
</div>
)
},
},
{
title: '开始时间',
key: 'start',
},
{
title: '结束时间',
key: 'end',
},
// {
// title: '',
// key: 'type',
// render(row) {
// return (
// row.type && (
// <NTag
// size="small"
// round
// bordered={false}
// type={dict[row.type]?.color}
// >
// {dict[row.type].label}
// </NTag>
// )
// )
// },
// },
{
title: '图片',
key: 'avatar',
render(row) {
if (row.avatar) {
return <img src={row.avatar} width="50" alt="" />
} else {
return <span>-</span>
}
},
},
{
title: '操作',
key: 'action',
render(row) {
return (
<div class="flex justify-end">
{row.type === 'task' ? (
<>
<NButton
type="primary"
size="small"
quaternary
onClick={() => editTask(row)}
>
<NIcon>
<EnterOutline />
</NIcon>
进入任务
</NButton>
<NButton
type="primary"
size="small"
quaternary
onClick={() => editTask(row)}
>
<NIcon>
<CreateOutline />
</NIcon>
编辑
</NButton>
</>
) : null}
{/* {!row.avatar ? (
<NButton
type="success"
size="small"
quaternary
onClick={() => handleEdit(row)}
>
<NIcon>
<AddCircleOutline />
</NIcon>
</NButton>
) : (
<></>
)} */}
{/* <NButton
type="error"
size="small"
quaternary
onClick={() => handleEdit(row)}
>
<NIcon>
<TrashBinOutline />
</NIcon>
</NButton> */}
</div>
)
},
},
]
const tableData = ref([])
const { getAllKeys } = useTree()
const expandedRowKeys = ref([])
onMounted(async () => {
await getTaskList()
})
async function getTaskList() {
const res = await getTask()
tableData.value = res
expandedRowKeys.value = getAllKeys(tableData.value, 'name')
}
const { showNewTask, curTaskData } = useTask()
function editTask(row) {
showNewTask.value = true
curTaskData.value = {
...row,
checkedEvent: getAllKeys(row.children, 'name'),
}
}
return () => (
<NDataTable
class="h-full"
flex-height
indent={30}
v-model:expanded-row-keys={expandedRowKeys.value}
columns={columns}
data={tableData.value}
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 {
NDatePicker,
NButton,
NFloatButton,
NIcon,
NDrawer,
NDrawerContent,
NTabs,
NTabPane,
NSelect,
} from 'naive-ui'
import { ArrowForward } from '@vicons/ionicons5'
import HeaderCom from '../Content/components/Header/index.vue'
import TaskList from './components/TaskList'
import EventList from './components/EventList'
import NewTask from './components/TaskList/components/NewTask'
import useTask from './components/TaskList/components/NewTask/hooks'
import { useEvent } from './components/EventList/hooks'
export default defineComponent({
setup() {
const show = ref(false)
const range = ref()
const { showNewTask } = useTask()
const addNewTask = () => {
showNewTask.value = true
}
const ddList = Array.from({ length: 8 }, (_, i) => ({
label: `DD-${i + 1}`,
value: `DD-${i + 1}`,
}))
const dd = ref(`DD-1`)
const paneClass = `border-1 h-full border-l-0 border-[var(--n-tab-border-color)] !p-2`
const { showMainEvent, mainEventData } = useEvent()
const addNewMainEvent = () => {
showMainEvent.value = true
mainEventData.value = {}
}
return () => (
<div class="flex flex-col bg-[#1c202c] w-h-full">
<div class="relative h-[60px]">
<HeaderCom />
</div>
<div class="z-30 flex flex-1 flex-col gap-4 p-5">
<RouterView />
</div>
<NFloatButton
class="z-40"
left={-10}
bottom={document.body.clientHeight / 2}
shape="square"
onClick={() => {
show.value = true
}}
>
<NIcon>
<ArrowForward />
</NIcon>
</NFloatButton>
<NDrawer
class="h-[100vh] bg-[#1c202cee]"
v-model:show={show.value}
width={document.body.clientWidth - 200}
placement="left"
display-directive={'show'}
>
<NDrawerContent title="事件管理" closable>
<div class="flex h-full flex-col gap-2">
<NTabs
class="h-full"
pane-wrapper-class="h-full"
type="card"
animated
placement="left"
defaultValue={'事件列表'}
>
{/* <NTabPane
class={paneClass}
name="任务列表"
tab="任务列表"
display-directive={'show'}
>
<div class="flex h-full flex-col gap-2">
<div class="flex justify-end gap-2">
<NDatePicker
v-model:value={range.value}
type="daterange"
clearable
/>
<NButton type="primary" onClick={addNewTask}>
添加任务
</NButton>
</div>
<div class="flex-1">
<TaskList />
</div>
</div>
</NTabPane> */}
<NTabPane
class={paneClass}
name="事件列表"
tab="事件列表"
display-directive={'show'}
>
<div class="flex h-full flex-col gap-2">
<div class="flex justify-end gap-2 ">
<NSelect
class="w-[200px]"
v-model:value={dd.value}
options={ddList}
></NSelect>
<NDatePicker
v-model:value={range.value}
type="daterange"
clearable
/>
<NButton type="primary" onClick={addNewMainEvent}>
添加事件
</NButton>
</div>
<div class="flex-1">
<EventList dd={dd.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>
)
},
})
import { RouterView } from 'vue-router'
import {
NDatePicker,
NButton,
NFloatButton,
NIcon,
NDrawer,
NDrawerContent,
NTabs,
NTabPane,
NSelect,
} from 'naive-ui'
import { ArrowForward } from '@vicons/ionicons5'
import HeaderCom from '../Content/components/Header/index.vue'
import TaskList from './components/TaskList'
import EventList from './components/EventList'
import NewTask from './components/TaskList/components/NewTask'
import useTask from './components/TaskList/components/NewTask/hooks'
import { useEvent } from './components/EventList/hooks'
import { getSimpList, getSimpTreeList } from '@/api/Gantt'
import { onBeforeMount, nextTick } from 'vue'
export default defineComponent({
setup() {
const show = ref(false)
const { showNewTask } = useTask()
const addNewTask = () => {
showNewTask.value = true
}
async function getSimpListData() {
const res = await getSimpList()
ddList.value = res.data.list
targetId.value = res.data.list[0].id
}
// const ddList = Array.from({ length: 8 }, (_, i) => ({
// label: `DD-${i + 1}`,
// value: `DD-${i + 1}`,
// }))
const ddList = ref([])
const paneClass = `border-1 h-full border-l-0 border-[var(--n-tab-border-color)] !p-2`
const {
showMainEvent,
mainEventData,
targetId,
range,
searchTreeList,
tableData,
} = useEvent()
const addNewMainEvent = async () => {
showMainEvent.value = true
// const res = await addNewMainEvent({ })
mainEventData.value = {}
}
onBeforeMount(() => {
nextTick(async () => {
await getSimpListData()
await searchTreeList()
})
})
return () => (
<div class="flex flex-col bg-[#1c202c] w-h-full">
<div class="relative h-[60px]">
<HeaderCom />
</div>
<div class="z-30 flex flex-1 flex-col gap-4 p-5">
<RouterView />
</div>
<NFloatButton
class="z-40"
left={-10}
bottom={document.body.clientHeight / 2}
shape="square"
onClick={() => {
show.value = true
}}
>
<NIcon>
<ArrowForward />
</NIcon>
</NFloatButton>
<NDrawer
class="h-[100vh] bg-[#1c202cee]"
v-model:show={show.value}
width={document.body.clientWidth - 200}
placement="left"
display-directive={'show'}
>
<NDrawerContent title="事件管理" closable>
<div class="flex h-full flex-col gap-2">
{/* <NTabs
class="h-full"
pane-wrapper-class="h-full"
type="card"
animated
placement="left"
defaultValue={'事件列表'}
>
<NTabPane
class={paneClass}
name="任务列表"
tab="任务列表"
display-directive={'show'}
>
<div class="flex h-full flex-col gap-2">
<div class="flex justify-end gap-2">
<NDatePicker
v-model:value={range.value}
type="daterange"
clearable
/>
<NButton type="primary" onClick={addNewTask}>
添加任务
</NButton>
</div>
<div class="flex-1">
<TaskList />
</div>
</div>
</NTabPane>
<NTabPane
class={paneClass}
name="事件列表"
tab="事件列表"
display-directive={'show'}
> */}
<div class="flex h-full flex-col gap-2">
<div class="flex justify-end gap-2 ">
<NSelect
class="w-[200px]"
v-model:value={targetId.value}
options={ddList.value}
label-field="name"
value-field="id"
></NSelect>
<NDatePicker
v-model:value={range.value}
type="daterange"
clearable
/>
<NButton type="primary" onClick={searchTreeList}>
搜索
</NButton>
<NButton type="primary" onClick={addNewMainEvent}>
添加事件
</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>
import TestJsx from './index.jsx'
</script>
<template>
<test-jsx />
</template>
<script setup>
import TestJsx from './index.jsx'
</script>
<template>
<test-jsx />
</template>

View File

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

View File

@ -13,11 +13,6 @@ interface ISatellite {
tle: string
}
interface IBaseFilterParam {
treeData: Array<any>
params: Array<string | number>
paramName: string
}
const { filterTreeNodeByField } = useTree()
const satelliteList = ref<ISatellite[]>([])
const checkedKeys = ref<Array<string | number>>([])
@ -121,6 +116,7 @@ export function useSatellite() {
addSatellites,
showPoint,
showPointUnderSat,
flyToSatellite,
}
}
@ -147,8 +143,6 @@ function createSatelliteCommunicationPayload(satId: string) {
beam.create()
console.log(beam)
if (!satelliteBeamMap.has(satId)) {
satelliteBeamMap.set(satId, new Map())
}
@ -198,6 +192,16 @@ async function getSatelliteList() {
const checked = JSON.parse(JSON.stringify(checkedKeys.value))
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
if (code === '200') {
checkedKeys.value = []
@ -235,3 +239,17 @@ function getAllNodesToPayload() {
})
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 { useSatellite } from './hooks/satellite'
import { showDetailsSatellite } from './components/SatDetail'
import { useEarth } from '../Earth/hooks/earth'
import { useEntity } from '@/hooks/entity'
const {
satelliteList,
checkedKeys,
getSatelliteList,
addSatellites,
flyToSatellite,
} = useSatellite()
onMounted(async () => {
@ -21,32 +20,13 @@ onMounted(async () => {
watch(checkedKeys, val => {
addSatellites(val)
})
const { flyTo } = useEarth()
const { satelliteMap } = useEntity()
const nodeProps = ({ option }: { option: TreeOption }) => {
// console.log(option, 'option')
return {
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 }) => {
// console.log(option)