雨辰7日gantt
This commit is contained in:
parent
a2b20d4b56
commit
7426a33282
45
src/utils/timer.js
Normal file
45
src/utils/timer.js
Normal file
@ -0,0 +1,45 @@
|
||||
export const useTimer = () => {
|
||||
return {
|
||||
Interval,
|
||||
}
|
||||
}
|
||||
|
||||
class Interval {
|
||||
constructor(callback, interval, options = {}) {
|
||||
this.callback = callback
|
||||
this.interval = interval
|
||||
this.timer = null
|
||||
this.options = options
|
||||
}
|
||||
|
||||
immediateRun() {
|
||||
if (this.options.immediate) {
|
||||
this.callback()
|
||||
}
|
||||
}
|
||||
|
||||
startInterval() {
|
||||
this.immediateRun()
|
||||
let expectedTime = performance.now() + this.interval
|
||||
|
||||
const self = this
|
||||
function loop(currentTime) {
|
||||
const deltaTime = currentTime - expectedTime
|
||||
// console.log(deltaTime)
|
||||
if (deltaTime >= 0) {
|
||||
self.callback()
|
||||
expectedTime += self.interval
|
||||
}
|
||||
|
||||
self.timer = requestAnimationFrame(loop)
|
||||
}
|
||||
|
||||
this.timer = requestAnimationFrame(loop)
|
||||
|
||||
return self.timer
|
||||
}
|
||||
|
||||
stopInterval() {
|
||||
cancelAnimationFrame(this.timer)
|
||||
}
|
||||
}
|
0
src/views/Gantt/components/EventList/components/EventDdConfig.jsx
Executable file → Normal file
0
src/views/Gantt/components/EventList/components/EventDdConfig.jsx
Executable file → Normal file
66
src/views/Gantt/components/EventList/components/MainEventEdit.jsx
Executable file → Normal file
66
src/views/Gantt/components/EventList/components/MainEventEdit.jsx
Executable file → Normal file
@ -19,7 +19,7 @@ export default defineComponent({
|
||||
setup() {
|
||||
const imageValue = ref()
|
||||
const {
|
||||
universalRules,
|
||||
mainEventRules,
|
||||
showMainEvent,
|
||||
mainEventData,
|
||||
targetId,
|
||||
@ -28,6 +28,8 @@ export default defineComponent({
|
||||
|
||||
const close = () => {
|
||||
showMainEvent.value = false
|
||||
uploadImg.value = []
|
||||
timeRange.value = null
|
||||
}
|
||||
const changeFile = async file => {
|
||||
console.log(uploadImg)
|
||||
@ -36,34 +38,55 @@ export default defineComponent({
|
||||
const res = await uploadImage(formData)
|
||||
imageValue.value = res.data.path
|
||||
}
|
||||
const sure = async () => {
|
||||
const formRef = ref(null)
|
||||
async function sure(e) {
|
||||
e.preventDefault()
|
||||
const data = {
|
||||
...mainEventData.value,
|
||||
targetId: targetId.value,
|
||||
fileUrl: imageValue.value,
|
||||
startTime: new Date(timeRange.value[0]).toISOString(),
|
||||
endTime: new Date(timeRange.value[0]).toISOString(),
|
||||
startTime: mainEventData.value.timeRange
|
||||
? mainEventData.value.timeRange[0]
|
||||
: null,
|
||||
endTime: mainEventData.value.timeRange
|
||||
? mainEventData.value.timeRange[1]
|
||||
: null,
|
||||
}
|
||||
|
||||
const res = mainEventData.value.id
|
||||
? await updateSimp(data)
|
||||
: await addSimp(data)
|
||||
if (res.code === 200) {
|
||||
showMainEvent.value = false
|
||||
searchTreeList()
|
||||
// formRef.value?.validate(async erros => {
|
||||
// if (!erros) {
|
||||
// const res = mainEventData.value.id
|
||||
// ? await updateSimp(data)
|
||||
// : await addSimp(data)
|
||||
// if (res.code === 200) {
|
||||
// close()
|
||||
// searchTreeList()
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
if (mainEventData.value.name && mainEventData.value.timeRange) {
|
||||
const res = mainEventData.value.id
|
||||
? await updateSimp(data)
|
||||
: await addSimp(data)
|
||||
if (res.code === 200) {
|
||||
close()
|
||||
searchTreeList()
|
||||
}
|
||||
} else {
|
||||
formRef.value?.validate(erros => {})
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
[
|
||||
() => mainEventData.value.startTime,
|
||||
() => mainEventData.value.endTime,
|
||||
() => mainEventData.value.fileUrl,
|
||||
// () => mainEventData.value.startTime,
|
||||
// () => mainEventData.value.endTime,
|
||||
],
|
||||
([start, end, fileUrl]) => {
|
||||
timeRange.value = start
|
||||
? [new Date(start).getTime(), new Date(end).getTime()]
|
||||
: null
|
||||
([fileUrl, start, end]) => {
|
||||
// timeRange.value = start
|
||||
// ? [new Date(start).getTime(), new Date(end).getTime()]
|
||||
// : null
|
||||
if (fileUrl) {
|
||||
uploadImg.value = [
|
||||
{
|
||||
@ -87,19 +110,20 @@ export default defineComponent({
|
||||
title={`${mainEventData.value.id ? '编辑' : '添加'}事件`}
|
||||
>
|
||||
<NForm
|
||||
ref={formRef}
|
||||
class="w-[500px]"
|
||||
model={mainEventData}
|
||||
model={mainEventData.value}
|
||||
label-placement="left"
|
||||
label-width="auto"
|
||||
rules={universalRules}
|
||||
rules={mainEventRules}
|
||||
>
|
||||
<NFormItem label="事件名称" path="name">
|
||||
<NInput v-model:value={mainEventData.value.name} />
|
||||
</NFormItem>
|
||||
<NFormItem label="事件时间" path="startTime">
|
||||
<NFormItem label="事件时间" path="timeRange">
|
||||
<NDatePicker
|
||||
v-model:value={timeRange.value}
|
||||
type="daterange"
|
||||
v-model:value={mainEventData.value.timeRange}
|
||||
type="datetimerange"
|
||||
clearable
|
||||
/>
|
||||
</NFormItem>
|
||||
|
87
src/views/Gantt/components/EventList/components/SubEventEdit.jsx
Executable file → Normal file
87
src/views/Gantt/components/EventList/components/SubEventEdit.jsx
Executable file → Normal file
@ -19,6 +19,8 @@ export default defineComponent({
|
||||
// },
|
||||
setup() {
|
||||
const {
|
||||
subEventRules,
|
||||
|
||||
showNewEvent,
|
||||
eventData,
|
||||
sonOptions,
|
||||
@ -27,6 +29,9 @@ export default defineComponent({
|
||||
} = useEvent()
|
||||
const imageValue = ref()
|
||||
const close = () => {
|
||||
timeRange.value = null
|
||||
uploadImg.value = []
|
||||
|
||||
showNewEvent.value = false
|
||||
}
|
||||
const changeFile = async file => {
|
||||
@ -35,7 +40,11 @@ export default defineComponent({
|
||||
const res = await uploadImage(formData)
|
||||
imageValue.value = res.data.path
|
||||
}
|
||||
const sure = async () => {
|
||||
|
||||
const formRef = ref(null)
|
||||
async function sure(e) {
|
||||
e.preventDefault()
|
||||
|
||||
const data = {
|
||||
// id: eventData.value.id ?? null,
|
||||
// activityId: oneClassData.value.activityId ?? null,
|
||||
@ -43,32 +52,53 @@ export default defineComponent({
|
||||
activityId: oneClassData.value?.pid ?? null,
|
||||
oneType: oneClassData.value?.id,
|
||||
...eventData.value,
|
||||
twoType: twoTypeId.value,
|
||||
// twoType: twoTypeId.value,
|
||||
fileUrl: imageValue.value,
|
||||
startTime: new Date(timeRange.value[0]).toISOString(),
|
||||
endTime: new Date(timeRange.value[1]).toISOString(),
|
||||
startTime: eventData.value.timeRange
|
||||
? eventData.value.timeRange[0]
|
||||
: null,
|
||||
endTime: eventData.value.timeRange
|
||||
? eventData.value.timeRange[1]
|
||||
: null,
|
||||
}
|
||||
|
||||
// eventData.value.startTime = data.startTime
|
||||
// formRef.value?.validate(async erros => {
|
||||
// console.log(erros, eventData, '')
|
||||
|
||||
// if (!erros) {
|
||||
if (
|
||||
eventData.value.name &&
|
||||
eventData.value.timeRange &&
|
||||
eventData.value.twoType
|
||||
) {
|
||||
const res = eventData.value.id
|
||||
? await updateSon(data)
|
||||
: await addSon(data)
|
||||
if (res.code === 200) {
|
||||
close()
|
||||
searchTreeList()
|
||||
}
|
||||
} else {
|
||||
formRef.value?.validate(erros => {})
|
||||
}
|
||||
|
||||
// }
|
||||
// })
|
||||
// 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,
|
||||
// () => eventData.value.startTime,
|
||||
// () => eventData.value.endTime,
|
||||
// () => eventData.value.twoType,
|
||||
],
|
||||
([start, end, fileUrl, twoType]) => {
|
||||
timeRange.value = start
|
||||
? [new Date(start).getTime(), new Date(end).getTime()]
|
||||
: null
|
||||
([fileUrl, start, end, twoType]) => {
|
||||
// timeRange.value = start
|
||||
// ? [new Date(start).getTime(), new Date(end).getTime()]
|
||||
// : null
|
||||
if (fileUrl) {
|
||||
uploadImg.value = [
|
||||
{
|
||||
@ -80,15 +110,15 @@ export default defineComponent({
|
||||
]
|
||||
console.log(uploadImg.value)
|
||||
}
|
||||
console.log(twoType, 'twoType')
|
||||
// console.log(twoType, 'twoType')
|
||||
|
||||
twoType && (twoTypeId.value = twoType)
|
||||
// twoType && (twoTypeId.value = twoType)
|
||||
}
|
||||
)
|
||||
|
||||
const timeRange = ref(null)
|
||||
const uploadImg = ref([])
|
||||
const twoTypeId = ref(null)
|
||||
// const twoTypeId = ref(null)
|
||||
|
||||
return () => (
|
||||
<ModalCom
|
||||
@ -96,14 +126,16 @@ export default defineComponent({
|
||||
title={`${eventData.value.id ? '编辑' : '添加'}子事件`}
|
||||
>
|
||||
<NForm
|
||||
ref={formRef}
|
||||
class="w-[500px]"
|
||||
model={eventData}
|
||||
model={eventData.value}
|
||||
label-placement="left"
|
||||
label-width="auto"
|
||||
rules={subEventRules}
|
||||
>
|
||||
<NFormItem label="二级分类">
|
||||
<NFormItem label="二级分类" path="twoType">
|
||||
<NSelect
|
||||
v-model:value={twoTypeId.value}
|
||||
v-model:value={eventData.value.twoType}
|
||||
options={sonOptions.value}
|
||||
label-field="name"
|
||||
value-field="id"
|
||||
@ -118,10 +150,11 @@ export default defineComponent({
|
||||
<NFormItem label="事件名称" path="name">
|
||||
<NInput v-model:value={eventData.value.name} />
|
||||
</NFormItem>
|
||||
<NFormItem label="事件时间" path="startTime">
|
||||
<NFormItem label="事件时间" path="timeRange">
|
||||
<NDatePicker
|
||||
v-model:value={timeRange.value}
|
||||
type="daterange"
|
||||
// v-model:value={timeRange.value}
|
||||
v-model:value={eventData.value.timeRange}
|
||||
type="datetimerange"
|
||||
clearable
|
||||
/>
|
||||
</NFormItem>
|
||||
|
0
src/views/Gantt/components/EventList/components/useEventDdConfig.jsx
Executable file → Normal file
0
src/views/Gantt/components/EventList/components/useEventDdConfig.jsx
Executable file → Normal file
58
src/views/Gantt/components/EventList/hooks.jsx
Executable file → Normal file
58
src/views/Gantt/components/EventList/hooks.jsx
Executable file → Normal file
@ -1,8 +1,26 @@
|
||||
import { getSimpTreeList, getTwoClass } from '@/api/Gantt'
|
||||
|
||||
const universalRules = {
|
||||
name: { required: true, message: '请输入', trigger: 'blur' },
|
||||
startTime: { required: true, message: '请输入', trigger: 'blur' },
|
||||
const subEventRules = {
|
||||
name: { required: true, message: '请输入名称', trigger: ['blur', 'input'] },
|
||||
timeRange: {
|
||||
required: true,
|
||||
message: '请选择时间',
|
||||
trigger: ['blur', 'input'],
|
||||
},
|
||||
twoType: {
|
||||
required: true,
|
||||
message: '请选择二级分类',
|
||||
trigger: ['blur', 'change'],
|
||||
},
|
||||
}
|
||||
const mainEventRules = {
|
||||
name: { required: true, message: '请输入名称', trigger: ['blur', 'input'] },
|
||||
|
||||
timeRange: {
|
||||
required: true,
|
||||
message: '请选择时间',
|
||||
trigger: ['blur', 'input'],
|
||||
},
|
||||
}
|
||||
const showMainEvent = ref(false)
|
||||
|
||||
@ -14,26 +32,25 @@ watch(showMainEvent, show => {
|
||||
|
||||
const mainEventData = ref({
|
||||
name: '',
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
timeRange: null,
|
||||
|
||||
// type: 'mainEvent',
|
||||
describe: '',
|
||||
fileUrl: '',
|
||||
})
|
||||
|
||||
function resetMainEventData() {
|
||||
mainEventData.value = ref({
|
||||
mainEventData.value = {
|
||||
name: '',
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
// type: 'mainEvent',
|
||||
timeRange: null,
|
||||
|
||||
describe: '',
|
||||
fileUrl: '',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const targetId = ref(null)
|
||||
const range = ref([new Date('2000-01-01').getTime(), Date.now()])
|
||||
const range = ref(null)
|
||||
|
||||
const showNewEvent = ref(false)
|
||||
|
||||
@ -46,25 +63,25 @@ watch(showNewEvent, show => {
|
||||
|
||||
const eventData = ref({
|
||||
name: '',
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
timeRange: null,
|
||||
fileUrl: '',
|
||||
describe: '',
|
||||
equipModel: '',
|
||||
reportSite: '',
|
||||
twoType: '',
|
||||
// type: 'subEvent',
|
||||
})
|
||||
|
||||
function resetEventData() {
|
||||
eventData.value = ref({
|
||||
eventData.value = {
|
||||
name: '',
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
timeRange: null,
|
||||
fileUrl: '',
|
||||
describe: '',
|
||||
equipModel: '',
|
||||
reportSite: '',
|
||||
})
|
||||
twoType: '',
|
||||
}
|
||||
}
|
||||
|
||||
const tableData = ref([])
|
||||
@ -75,8 +92,8 @@ async function searchTreeList() {
|
||||
|
||||
const res = await getSimpTreeList({
|
||||
targetId: targetId.value,
|
||||
startTime: new Date(range.value[0]).toISOString(),
|
||||
endTime: new Date(range.value[1]).toISOString(),
|
||||
startTime: range.value ? range.value[0] : null,
|
||||
endTime: range.value ? range.value[1] : null,
|
||||
})
|
||||
tableData.value = res.data.list
|
||||
// console.log('searchTreeList', tableData)
|
||||
@ -90,7 +107,8 @@ async function getTwoClassList(oneTypeId) {
|
||||
// const getSonList = async()
|
||||
export const useEvent = () => {
|
||||
return {
|
||||
universalRules,
|
||||
subEventRules,
|
||||
mainEventRules,
|
||||
showMainEvent,
|
||||
mainEventData,
|
||||
showNewEvent,
|
||||
|
5
src/views/Gantt/components/EventList/index.jsx
Executable file → Normal file
5
src/views/Gantt/components/EventList/index.jsx
Executable file → Normal file
@ -222,7 +222,10 @@ export default defineComponent({
|
||||
|
||||
function editSubEvent(row) {
|
||||
showNewEvent.value = true
|
||||
eventData.value = cloneDeep(row)
|
||||
eventData.value = cloneDeep({
|
||||
...row,
|
||||
timeRange: [row.startTime, row.endTime],
|
||||
})
|
||||
getTwoClassList(row.oneType)
|
||||
console.log('子事件编辑:', row, 'onClassData', oneClassData)
|
||||
}
|
||||
|
0
src/views/Gantt/components/Gantt/components/VRender.jsx
Executable file → Normal file
0
src/views/Gantt/components/Gantt/components/VRender.jsx
Executable file → Normal file
101
src/views/Gantt/components/Gantt/hooks/gantt.ts
Executable file → Normal file
101
src/views/Gantt/components/Gantt/hooks/gantt.ts
Executable file → Normal file
@ -4,7 +4,8 @@ import { Gantt, tools, TYPES } from '@visactor/vtable-gantt'
|
||||
import { getMainGantt, getSubGantt } from '@/api/Gantt/gantt'
|
||||
import { getSon } from '@/api/Gantt'
|
||||
import { useTree } from '@/utils/tree'
|
||||
import * as dayjs from 'dayjs'
|
||||
import { useInfoBox } from './infoBox.jsx'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
type GanttParams = {
|
||||
route?: any
|
||||
@ -35,7 +36,6 @@ const useGantt = ({ router, route }: GanttParams) => {
|
||||
: await getMainGantt(params)
|
||||
if (code === 200) {
|
||||
// records.value = data.list
|
||||
console.log(subId, ',,,,,')
|
||||
if (subId) {
|
||||
records.value = data.list
|
||||
.reduce((acc, cur) => {
|
||||
@ -67,7 +67,7 @@ const useGantt = ({ router, route }: GanttParams) => {
|
||||
return acc
|
||||
}, [])
|
||||
.flat()
|
||||
console.log(records.value)
|
||||
// console.log(records.value)
|
||||
|
||||
records.value.length > 0 &&
|
||||
(timeRange.value = getTimeRangeForTree(records.value))
|
||||
@ -91,19 +91,15 @@ const useGantt = ({ router, route }: GanttParams) => {
|
||||
month,
|
||||
lastDayOfMonth.getDate()
|
||||
)
|
||||
endOfMonth.setHours(23, 59, 59, 9999)
|
||||
// new Date().toISOString()
|
||||
|
||||
endOfMonth.setHours(23, 59, 59, 0)
|
||||
// console.log(
|
||||
// mainEvent.startTime,
|
||||
// startOfMonth.toISOString(),
|
||||
// endOfMonth.toISOString()
|
||||
// startOfMonth.toLocaleString(),
|
||||
// endOfMonth.toLocaleString()
|
||||
// )
|
||||
|
||||
return {
|
||||
...mainEvent,
|
||||
start: startOfMonth.toISOString(),
|
||||
end: endOfMonth.toISOString(),
|
||||
start: startOfMonth,
|
||||
end: endOfMonth,
|
||||
}
|
||||
}),
|
||||
}
|
||||
@ -200,10 +196,10 @@ const useGantt = ({ router, route }: GanttParams) => {
|
||||
maxDate: timeRange.value[1],
|
||||
markLine: [
|
||||
{
|
||||
date: '2024-01-14T21:12:40',
|
||||
date: new Date(),
|
||||
style: {
|
||||
lineWidth: 1,
|
||||
lineColor: 'blue',
|
||||
lineColor: 'rgb(13, 255, 255)',
|
||||
lineDash: [8, 4],
|
||||
},
|
||||
},
|
||||
@ -337,6 +333,8 @@ const useGantt = ({ router, route }: GanttParams) => {
|
||||
return taskListTable
|
||||
}
|
||||
|
||||
const { createInfoBox, updatePosition, removeInfoBox } = useInfoBox()
|
||||
|
||||
function renderTaskBar(subId: string | number) {
|
||||
// console.log(subId, '------');
|
||||
const taskBar = {
|
||||
@ -353,20 +351,43 @@ const useGantt = ({ router, route }: GanttParams) => {
|
||||
const container = new Group({
|
||||
width,
|
||||
height,
|
||||
fill: 'transparent',
|
||||
// fill: textColor,
|
||||
// fill: 'transparent',
|
||||
// fill: '#ff0',
|
||||
// fillOpacity: 0.1,
|
||||
// stroke: textColor,
|
||||
// strokeOpacity: 0.2,
|
||||
// lineWidth: 4,
|
||||
lineWidth: 4,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
cursor: 'pointer',
|
||||
})
|
||||
|
||||
container.addEventListener('mouseenter', event => {
|
||||
// const { x, y } = useMouse()
|
||||
createInfoBox({
|
||||
x: event.client.x,
|
||||
y: event.client.y,
|
||||
content: taskRecord.describe,
|
||||
target: event.target,
|
||||
})
|
||||
})
|
||||
|
||||
container.addEventListener('mouseleave', event => {
|
||||
removeInfoBox()
|
||||
})
|
||||
|
||||
container.addEventListener('mousemove', event => {
|
||||
updatePosition({
|
||||
x: event.client.x,
|
||||
y: event.client.y,
|
||||
content: taskRecord.describe,
|
||||
target: event.target,
|
||||
})
|
||||
})
|
||||
|
||||
container.addEventListener('click', async () => {
|
||||
console.log(taskRecord, 'ooooooo')
|
||||
removeInfoBox()
|
||||
if (!subId) {
|
||||
router.push({
|
||||
path: `/gantt/sub/${taskRecord.id}`,
|
||||
@ -434,6 +455,10 @@ const useGantt = ({ router, route }: GanttParams) => {
|
||||
y: 10,
|
||||
boundsPadding: [0, 0, 10, 0],
|
||||
cursor: 'pointer',
|
||||
// texture: 'rect',
|
||||
// textureColor: '#0006',
|
||||
// html: { dom: `<img width='200' height='200' src='${imgUrl}' />` },
|
||||
opacity: taskRecord.status === 2 ? 0.3 : 1,
|
||||
})
|
||||
container.add(image)
|
||||
image.addEventListener('click', e => {
|
||||
@ -485,6 +510,8 @@ const useGantt = ({ router, route }: GanttParams) => {
|
||||
fontWeight: 'bold',
|
||||
maxLineWidth: width,
|
||||
textAlign: 'center',
|
||||
opacity: taskRecord.status === 2 ? 0.3 : 1,
|
||||
|
||||
// boundsPadding: [10, 0, 0, 0],
|
||||
})
|
||||
nameContainer.add(name)
|
||||
@ -495,6 +522,7 @@ const useGantt = ({ router, route }: GanttParams) => {
|
||||
fontFamily: 'sans-serif',
|
||||
fill: textColor,
|
||||
boundsPadding: [10, 0, 0, 0],
|
||||
opacity: taskRecord.status === 2 ? 0.3 : 1,
|
||||
})
|
||||
container.add(start)
|
||||
const end = new Text({
|
||||
@ -503,6 +531,7 @@ const useGantt = ({ router, route }: GanttParams) => {
|
||||
fontFamily: 'sans-serif',
|
||||
fill: textColor,
|
||||
boundsPadding: [10, 0, 0, 0],
|
||||
opacity: taskRecord.status === 2 ? 0.3 : 1,
|
||||
})
|
||||
container.add(end)
|
||||
const rect = new Rect({
|
||||
@ -526,6 +555,7 @@ const useGantt = ({ router, route }: GanttParams) => {
|
||||
],
|
||||
},
|
||||
boundsPadding: [10, 0, 0, 0],
|
||||
opacity: taskRecord.status === 2 ? 0.3 : 1,
|
||||
})
|
||||
container.add(rect)
|
||||
|
||||
@ -546,6 +576,20 @@ const useGantt = ({ router, route }: GanttParams) => {
|
||||
// console.log(taskBar, '0000000');
|
||||
return taskBar
|
||||
}
|
||||
|
||||
function updateMarkLine() {
|
||||
ganttInstance?.updateMarkLine([
|
||||
{
|
||||
date: new Date(),
|
||||
style: {
|
||||
lineWidth: 1,
|
||||
lineColor: 'rgb(13, 255, 255)',
|
||||
lineDash: [8, 4],
|
||||
},
|
||||
},
|
||||
])
|
||||
console.log(ganttInstance)
|
||||
}
|
||||
function renderGroup(opt: IGroupGraphicAttribute) {
|
||||
return new Group(opt)
|
||||
}
|
||||
@ -583,7 +627,9 @@ const useGantt = ({ router, route }: GanttParams) => {
|
||||
})
|
||||
|
||||
const day = new Text({
|
||||
text: startDate.toLocaleDateString(),
|
||||
text: subId
|
||||
? dayjs(startDate).format('YYYY年M月D日')
|
||||
: dayjs(startDate).format('YYYY年M月'),
|
||||
// scale === 'day'
|
||||
// ? startDate.toLocaleDateString()
|
||||
// : startDate.toLocaleTimeString(),
|
||||
@ -600,9 +646,24 @@ const useGantt = ({ router, route }: GanttParams) => {
|
||||
}
|
||||
},
|
||||
},
|
||||
// {
|
||||
// unit: 'hour',
|
||||
// step: 1,
|
||||
// style: {
|
||||
// fontSize: 10,
|
||||
// fontWeight: 'normal',
|
||||
// },
|
||||
// visible: false,
|
||||
// },
|
||||
]
|
||||
}
|
||||
return { renderMainTask, changeTimeScales, currentImage }
|
||||
return {
|
||||
getGanttData,
|
||||
renderMainTask,
|
||||
changeTimeScales,
|
||||
currentImage,
|
||||
updateMarkLine,
|
||||
}
|
||||
}
|
||||
|
||||
export default useGantt
|
||||
|
57
src/views/Gantt/components/Gantt/hooks/infoBox.jsx
Normal file
57
src/views/Gantt/components/Gantt/hooks/infoBox.jsx
Normal file
@ -0,0 +1,57 @@
|
||||
// import { NCard } from 'naive-ui'
|
||||
|
||||
let infoBox
|
||||
export const useInfoBox = () => {
|
||||
function createInfoBox({ x, y, content, target }) {
|
||||
if (!content) return
|
||||
if (!infoBox) {
|
||||
infoBox = document.createElement('div')
|
||||
|
||||
infoBox.id = 'infoBox'
|
||||
infoBox.className = `absolute z-50 shadow-sm bg-[var(--color-bg)] max-w-[300px] text-sm p-2`
|
||||
document.body.appendChild(infoBox)
|
||||
|
||||
infoBox.innerHTML = `<p>${content}</p>`
|
||||
|
||||
infoBox.offsetHeight
|
||||
}
|
||||
|
||||
updatePosition({ x, y })
|
||||
}
|
||||
|
||||
function updatePosition({ x, y }) {
|
||||
if (!infoBox) return
|
||||
const pageWidth = window.innerWidth
|
||||
const pageHeight = window.innerHeight
|
||||
|
||||
let topPosition = y + 20
|
||||
let leftPosition = x + 20
|
||||
|
||||
let rightPosition = 0
|
||||
let bottomPosition = 0
|
||||
|
||||
if (
|
||||
pageWidth - x < infoBox.offsetWidth + 20 ||
|
||||
pageHeight - y < infoBox.offsetHeight + 20
|
||||
) {
|
||||
leftPosition = 0
|
||||
rightPosition = infoBox.offsetWidth + 20
|
||||
// topPosition = 0
|
||||
// bottomPosition = pageHeight - infoBox.offsetHeight + 10
|
||||
}
|
||||
|
||||
infoBox.style.top = topPosition ? `${topPosition}px` : null
|
||||
infoBox.style.left = leftPosition ? `${leftPosition}px` : null
|
||||
infoBox.style.right = rightPosition ? `${rightPosition}px` : null
|
||||
infoBox.style.bottom = bottomPosition ? `${bottomPosition}px` : null
|
||||
}
|
||||
|
||||
function removeInfoBox() {
|
||||
if (infoBox) {
|
||||
infoBox.remove()
|
||||
infoBox = null
|
||||
}
|
||||
}
|
||||
|
||||
return { createInfoBox, updatePosition, removeInfoBox }
|
||||
}
|
65
src/views/Gantt/components/Gantt/index.jsx
Executable file → Normal file
65
src/views/Gantt/components/Gantt/index.jsx
Executable file → Normal file
@ -1,6 +1,8 @@
|
||||
import { NImage } from 'naive-ui'
|
||||
import useGantt from './hooks/gantt'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { useTimer } from '@/utils/timer.js'
|
||||
import { onBeforeUnmount } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
@ -20,26 +22,33 @@ export default defineComponent({
|
||||
setup(props, { expose }) {
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const { renderMainTask, changeTimeScales, currentImage } = useGantt({
|
||||
const {
|
||||
getGanttData,
|
||||
renderMainTask,
|
||||
changeTimeScales,
|
||||
currentImage,
|
||||
updateMarkLine,
|
||||
} = useGantt({
|
||||
route,
|
||||
router,
|
||||
})
|
||||
|
||||
const { Interval } = useTimer()
|
||||
|
||||
let intervalTimer = null
|
||||
// let markLineIntervalTimer = null
|
||||
|
||||
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,
|
||||
})
|
||||
stopRefresh()
|
||||
|
||||
intervalTimer = new Interval(startRefresh, 5000, { immediate: true })
|
||||
intervalTimer && intervalTimer.startInterval()
|
||||
|
||||
refresh.value = false
|
||||
}
|
||||
})
|
||||
@ -47,15 +56,16 @@ export default defineComponent({
|
||||
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,
|
||||
startTime: props.dateRange ? props.dateRange[0] : null,
|
||||
endTime: props.dateRange ? props.dateRange[1] : null,
|
||||
})
|
||||
|
||||
stopRefresh()
|
||||
intervalTimer = new Interval(startRefresh, 5000, { immediate: true })
|
||||
intervalTimer.startInterval()
|
||||
refresh.value = false
|
||||
})
|
||||
})
|
||||
@ -74,6 +84,29 @@ export default defineComponent({
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
async function startRefresh() {
|
||||
await getGanttData({
|
||||
ids: props.types,
|
||||
startTime: props.dateRange ? props.dateRange[0] : null,
|
||||
endTime: props.dateRange ? props.dateRange[1] : null,
|
||||
})
|
||||
|
||||
updateMarkLine()
|
||||
}
|
||||
|
||||
function stopRefresh() {
|
||||
console.log(intervalTimer, 'stop!!!!!!!!!!!!!!!!!!!!!!!!!!!')
|
||||
intervalTimer && intervalTimer.stopInterval()
|
||||
intervalTimer = null
|
||||
|
||||
// markLineIntervalTimer && markLineIntervalTimer.stopInterval()
|
||||
// intervalTimer = null
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
stopRefresh()
|
||||
})
|
||||
return () => (
|
||||
<>
|
||||
<div id="tableContainer" class="bg-[#1c202c] w-h-full"></div>
|
||||
|
0
src/views/Gantt/components/Gantt1/components/VRender.jsx
Executable file → Normal file
0
src/views/Gantt/components/Gantt1/components/VRender.jsx
Executable file → Normal file
0
src/views/Gantt/components/Gantt1/hooks/gantt.ts
Executable file → Normal file
0
src/views/Gantt/components/Gantt1/hooks/gantt.ts
Executable file → Normal file
0
src/views/Gantt/components/Gantt1/index.jsx
Executable file → Normal file
0
src/views/Gantt/components/Gantt1/index.jsx
Executable file → Normal file
0
src/views/Gantt/components/Gantt2/components/VRender.jsx
Executable file → Normal file
0
src/views/Gantt/components/Gantt2/components/VRender.jsx
Executable file → Normal file
0
src/views/Gantt/components/Gantt2/hooks/gantt.ts
Executable file → Normal file
0
src/views/Gantt/components/Gantt2/hooks/gantt.ts
Executable file → Normal file
6
src/views/Gantt/components/Gantt2/index.jsx
Executable file → Normal file
6
src/views/Gantt/components/Gantt2/index.jsx
Executable file → Normal file
@ -15,7 +15,7 @@ export default defineComponent({
|
||||
types: {
|
||||
type: Array,
|
||||
require: true,
|
||||
}
|
||||
},
|
||||
},
|
||||
setup(props, { expose }) {
|
||||
const router = useRouter()
|
||||
@ -32,8 +32,8 @@ export default defineComponent({
|
||||
// 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
|
||||
startTime: props.dateRange ? props.dateRange[0] : null,
|
||||
endTime: props.dateRange ? props.dateRange[1] : null,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
0
src/views/Gantt/components/GanttEdit/hooks/ganttEdit.ts
Executable file → Normal file
0
src/views/Gantt/components/GanttEdit/hooks/ganttEdit.ts
Executable file → Normal file
0
src/views/Gantt/components/GanttEdit/index.jsx
Executable file → Normal file
0
src/views/Gantt/components/GanttEdit/index.jsx
Executable file → Normal file
9
src/views/Gantt/components/MainGantt/index.jsx
Executable file → Normal file
9
src/views/Gantt/components/MainGantt/index.jsx
Executable file → Normal file
@ -12,7 +12,7 @@ import { onBeforeMount } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const range = ref([new Date('2000-01-01').getTime(), Date.now()])
|
||||
const range = ref([new Date('2000-01-01 00:00:00').getTime(), Date.now()])
|
||||
const value = ref('year')
|
||||
const types = ref([])
|
||||
|
||||
@ -43,7 +43,12 @@ export default defineComponent({
|
||||
return () => (
|
||||
<>
|
||||
<div class="flex gap-2">
|
||||
<NDatePicker v-model:value={range.value} type="daterange" clearable />
|
||||
<NDatePicker
|
||||
class="w-[600px]"
|
||||
v-model:value={range.value}
|
||||
type="datetimerange"
|
||||
clearable
|
||||
/>
|
||||
{/* <NRadioGroup v-model:value={value.value} name="radiobuttongroup">
|
||||
<NRadioButton value="hour" label="日" />
|
||||
<NRadioButton value="day" label="月" />
|
||||
|
2
src/views/Gantt/components/MainGantt1/index.jsx
Executable file → Normal file
2
src/views/Gantt/components/MainGantt1/index.jsx
Executable file → Normal file
@ -24,7 +24,7 @@ export default defineComponent({
|
||||
<div class="flex gap-2">
|
||||
<NDatePicker
|
||||
v-model:value={range.value}
|
||||
type="daterange"
|
||||
type="datetimerange"
|
||||
clearable
|
||||
disabled
|
||||
/>
|
||||
|
43
src/views/Gantt/components/MainGantt2/index.jsx
Executable file → Normal file
43
src/views/Gantt/components/MainGantt2/index.jsx
Executable file → Normal file
@ -1,4 +1,4 @@
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useRouter } from 'vue-router'
|
||||
import {
|
||||
NDatePicker,
|
||||
NRadioButton,
|
||||
@ -7,12 +7,12 @@ import {
|
||||
NSelect,
|
||||
} from 'naive-ui'
|
||||
import GanttCom from '../Gantt'
|
||||
import {getDDList}from '@/api/Gantt/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 range = ref([new Date('2000-01-01 00:00:00').getTime(), Date.now()])
|
||||
const value = ref('year')
|
||||
const types = ref([])
|
||||
|
||||
@ -22,40 +22,36 @@ export default defineComponent({
|
||||
router.push('/gantt/mainEdit')
|
||||
}
|
||||
|
||||
onBeforeMount(async ()=>{
|
||||
onBeforeMount(async () => {
|
||||
await getDDOptions()
|
||||
})
|
||||
|
||||
const ddOptions = ref([])
|
||||
async function getDDOptions (){
|
||||
const {code,data} = await getDDList()
|
||||
if(code === 200) {
|
||||
async function getDDOptions() {
|
||||
const { code, data } = await getDDList()
|
||||
if (code === 200) {
|
||||
ddOptions.value = data.list
|
||||
types.value = ddOptions.value.map(item=>item.id)
|
||||
types.value = ddOptions.value.map(item => item.id)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const ganttRef = ref(null)
|
||||
function searchGanttData(){
|
||||
function searchGanttData() {
|
||||
// console.log(ganttRef);
|
||||
renderMainTask(document.querySelector('#tableContainer'),{
|
||||
renderMainTask(document.querySelector('#tableContainer'), {
|
||||
ids: types.value,
|
||||
startTime: new Date(range.value[0]).toISOString(),
|
||||
endTime: new Date(range.value[1]).toISOString()
|
||||
startTime: range.value[0],
|
||||
endTime: range.value[1],
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
return () => (
|
||||
<>
|
||||
<div class="flex gap-2">
|
||||
<NDatePicker
|
||||
v-model:value={range.value}
|
||||
type="daterange"
|
||||
type="datetimerange"
|
||||
clearable
|
||||
|
||||
/>
|
||||
{/* <NRadioGroup v-model:value={value.value} name="radiobuttongroup">
|
||||
<NRadioButton value="hour" label="日" />
|
||||
@ -65,9 +61,9 @@ export default defineComponent({
|
||||
v-model:value={types.value}
|
||||
multiple
|
||||
options={ddOptions.value}
|
||||
label-field='name'
|
||||
label-field="name"
|
||||
clearable
|
||||
value-field='id'
|
||||
value-field="id"
|
||||
></NSelect>
|
||||
{/* <NButton class="ml-auto" type="primary" onClick={editEvent}>
|
||||
编辑事件
|
||||
@ -76,7 +72,12 @@ export default defineComponent({
|
||||
搜索
|
||||
</NButton>
|
||||
</div>
|
||||
<GanttCom ref={ganttRef} scale={value.value} dateRange={range.value} types={types.value}/>
|
||||
<GanttCom
|
||||
ref={ganttRef}
|
||||
scale={value.value}
|
||||
dateRange={range.value}
|
||||
types={types.value}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
},
|
||||
|
6
src/views/Gantt/components/MainGanttEdit/index.jsx
Executable file → Normal file
6
src/views/Gantt/components/MainGanttEdit/index.jsx
Executable file → Normal file
@ -21,7 +21,11 @@ export default defineComponent({
|
||||
return () => (
|
||||
<>
|
||||
<div class="flex gap-2">
|
||||
<NDatePicker v-model:value={range.value} type="daterange" clearable />
|
||||
<NDatePicker
|
||||
v-model:value={range.value}
|
||||
type="datetimerange"
|
||||
clearable
|
||||
/>
|
||||
<NRadioGroup v-model:value={value.value} name="radiobuttongroup">
|
||||
<NRadioButton value="hour" label="日" />
|
||||
<NRadioButton value="day" label="月" />
|
||||
|
0
src/views/Gantt/components/SubGantt/index.jsx
Executable file → Normal file
0
src/views/Gantt/components/SubGantt/index.jsx
Executable file → Normal file
0
src/views/Gantt/components/SubGanttEdit/index.jsx
Executable file → Normal file
0
src/views/Gantt/components/SubGanttEdit/index.jsx
Executable file → Normal file
0
src/views/Gantt/components/TaskList/components/NewTask/hooks.ts
Executable file → Normal file
0
src/views/Gantt/components/TaskList/components/NewTask/hooks.ts
Executable file → Normal file
0
src/views/Gantt/components/TaskList/components/NewTask/index.jsx
Executable file → Normal file
0
src/views/Gantt/components/TaskList/components/NewTask/index.jsx
Executable file → Normal file
0
src/views/Gantt/components/TaskList/index.jsx
Executable file → Normal file
0
src/views/Gantt/components/TaskList/index.jsx
Executable file → Normal file
2
src/views/Gantt/index copy.vue
Executable file → Normal file
2
src/views/Gantt/index copy.vue
Executable file → Normal file
@ -519,7 +519,7 @@ onMounted(() => {
|
||||
</div>
|
||||
<div class="z-30 flex flex-1 flex-col gap-2 p-5">
|
||||
<div class="flex gap-2">
|
||||
<n-date-picker v-model:value="range" type="daterange" clearable />
|
||||
<n-date-picker v-model:value="range" type="datetimerange" clearable />
|
||||
<n-radio-group v-model:value="value" name="radiobuttongroup">
|
||||
<n-radio-button value="day" label="日" />
|
||||
<n-radio-button value="month" label="月" />
|
||||
|
2
src/views/Gantt/index.jsx
Executable file → Normal file
2
src/views/Gantt/index.jsx
Executable file → Normal file
@ -141,7 +141,7 @@ export default defineComponent({
|
||||
></NSelect>
|
||||
<NDatePicker
|
||||
v-model:value={range.value}
|
||||
type="daterange"
|
||||
type="datetimerange"
|
||||
clearable
|
||||
/>
|
||||
<NButton type="primary" onClick={searchTreeList}>
|
||||
|
0
src/views/Gantt/index.vue
Executable file → Normal file
0
src/views/Gantt/index.vue
Executable file → Normal file
94
src/views/Gantt0305/components/EventList/components/EventDdConfig.jsx
Executable file
94
src/views/Gantt0305/components/EventList/components/EventDdConfig.jsx
Executable file
@ -0,0 +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>
|
||||
)
|
||||
},
|
||||
})
|
130
src/views/Gantt0305/components/EventList/components/MainEventEdit.jsx
Executable file
130
src/views/Gantt0305/components/EventList/components/MainEventEdit.jsx
Executable file
@ -0,0 +1,130 @@
|
||||
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>
|
||||
)
|
||||
},
|
||||
})
|
155
src/views/Gantt0305/components/EventList/components/SubEventEdit.jsx
Executable file
155
src/views/Gantt0305/components/EventList/components/SubEventEdit.jsx
Executable file
@ -0,0 +1,155 @@
|
||||
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>
|
||||
)
|
||||
},
|
||||
})
|
103
src/views/Gantt0305/components/EventList/components/useEventDdConfig.jsx
Executable file
103
src/views/Gantt0305/components/EventList/components/useEventDdConfig.jsx
Executable file
@ -0,0 +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,
|
||||
}
|
||||
}
|
106
src/views/Gantt0305/components/EventList/hooks.jsx
Executable file
106
src/views/Gantt0305/components/EventList/hooks.jsx
Executable file
@ -0,0 +1,106 @@
|
||||
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,
|
||||
}
|
||||
}
|
296
src/views/Gantt0305/components/EventList/index.jsx
Executable file
296
src/views/Gantt0305/components/EventList/index.jsx
Executable file
@ -0,0 +1,296 @@
|
||||
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} />
|
||||
</>
|
||||
)
|
||||
},
|
||||
})
|
47
src/views/Gantt0305/components/Gantt/components/VRender.jsx
Executable file
47
src/views/Gantt0305/components/Gantt/components/VRender.jsx
Executable 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>
|
||||
)
|
||||
},
|
||||
})
|
608
src/views/Gantt0305/components/Gantt/hooks/gantt.ts
Executable file
608
src/views/Gantt0305/components/Gantt/hooks/gantt.ts
Executable file
@ -0,0 +1,608 @@
|
||||
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/gantt'
|
||||
import { getSon } from '@/api/Gantt'
|
||||
import { useTree } from '@/utils/tree'
|
||||
import * as dayjs from 'dayjs'
|
||||
|
||||
type GanttParams = {
|
||||
route?: any
|
||||
router?: any
|
||||
}
|
||||
let ganttInstance: null | Gantt = null
|
||||
const bgColor = '#1c202c'
|
||||
const headerBgColor = '#33566f22'
|
||||
const textColor = '#65c5e7'
|
||||
const textColorWithOp = '#75fbfd22'
|
||||
|
||||
const { getTimeRangeForTree } = useTree()
|
||||
|
||||
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
|
||||
console.log(subId, ',,,,,')
|
||||
if (subId) {
|
||||
records.value = data.list
|
||||
.reduce((acc, cur) => {
|
||||
if (Array.isArray(cur.children) && cur.children.length > 0) {
|
||||
acc.push(
|
||||
cur.children.map(twoType => {
|
||||
return {
|
||||
...twoType,
|
||||
children:
|
||||
twoType.children &&
|
||||
twoType.children.map(eventItem => {
|
||||
console.log(
|
||||
eventItem.startTime,
|
||||
eventItem.endTime
|
||||
// new Date(eventItem.startTime).getMonth()
|
||||
)
|
||||
return {
|
||||
...eventItem,
|
||||
start: eventItem.startTime,
|
||||
end: eventItem.endTime,
|
||||
}
|
||||
}),
|
||||
parentName: cur.name,
|
||||
childrenLengthForParent: cur.children.length,
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
return acc
|
||||
}, [])
|
||||
.flat()
|
||||
console.log(records.value)
|
||||
|
||||
records.value.length > 0 &&
|
||||
(timeRange.value = getTimeRangeForTree(records.value))
|
||||
} else {
|
||||
// console.log(data.list, '------')
|
||||
records.value = data.list.map(item => {
|
||||
return {
|
||||
...item,
|
||||
children:
|
||||
item.children &&
|
||||
item.children.map(mainEvent => {
|
||||
const date = new Date(mainEvent.startTime)
|
||||
const year = date.getFullYear()
|
||||
const month = date.getMonth()
|
||||
const startOfMonth = new Date(year, month, 1)
|
||||
startOfMonth.setHours(0, 0, 0, 0)
|
||||
|
||||
const lastDayOfMonth = new Date(year, month + 1, 0)
|
||||
const endOfMonth = new Date(
|
||||
year,
|
||||
month,
|
||||
lastDayOfMonth.getDate()
|
||||
)
|
||||
endOfMonth.setHours(23, 59, 59, 9999)
|
||||
// new Date().toISOString()
|
||||
|
||||
// console.log(
|
||||
// mainEvent.startTime,
|
||||
// startOfMonth.toISOString(),
|
||||
// endOfMonth.toISOString()
|
||||
// )
|
||||
|
||||
return {
|
||||
...mainEvent,
|
||||
start: startOfMonth.toISOString(),
|
||||
end: endOfMonth.toISOString(),
|
||||
}
|
||||
}),
|
||||
}
|
||||
})
|
||||
}
|
||||
records.value.length > 0 && ganttInstance?.setRecords(records.value)
|
||||
}
|
||||
}
|
||||
async function renderMainTask(
|
||||
dom: HTMLElement,
|
||||
params: Record<string, string>
|
||||
) {
|
||||
// console.log(subId, 'renderMainTask')
|
||||
await getGanttData(params)
|
||||
const option = getOption()
|
||||
|
||||
// if (ganttInstance) {
|
||||
// ganttInstance.setRecords(records.value)
|
||||
// } else {
|
||||
if (records.value.length === 0) return
|
||||
ganttInstance && ganttInstance?.release()
|
||||
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: 150,
|
||||
// colWidth: 1040,
|
||||
// verticalLine: {
|
||||
// lineColor: textColorWithOp,
|
||||
// lineWidth: 1,
|
||||
// lineDash: [4, 2],
|
||||
// },
|
||||
horizontalLine: {
|
||||
lineColor: textColorWithOp,
|
||||
lineWidth: 1,
|
||||
lineDash: [4, 2],
|
||||
},
|
||||
scales: getTimeScales(subId ? 'day' : 'month'),
|
||||
},
|
||||
minDate: timeRange.value[0],
|
||||
maxDate: timeRange.value[1],
|
||||
markLine: [
|
||||
{
|
||||
date: '2024-01-14T21:12:40',
|
||||
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
|
||||
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: 'parentName',
|
||||
title: '事件分类',
|
||||
width: '120',
|
||||
mergeCell: true,
|
||||
})
|
||||
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: 'start',
|
||||
endDateField: 'end',
|
||||
// progressField: 'progress',
|
||||
barStyle: { width: subId ? 200 : 80 },
|
||||
customLayout: args => {
|
||||
// console.log(args, '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',
|
||||
})
|
||||
|
||||
container.addEventListener('click', async () => {
|
||||
console.log(taskRecord, 'ooooooo')
|
||||
if (!subId) {
|
||||
router.push({
|
||||
path: `/gantt/sub/${taskRecord.id}`,
|
||||
})
|
||||
} else {
|
||||
const { code, data } = await getSon({ id: taskRecord.id })
|
||||
|
||||
if (code === 200) {
|
||||
const showData = {
|
||||
事件名称: data.name,
|
||||
事件时间: `${data.startTime} - ${data.endTime}`,
|
||||
装备型号: data.equipModel,
|
||||
上报站点: data.reportSite,
|
||||
事件描述: data.describe,
|
||||
}
|
||||
window.$dialog.info({
|
||||
title: '子事件详情',
|
||||
class: '!w-[40vw]',
|
||||
content: () => {
|
||||
return h(
|
||||
'div',
|
||||
{ class: 'flex flex-col gap-4 w-full h-full' },
|
||||
[
|
||||
...Object.keys(showData).map(key => {
|
||||
return h('div', { class: 'flex w-full h-full ' }, [
|
||||
h('div', { class: 'w-[120px]' }, key),
|
||||
h(
|
||||
'div',
|
||||
{ class: 'flex-1 text-wrap' },
|
||||
showData[key]
|
||||
),
|
||||
])
|
||||
}),
|
||||
h('div', { class: 'flex w-full h-full' }, [
|
||||
h('div', { class: 'w-[120px]' }, '图片'),
|
||||
h('img', {
|
||||
src: `${window.settings.imgServer}${data.fileUrl}`,
|
||||
}),
|
||||
]),
|
||||
]
|
||||
)
|
||||
},
|
||||
positiveText: '确定',
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
// if (!subId) {
|
||||
// container.addEventListener('click', () => {
|
||||
// console.log(taskRecord, 'ooooooo')
|
||||
// router.push({
|
||||
// path: `/gantt/sub/${taskRecord.id}`,
|
||||
// })
|
||||
// })
|
||||
// }
|
||||
|
||||
if (subId) {
|
||||
const imgUrl = `${window.settings.imgServer}${taskRecord.fileUrl}`
|
||||
// console.log(imgUrl);
|
||||
const image = new Image({
|
||||
image: imgUrl,
|
||||
width: 100,
|
||||
height: 100,
|
||||
x: 10,
|
||||
y: 10,
|
||||
boundsPadding: [0, 0, 10, 0],
|
||||
cursor: 'pointer',
|
||||
})
|
||||
container.add(image)
|
||||
image.addEventListener('click', e => {
|
||||
e.stopPropagation()
|
||||
|
||||
currentImage.value = [imgUrl, 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 start = new Text({
|
||||
text: `${taskRecord.startTime}`,
|
||||
fontSize: 13,
|
||||
fontFamily: 'sans-serif',
|
||||
fill: textColor,
|
||||
boundsPadding: [10, 0, 0, 0],
|
||||
})
|
||||
container.add(start)
|
||||
const end = new Text({
|
||||
text: `${taskRecord.endTime}`,
|
||||
fontSize: 13,
|
||||
fontFamily: 'sans-serif',
|
||||
fill: textColor,
|
||||
boundsPadding: [10, 0, 0, 0],
|
||||
})
|
||||
container.add(end)
|
||||
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
|
88
src/views/Gantt0305/components/Gantt/index.jsx
Executable file
88
src/views/Gantt0305/components/Gantt/index.jsx
Executable file
@ -0,0 +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',
|
||||
},
|
||||
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>
|
||||
</>
|
||||
)
|
||||
},
|
||||
})
|
47
src/views/Gantt0305/components/Gantt1/components/VRender.jsx
Executable file
47
src/views/Gantt0305/components/Gantt1/components/VRender.jsx
Executable 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>
|
||||
)
|
||||
},
|
||||
})
|
452
src/views/Gantt0305/components/Gantt1/hooks/gantt.ts
Executable file
452
src/views/Gantt0305/components/Gantt1/hooks/gantt.ts
Executable 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
|
51
src/views/Gantt0305/components/Gantt1/index.jsx
Executable file
51
src/views/Gantt0305/components/Gantt1/index.jsx
Executable 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>
|
||||
</>
|
||||
)
|
||||
},
|
||||
})
|
47
src/views/Gantt0305/components/Gantt2/components/VRender.jsx
Executable file
47
src/views/Gantt0305/components/Gantt2/components/VRender.jsx
Executable 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>
|
||||
)
|
||||
},
|
||||
})
|
461
src/views/Gantt0305/components/Gantt2/hooks/gantt.ts
Executable file
461
src/views/Gantt0305/components/Gantt2/hooks/gantt.ts
Executable 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
|
66
src/views/Gantt0305/components/Gantt2/index.jsx
Executable file
66
src/views/Gantt0305/components/Gantt2/index.jsx
Executable 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>
|
||||
</>
|
||||
)
|
||||
},
|
||||
})
|
389
src/views/Gantt0305/components/GanttEdit/hooks/ganttEdit.ts
Executable file
389
src/views/Gantt0305/components/GanttEdit/hooks/ganttEdit.ts
Executable file
@ -0,0 +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
|
29
src/views/Gantt0305/components/GanttEdit/index.jsx
Executable file
29
src/views/Gantt0305/components/GanttEdit/index.jsx
Executable file
@ -0,0 +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>
|
||||
},
|
||||
})
|
75
src/views/Gantt0305/components/MainGantt/index.jsx
Executable file
75
src/views/Gantt0305/components/MainGantt/index.jsx
Executable file
@ -0,0 +1,75 @@
|
||||
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}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
},
|
||||
})
|
48
src/views/Gantt0305/components/MainGantt1/index.jsx
Executable file
48
src/views/Gantt0305/components/MainGantt1/index.jsx
Executable 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} />
|
||||
</>
|
||||
)
|
||||
},
|
||||
})
|
83
src/views/Gantt0305/components/MainGantt2/index.jsx
Executable file
83
src/views/Gantt0305/components/MainGantt2/index.jsx
Executable 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}/>
|
||||
</>
|
||||
)
|
||||
},
|
||||
})
|
45
src/views/Gantt0305/components/MainGanttEdit/index.jsx
Executable file
45
src/views/Gantt0305/components/MainGanttEdit/index.jsx
Executable file
@ -0,0 +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} />
|
||||
</>
|
||||
)
|
||||
},
|
||||
})
|
33
src/views/Gantt0305/components/SubGantt/index.jsx
Executable file
33
src/views/Gantt0305/components/SubGantt/index.jsx
Executable file
@ -0,0 +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} />
|
||||
</>
|
||||
)
|
||||
},
|
||||
})
|
33
src/views/Gantt0305/components/SubGanttEdit/index.jsx
Executable file
33
src/views/Gantt0305/components/SubGanttEdit/index.jsx
Executable file
@ -0,0 +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} />
|
||||
</>
|
||||
)
|
||||
},
|
||||
})
|
15
src/views/Gantt0305/components/TaskList/components/NewTask/hooks.ts
Executable file
15
src/views/Gantt0305/components/TaskList/components/NewTask/hooks.ts
Executable file
@ -0,0 +1,15 @@
|
||||
const showNewTask = ref(false)
|
||||
|
||||
const curTaskData = ref({
|
||||
name: '',
|
||||
description: '',
|
||||
checkedEvent: [],
|
||||
})
|
||||
const useTask = () => {
|
||||
return {
|
||||
showNewTask,
|
||||
curTaskData,
|
||||
}
|
||||
}
|
||||
|
||||
export default useTask
|
118
src/views/Gantt0305/components/TaskList/components/NewTask/index.jsx
Executable file
118
src/views/Gantt0305/components/TaskList/components/NewTask/index.jsx
Executable file
@ -0,0 +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>
|
||||
)
|
||||
},
|
||||
})
|
172
src/views/Gantt0305/components/TaskList/index.jsx
Executable file
172
src/views/Gantt0305/components/TaskList/index.jsx
Executable file
@ -0,0 +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}
|
||||
/>
|
||||
)
|
||||
},
|
||||
})
|
531
src/views/Gantt0305/index copy.vue
Executable file
531
src/views/Gantt0305/index copy.vue
Executable file
@ -0,0 +1,531 @@
|
||||
<script setup>
|
||||
import HeaderCom from '../Content/components/Header/index.vue'
|
||||
import { Gantt, tools } from '@visactor/vtable-gantt'
|
||||
|
||||
// import * as VTableGantt from '@visactor/vtable-gantt';
|
||||
import * as VRender from '@visactor/vtable/es/vrender'
|
||||
const checked = ref({
|
||||
id: 'test',
|
||||
|
||||
width: 20,
|
||||
height: 20,
|
||||
checked: false,
|
||||
})
|
||||
let ganttInstance
|
||||
const barColors0 = [
|
||||
'#aecde6',
|
||||
'#c6a49a',
|
||||
'#ffb582',
|
||||
'#eec1de',
|
||||
'#b3d9b3',
|
||||
'#cccccc',
|
||||
'#e59a9c',
|
||||
'#d9d1a5',
|
||||
'#c9bede',
|
||||
]
|
||||
const barColors = [
|
||||
'#1f77b4',
|
||||
'#8c564b',
|
||||
'#ff7f0e',
|
||||
'#e377c2',
|
||||
'#2ca02c',
|
||||
'#7f7f7f',
|
||||
'#d62728',
|
||||
'#bcbd22',
|
||||
'#9467bd',
|
||||
]
|
||||
const records = [
|
||||
{
|
||||
id: 1,
|
||||
title: 'Project Task 1',
|
||||
developer: 'bear.xiong',
|
||||
avatar:
|
||||
'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg',
|
||||
start: '2024-07-24',
|
||||
end: '2024-07-26',
|
||||
progress: 31,
|
||||
priority: 'P0',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: 'Project Task 2',
|
||||
developer: 'wolf.lang',
|
||||
avatar:
|
||||
'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg',
|
||||
start: '07/25/2024',
|
||||
end: '07/28/2024',
|
||||
progress: 60,
|
||||
priority: 'P0',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: 'Project Task 3',
|
||||
developer: 'rabbit.tu',
|
||||
avatar:
|
||||
'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg',
|
||||
start: '2024-07-28',
|
||||
end: '2024-08-01',
|
||||
progress: 100,
|
||||
priority: 'P1',
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: 'Project Task 4',
|
||||
developer: 'cat.mao',
|
||||
avatar:
|
||||
'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg',
|
||||
start: '2024-07-31',
|
||||
end: '2024-08-03',
|
||||
progress: 31,
|
||||
priority: 'P0',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: 'Project Task 5',
|
||||
developer: 'bird.niao',
|
||||
avatar:
|
||||
'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bird.jpeg',
|
||||
start: '2024-08-02',
|
||||
end: '2024-08-04',
|
||||
progress: 60,
|
||||
priority: 'P0',
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
title: 'Project Task 5',
|
||||
developer: 'bird.niao',
|
||||
avatar:
|
||||
'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bird.jpeg',
|
||||
start: '2024-08-02',
|
||||
end: '2024-08-04',
|
||||
progress: 60,
|
||||
priority: 'P0',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: 'Project Task 6',
|
||||
developer: 'flower.hua',
|
||||
avatar:
|
||||
'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg',
|
||||
start: '2024-08-03',
|
||||
end: '2024-08-10',
|
||||
progress: 100,
|
||||
priority: 'P1',
|
||||
},
|
||||
]
|
||||
const columns = [
|
||||
{
|
||||
field: 'title',
|
||||
title: 'TASK',
|
||||
width: '200',
|
||||
mergeCell: true,
|
||||
|
||||
headerStyle: {
|
||||
textAlign: 'center',
|
||||
fontSize: 20,
|
||||
fontWeight: 'bold',
|
||||
color: 'white',
|
||||
bgColor: '#1c202c',
|
||||
},
|
||||
style: {
|
||||
bgColor: '#1c202c',
|
||||
},
|
||||
customLayout: args => {
|
||||
const { table, row, col, rect } = args
|
||||
const taskRecord = table.getCellOriginRecord(col, row)
|
||||
const { height, width } = rect ?? table.getCellRect(col, row)
|
||||
const container = new VRender.Group({
|
||||
y: 10,
|
||||
x: 20,
|
||||
height: height - 20,
|
||||
width: width - 40,
|
||||
fill: 'white',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
cornerRadius: 30,
|
||||
})
|
||||
|
||||
const developer = new VRender.Text({
|
||||
text: taskRecord.developer,
|
||||
fontSize: 16,
|
||||
fontFamily: 'sans-serif',
|
||||
fill: barColors[args.row],
|
||||
fontWeight: 'bold',
|
||||
maxLineWidth: width - 120,
|
||||
boundsPadding: [10, 0, 0, 0],
|
||||
alignSelf: 'center',
|
||||
})
|
||||
container.add(developer)
|
||||
|
||||
const days = new VRender.Text({
|
||||
text: `${tools.formatDate(
|
||||
new Date(taskRecord.start),
|
||||
'mm/dd'
|
||||
)}-${tools.formatDate(new Date(taskRecord.end), 'mm/dd')}`,
|
||||
fontSize: 12,
|
||||
fontFamily: 'sans-serif',
|
||||
fontWeight: 'bold',
|
||||
fill: 'black',
|
||||
boundsPadding: [10, 0, 0, 0],
|
||||
alignSelf: 'center',
|
||||
})
|
||||
container.add(days)
|
||||
|
||||
return {
|
||||
rootContainer: container,
|
||||
expectedWidth: 160,
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
const option = {
|
||||
records,
|
||||
taskListTable: {
|
||||
columns,
|
||||
tableWidth: 'auto',
|
||||
theme: {
|
||||
underlayBackgroundColor: '#1c202c',
|
||||
headerStyle: {
|
||||
borderColor: '#e1e4e8',
|
||||
borderLineWidth: 0,
|
||||
fontSize: 18,
|
||||
fontWeight: 'bold',
|
||||
color: 'red',
|
||||
// bgColor: '#EEF1F5'
|
||||
},
|
||||
bodyStyle: {
|
||||
borderColor: '#e1e4e8',
|
||||
borderLineWidth: 0,
|
||||
fontSize: 16,
|
||||
color: '#4D4D4D',
|
||||
bgColor: '#FFF',
|
||||
},
|
||||
},
|
||||
},
|
||||
frame: {
|
||||
outerFrameStyle: {
|
||||
borderLineWidth: 0,
|
||||
borderColor: 'red',
|
||||
cornerRadius: 0,
|
||||
},
|
||||
// verticalSplitLineHighlight: {
|
||||
// lineColor: 'green',
|
||||
// lineWidth: 3
|
||||
// }
|
||||
},
|
||||
grid: {
|
||||
backgroundColor: '#1c202c',
|
||||
// horizontalLine: {
|
||||
// lineWidth: 2,
|
||||
// lineColor: '#d5d9ee',
|
||||
// },
|
||||
verticalLine: {
|
||||
lineWidth: 1,
|
||||
lineColor: '#d5d9ee',
|
||||
lineDash: [8, 4],
|
||||
},
|
||||
},
|
||||
taskList: {
|
||||
backgroundColor: '#1c202c',
|
||||
headerStyle: {
|
||||
borderColor: '#e1e4e8',
|
||||
borderLineWidth: 0,
|
||||
fontSize: 18,
|
||||
fontWeight: 'bold',
|
||||
color: 'red',
|
||||
},
|
||||
},
|
||||
headerRowHeight: 60,
|
||||
rowHeight: 100,
|
||||
taskBar: {
|
||||
resizable: false,
|
||||
moveable: false,
|
||||
startDateField: 'start',
|
||||
endDateField: 'end',
|
||||
progressField: 'progress',
|
||||
barStyle: { width: 60 },
|
||||
customLayout: args => {
|
||||
const colorLength = barColors.length
|
||||
const {
|
||||
width,
|
||||
height,
|
||||
index,
|
||||
startDate,
|
||||
endDate,
|
||||
taskDays,
|
||||
progress,
|
||||
taskRecord,
|
||||
ganttInstance,
|
||||
} = args
|
||||
const container = new VRender.Group({
|
||||
width,
|
||||
height,
|
||||
// cornerRadius: 30,
|
||||
fill: {
|
||||
gradient: 'linear',
|
||||
x0: 0,
|
||||
y0: 0,
|
||||
x1: 1,
|
||||
y1: 0,
|
||||
stops: [
|
||||
{
|
||||
offset: 0,
|
||||
color: barColors0[index % colorLength],
|
||||
},
|
||||
{
|
||||
offset: 0.5,
|
||||
color: barColors[index % colorLength],
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: barColors0[index % colorLength],
|
||||
},
|
||||
],
|
||||
},
|
||||
display: 'flex',
|
||||
// flexDirection: 'column',
|
||||
flexWrap: 'nowrap',
|
||||
})
|
||||
const containerLeft = new VRender.Group({
|
||||
height: 60,
|
||||
width: 60,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-around',
|
||||
// fill: 'red'
|
||||
})
|
||||
container.add(containerLeft)
|
||||
|
||||
const avatar = new VRender.Image({
|
||||
width: 50,
|
||||
height: 50,
|
||||
image: taskRecord.avatar,
|
||||
cornerRadius: 25,
|
||||
})
|
||||
containerLeft.add(avatar)
|
||||
const containerCenter = new VRender.Group({
|
||||
height: 60,
|
||||
width: width - (width >= 120 ? 120 : 60),
|
||||
display: 'flex',
|
||||
// flexDirection: 'column',
|
||||
// alignItems: 'left'
|
||||
})
|
||||
container.add(containerCenter)
|
||||
const checkbox = new VRender.CheckBox(checked.value)
|
||||
container.add(checkbox)
|
||||
|
||||
checkbox.addEventListener('click', event => {
|
||||
console.log(event, checkbox, 'event')
|
||||
})
|
||||
const developer = new VRender.Text({
|
||||
text: taskRecord.developer,
|
||||
fontSize: 16,
|
||||
fontFamily: 'sans-serif',
|
||||
fill: 'white',
|
||||
fontWeight: 'bold',
|
||||
maxLineWidth: width - (width >= 120 ? 120 : 60),
|
||||
boundsPadding: [10, 0, 0, 0],
|
||||
})
|
||||
containerCenter.add(developer)
|
||||
|
||||
const days = new VRender.Text({
|
||||
text: `${taskDays}天`,
|
||||
fontSize: 13,
|
||||
fontFamily: 'sans-serif',
|
||||
fill: 'white',
|
||||
boundsPadding: [10, 0, 0, 0],
|
||||
})
|
||||
containerCenter.add(days)
|
||||
|
||||
if (width >= 120) {
|
||||
const containerRight = new VRender.Group({
|
||||
cornerRadius: 20,
|
||||
fill: 'white',
|
||||
height: 40,
|
||||
width: 40,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center', // 垂直方向居中对齐
|
||||
boundsPadding: [10, 0, 0, 0],
|
||||
})
|
||||
container.add(containerRight)
|
||||
|
||||
const progressText = new VRender.Text({
|
||||
text: `${progress}%`,
|
||||
fontSize: 12,
|
||||
fontFamily: 'sans-serif',
|
||||
fill: 'black',
|
||||
alignSelf: 'center',
|
||||
fontWeight: 'bold',
|
||||
maxLineWidth: (width - 60) / 2,
|
||||
boundsPadding: [0, 0, 0, 0],
|
||||
})
|
||||
containerRight.add(progressText)
|
||||
}
|
||||
return {
|
||||
rootContainer: container,
|
||||
// renderDefaultBar: true
|
||||
// renderDefaultText: true
|
||||
}
|
||||
},
|
||||
hoverBarStyle: {
|
||||
cornerRadius: 0,
|
||||
barOverlayColor: '#f0f0',
|
||||
},
|
||||
},
|
||||
timelineHeader: {
|
||||
backgroundColor: '#1c202c',
|
||||
colWidth: 100,
|
||||
// verticalLine: {
|
||||
// lineColor: 'red',
|
||||
// lineWidth: 1,
|
||||
// lineDash: [4, 2]
|
||||
// },
|
||||
// horizontalLine: {
|
||||
// lineColor: 'green',
|
||||
// lineWidth: 1,
|
||||
// lineDash: [4, 2]
|
||||
// },
|
||||
scales: [
|
||||
{
|
||||
unit: 'day',
|
||||
step: 1,
|
||||
format(date) {
|
||||
return date.dateIndex.toString()
|
||||
},
|
||||
customLayout: args => {
|
||||
const colorLength = barColors.length
|
||||
const {
|
||||
width,
|
||||
height,
|
||||
index,
|
||||
startDate,
|
||||
endDate,
|
||||
days,
|
||||
dateIndex,
|
||||
title,
|
||||
ganttInstance,
|
||||
} = args
|
||||
console.log(width, height, 'height')
|
||||
const container = new VRender.Group({
|
||||
width,
|
||||
height,
|
||||
fill: '#1c202c',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'nowrap',
|
||||
})
|
||||
const containerLeft = new VRender.Group({
|
||||
height,
|
||||
width: 30,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-around',
|
||||
// fill: 'red'
|
||||
})
|
||||
container.add(containerLeft)
|
||||
|
||||
const avatar = new VRender.Image({
|
||||
width: 20,
|
||||
height: 30,
|
||||
image:
|
||||
'<svg t="1724675965803" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4299" width="200" height="200"><path d="M53.085678 141.319468C23.790257 141.319468 0 165.035326 0 194.34775L0 918.084273C0 947.295126 23.796789 971.112572 53.085678 971.112572L970.914322 971.112572C1000.209743 971.112572 1024 947.396696 1024 918.084273L1024 194.34775C1024 165.136896 1000.203211 141.319468 970.914322 141.319468L776.827586 141.319468 812.137931 176.629813 812.137931 88.275862C812.137931 68.774506 796.328942 52.965517 776.827586 52.965517 757.32623 52.965517 741.517241 68.774506 741.517241 88.275862L741.517241 176.629813 741.517241 211.940158 776.827586 211.940158 970.914322 211.940158C961.186763 211.940158 953.37931 204.125926 953.37931 194.34775L953.37931 918.084273C953.37931 908.344373 961.25643 900.491882 970.914322 900.491882L53.085678 900.491882C62.813237 900.491882 70.62069 908.306097 70.62069 918.084273L70.62069 194.34775C70.62069 204.087649 62.74357 211.940158 53.085678 211.940158L247.172414 211.940158C266.67377 211.940158 282.482759 196.131169 282.482759 176.629813 282.482759 157.128439 266.67377 141.319468 247.172414 141.319468L53.085678 141.319468ZM211.862069 176.629813C211.862069 196.131169 227.671058 211.940158 247.172414 211.940158 266.67377 211.940158 282.482759 196.131169 282.482759 176.629813L282.482759 88.275862C282.482759 68.774506 266.67377 52.965517 247.172414 52.965517 227.671058 52.965517 211.862069 68.774506 211.862069 88.275862L211.862069 176.629813ZM1024 353.181537 1024 317.871192 988.689655 317.871192 35.310345 317.871192 0 317.871192 0 353.181537 0 441.457399C0 460.958755 15.808989 476.767744 35.310345 476.767744 54.811701 476.767744 70.62069 460.958755 70.62069 441.457399L70.62069 353.181537 35.310345 388.491882 988.689655 388.491882 953.37931 353.181537 953.37931 441.457399C953.37931 460.958755 969.188299 476.767744 988.689655 476.767744 1008.191011 476.767744 1024 460.958755 1024 441.457399L1024 353.181537ZM776.937913 582.62069C796.439287 582.62069 812.248258 566.811701 812.248258 547.310345 812.248258 527.808989 796.439287 512 776.937913 512L247.172414 512C227.671058 512 211.862069 527.808989 211.862069 547.310345 211.862069 566.811701 227.671058 582.62069 247.172414 582.62069L776.937913 582.62069ZM247.172414 688.551724C227.671058 688.551724 211.862069 704.360713 211.862069 723.862069 211.862069 743.363425 227.671058 759.172414 247.172414 759.172414L600.386189 759.172414C619.887563 759.172414 635.696534 743.363425 635.696534 723.862069 635.696534 704.360713 619.887563 688.551724 600.386189 688.551724L247.172414 688.551724ZM776.827586 211.940158 741.517241 176.629813 741.517241 247.328574C741.517241 266.829948 757.32623 282.638919 776.827586 282.638919 796.328942 282.638919 812.137931 266.829948 812.137931 247.328574L812.137931 176.629813 812.137931 141.319468 776.827586 141.319468 247.172414 141.319468C227.671058 141.319468 211.862069 157.128439 211.862069 176.629813 211.862069 196.131169 227.671058 211.940158 247.172414 211.940158L776.827586 211.940158ZM282.482759 176.629813C282.482759 157.128439 266.67377 141.319468 247.172414 141.319468 227.671058 141.319468 211.862069 157.128439 211.862069 176.629813L211.862069 247.328574C211.862069 266.829948 227.671058 282.638919 247.172414 282.638919 266.67377 282.638919 282.482759 266.829948 282.482759 247.328574L282.482759 176.629813Z" fill="#389BFF" p-id="4300"></path></svg>',
|
||||
})
|
||||
containerLeft.add(avatar)
|
||||
|
||||
const containerCenter = new VRender.Group({
|
||||
height,
|
||||
width: width - 30,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
// alignItems: 'left'
|
||||
})
|
||||
container.add(containerCenter)
|
||||
const dayNumber = new VRender.Text({
|
||||
text: String(dateIndex).padStart(2, '0'),
|
||||
fontSize: 20,
|
||||
fontWeight: 'bold',
|
||||
fontFamily: 'sans-serif',
|
||||
fill: 'white',
|
||||
textAlign: 'right',
|
||||
maxLineWidth: width - 30,
|
||||
boundsPadding: [15, 0, 0, 0],
|
||||
})
|
||||
containerCenter.add(dayNumber)
|
||||
|
||||
const weekDay = new VRender.Text({
|
||||
text: tools.getWeekday(startDate, 'short').toLocaleUpperCase(),
|
||||
fontSize: 12,
|
||||
fontFamily: 'sans-serif',
|
||||
fill: 'white',
|
||||
boundsPadding: [0, 0, 0, 0],
|
||||
})
|
||||
containerCenter.add(weekDay)
|
||||
return {
|
||||
rootContainer: container,
|
||||
//renderDefaultText: true
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
minDate: '2024-07-20',
|
||||
maxDate: '2024-08-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: '#1c202c',
|
||||
}
|
||||
|
||||
const range = ref()
|
||||
const value = ref('month')
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
ganttInstance = new Gantt(document.getElementById('tableContainer'), option)
|
||||
window['ganttInstance'] = ganttInstance
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col bg-[#1c202c] w-h-full">
|
||||
<div class="relative h-[60px]">
|
||||
<header-com />
|
||||
</div>
|
||||
<div class="z-30 flex flex-1 flex-col gap-2 p-5">
|
||||
<div class="flex gap-2">
|
||||
<n-date-picker v-model:value="range" type="daterange" clearable />
|
||||
<n-radio-group v-model:value="value" name="radiobuttongroup">
|
||||
<n-radio-button value="day" label="日" />
|
||||
<n-radio-button value="month" label="月" />
|
||||
</n-radio-group>
|
||||
</div>
|
||||
<div id="tableContainer" class="bg-[#1c202c] w-h-full"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
179
src/views/Gantt0305/index.jsx
Executable file
179
src/views/Gantt0305/index.jsx
Executable file
@ -0,0 +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'
|
||||
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>
|
||||
)
|
||||
},
|
||||
})
|
7
src/views/Gantt0305/index.vue
Executable file
7
src/views/Gantt0305/index.vue
Executable file
@ -0,0 +1,7 @@
|
||||
<script setup>
|
||||
import TestJsx from './index.jsx'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<test-jsx />
|
||||
</template>
|
Loading…
Reference in New Issue
Block a user