Gitlab@Informatics

Skip to content
Snippets Groups Projects
Commit 177868d1 authored by 65160206's avatar 65160206
Browse files

fix merge

parent 06081571
No related branches found
No related tags found
No related merge requests found
...@@ -40,7 +40,6 @@ ...@@ -40,7 +40,6 @@
<label>Produced Quantity:</label> <label>Produced Quantity:</label>
<input v-model.number="form.producedQuantity" type="number" required /> <input v-model.number="form.producedQuantity" type="number" required />
</div> </div>
<!-- ปุ่ม Add หรือ Save Changes --> <!-- ปุ่ม Add หรือ Save Changes -->
<div class="dialog-buttons"> <div class="dialog-buttons">
<button type="submit" class="primary-btn"> <button type="submit" class="primary-btn">
...@@ -53,17 +52,41 @@ ...@@ -53,17 +52,41 @@
</div> </div>
</template> </template>
<script setup lang="ts">
import { reactive, ref, watch } from 'vue'
<script> interface QueueItem {
export default { machineID: string
name: 'AddQueueDialog', orderID: number | null
props: { pageNumber: number | null
visible: Boolean, startTime: string
queueItem: Object, // รับ queueItem ถ้ามี finishTime: string
}, status: string
data() { bottleSize: string
return { producedQuantity: number | null
form: { queueID?: number
}
interface Machine {
machineID: string
name: string
}
// Props
const props = defineProps<{
visible: boolean
queueItem?: QueueItem
}>()
// Emits
const emit = defineEmits<{
(e: 'close'): void
(e: 'edit', payload: QueueItem): void
(e: 'add', payload: QueueItem): void
}>()
// Reactive form state
const form = reactive<QueueItem>({
machineID: '', machineID: '',
orderID: null, orderID: null,
pageNumber: null, pageNumber: null,
...@@ -72,59 +95,63 @@ export default { ...@@ -72,59 +95,63 @@ export default {
status: '', status: '',
bottleSize: '', bottleSize: '',
producedQuantity: null, producedQuantity: null,
}, })
machines: [
// Static machines list
const machines: Machine[] = [
{ machineID: 'MC1', name: 'เครื่องเป่าขวด' }, { machineID: 'MC1', name: 'เครื่องเป่าขวด' },
{ machineID: 'MC2', name: 'เครื่องเป่าขวด2' }, { machineID: 'MC2', name: 'เครื่องเป่าขวด2' },
{ machineID: 'MC3', name: 'เครื่องสวมฉลาก' }, { machineID: 'MC3', name: 'เครื่องสวมฉลาก' },
{ machineID: 'MC4', name: 'เครื่องสวมฉลาก2' }, { machineID: 'MC4', name: 'เครื่องสวมฉลาก2' },
{ machineID: 'MC5', name: 'เครื่องบรรจุ+แพ็ค' }, { machineID: 'MC5', name: 'เครื่องบรรจุ+แพ็ค' },
], ]
isEditing: false, // เช็คว่าเป็นโหมด Edit หรือไม่
}; // Track editing mode
}, const isEditing = ref(false)
watch: {
queueItem(newItem) { // Functions
if (newItem) { function resetForm() {
this.form = { ...newItem }; // ถ้าเป็น Edit โยนค่ามาใส่ฟอร์ม form.machineID = ''
this.isEditing = true; form.orderID = null
form.pageNumber = null
form.startTime = ''
form.finishTime = ''
form.status = ''
form.bottleSize = ''
form.producedQuantity = null
}
function closeDialog() {
console.log("Closing dialog")
emit("close")
resetForm()
}
function handleSubmit() {
if (isEditing.value) {
emit("edit", { ...form })
} else { } else {
this.resetForm(); emit("add", { ...form, queueID: Date.now() })
this.isEditing = false;
} }
}, closeDialog()
}, }
methods: {
closeDialog() { // Watch for changes on queueItem prop to toggle edit mode
console.log("Closing dialog"); // Debugging watch(
this.$emit("close"); () => props.queueItem,
this.resetForm(); (newItem) => {
}, if (newItem) {
handleSubmit() { Object.assign(form, newItem)
if (this.isEditing) { isEditing.value = true
this.$emit("edit", { ...this.form }); // ส่งข้อมูลไปอัปเดต Queue
} else { } else {
this.$emit("add", { ...this.form, queueID: Date.now() }); // กรณีเพิ่มใหม่ต้องให้ queueID ไม่ซ้ำ resetForm()
isEditing.value = false
} }
this.closeDialog();
}, },
resetForm() { { immediate: true }
this.form = { )
machineID: '',
orderID: null,
pageNumber: null,
startTime: '',
finishTime: '',
status: '',
bottleSize: '',
producedQuantity: null,
};
},
},
};
</script> </script>
<style scoped> <style scoped>
.add-queue-dialog-overlay { .add-queue-dialog-overlay {
position: fixed; position: fixed;
......
...@@ -5,8 +5,9 @@ ...@@ -5,8 +5,9 @@
<GanttCalendar v-model:modelValue="selectedDate" /> <GanttCalendar v-model:modelValue="selectedDate" />
<!-- ปุ่ม Make a queue พร้อม event listener --> <!-- ปุ่ม Make a queue พร้อม event listener -->
<MakequeueBtn @updateQueue="handleUpdateQueue" /> <MakequeueBtn @click="queueStore.makeQueue()" />
</div> </div>
<!-- Gantt Chart UI --> <!-- Gantt Chart UI -->
<div class="gantt-chart"> <div class="gantt-chart">
<!-- Header: Time Scale --> <!-- Header: Time Scale -->
...@@ -21,41 +22,26 @@ ...@@ -21,41 +22,26 @@
<!-- Rows: เครื่องจักรแต่ละตัว --> <!-- Rows: เครื่องจักรแต่ละตัว -->
<div class="rows"> <div class="rows">
<div <div v-for="machine in machines" :key="machine.machineID" class="row" @dragover.prevent="onDragOver($event)"
v-for="machine in machines" @drop="onDrop($event, machine.machineID)">
:key="machine.id"
class="row"
@dragover.prevent="onDragOver($event)"
@drop="onDrop($event, machine.machineID)"
>
<div class="machine-label"> <div class="machine-label">
{{ machine.name }} {{ machine.machineID }}
</div> </div>
<div class="row-timeline"> <div class="row-timeline">
<!-- เส้นแนวตั้ง (Grid Lines) --> <!-- เส้นแนวตั้ง (Grid Lines) -->
<div <div v-for="hour in hours" :key="'line-' + hour" class="vertical-line" :style="getLineStyle(hour)"></div>
v-for="hour in hours"
:key="'line-' + hour"
class="vertical-line"
:style="getLineStyle(hour)"
></div>
<!-- แสดง Queue ที่ตรงกับวันที่ (selectedDate), Page, และ Machine --> <!-- แสดง Queue ที่ตรงกับวันที่ (selectedDate), Page, และ Machine -->
<div <div v-for="item in filteredQueue(machine.machineID)" :key="item.queueID" class="order"
v-for="item in filteredQueue(machine.machineID)"
:key="item.queueID"
class="order"
:class="{ 'faded': draggingQueue && draggingQueue.queueID === item.queueID }" :class="{ 'faded': draggingQueue && draggingQueue.queueID === item.queueID }"
:style="getQueueStyle(item)" :style="getQueueStyle(item)">
>
<!-- Handle สำหรับ Resize ด้านซ้าย --> <!-- Handle สำหรับ Resize ด้านซ้าย -->
<div class="resize-handle left" @mousedown="onResizeStart($event, item, 'left')"></div> <div class="resize-handle left" @mousedown="onResizeStart($event, item, 'left')"></div>
<!-- ส่วนกลางของ Order ใช้สำหรับลาก และเปิด Dialog เมื่อคลิก --> <!-- ส่วนกลางของ Order ใช้สำหรับลาก และเปิด Dialog เมื่อคลิก -->
<div class="order-content" draggable="true" <div class="order-content" draggable="true" @dragstart="onDragStart($event, item)"
@dragstart="onDragStart($event, item)" @dragend="onDragEnd($event, item)" @click.stop="openQueueDialog(item)">
@dragend="onDragEnd($event, item)"
@click.stop="openQueueDialog(item)">
{{ item.orderID }} ({{ getTimeString(item.startTime) }} - {{ getTimeString(item.finishTime) }}) {{ item.orderID }} ({{ getTimeString(item.startTime) }} - {{ getTimeString(item.finishTime) }})
</div> </div>
...@@ -68,7 +54,8 @@ ...@@ -68,7 +54,8 @@
<!-- Ghost Queue (ขณะลาก) --> <!-- Ghost Queue (ขณะลาก) -->
<div v-if="ghostQueue" class="drag-ghost" :style="ghostStyle"> <div v-if="ghostQueue" class="drag-ghost" :style="ghostStyle">
{{ ghostQueue.orderID }} ({{ getTimeString(ghostQueue.startTime) }} - {{ getTimeString(ghostQueue.finishTime) }}) {{ ghostQueue.orderID }} ({{ getTimeString(ghostQueue.startTime) }} - {{ getTimeString(ghostQueue.finishTime)
}})
</div> </div>
<v-divider :thickness="7"></v-divider> <v-divider :thickness="7"></v-divider>
...@@ -99,96 +86,66 @@ ...@@ -99,96 +86,66 @@
</div> </div>
<!-- AddQueueDialog (สำหรับเพิ่ม/แก้ไข) --> <!-- AddQueueDialog (สำหรับเพิ่ม/แก้ไข) -->
<AddQueueDialog <AddQueueDialog :visible="showAddDialog" :queueItem="selectedQueueItem" @close="closeAddQueueDialog"
:visible="showAddDialog" @add="handleAddQueue" @edit="handleEditQueue" />
:queueItem="selectedQueueItem"
@close="closeAddQueueDialog"
@add="handleAddQueue"
@edit="handleEditQueue"
/>
</div> </div>
<!-- Order Dialog (สำหรับดูรายละเอียดเพิ่มเติม) --> <!-- Order Dialog (สำหรับดูรายละเอียดเพิ่มเติม) -->
<OrderDialog <OrderDialog v-if="selectedQueueItem && !showAddDialog" :queueItem="selectedQueueItem"
v-if="selectedQueueItem && !showAddDialog" :color="getQueueColor(selectedQueueItem.orderID)" @close="closeQueueDialog" @edit="openAddQueueDialogForEdit"
:queueItem="selectedQueueItem" @delete="deleteQueueItem" />
:color="getQueueColor(selectedQueueItem.orderID)"
@close="closeQueueDialog"
@edit="openAddQueueDialogForEdit"
@delete="deleteQueueItem"
/>
</div> </div>
</div> </div>
</template> </template>
<script> <script lang="ts" setup>
import { ref, reactive, computed, onMounted, onBeforeUnmount, watch } from 'vue';
import GanttCalendar from './GanttCalendar.vue'; import GanttCalendar from './GanttCalendar.vue';
import OrderDialog from './OrderDialog.vue'; import OrderDialog from './OrderDialog.vue';
import AddQueueDialog from './AddQueueDialog.vue'; import AddQueueDialog from './AddQueueDialog.vue';
import MakequeueBtn from './MakequeueBtn.vue'; import MakequeueBtn from './MakequeueBtn.vue';
import './ganttChart.css'; import './ganttChart.css';
import { useQueueStore } from '@/stores/queue';
import type { QueueItem } from '@/types/QueueItem';
export default { interface Machine {
name: 'GanttChart', machineID: string;
components: { name: string;
MakequeueBtn, }
GanttCalendar,
OrderDialog,
AddQueueDialog,
},
data() {
return {
showAddDialog: false,
// ค่าจาก Calendar
selectedDate: '1/1/2025',
// กำหนดช่วงเวลาใน Gantt
startTime: '08:00',
endTime: '17:00',
// รายการเครื่องจักร
machines: [ const queueStore = useQueueStore();
// NOTE: ถ้าต้องการโหลดข้อมูลทันทีที่เข้าหน้านี้ ให้ un-comment ด้านล่าง
onMounted(() => {
});
// State
const showAddDialog = ref(false);
const selectedDate = ref('1/1/2025');
const startTime = ref('08:00');
const endTime = ref('17:00');
const machines = ref<Machine[]>([
{ machineID: 'MC1', name: 'เครื่องเป่าขวด' }, { machineID: 'MC1', name: 'เครื่องเป่าขวด' },
{ machineID: 'MC2', name: 'เครื่องเป่าขวด2' }, { machineID: 'MC2', name: 'เครื่องเป่าขวด2' },
{ machineID: 'MC3', name: 'เครื่องสวมฉลาก' }, { machineID: 'MC3', name: 'เครื่องสวมฉลาก' },
{ machineID: 'MC4', name: 'เครื่องบรรจุ+แพ็ค' }, { machineID: 'MC4', name: 'เครื่องบรรจุ+แพ็ค' },
], ]);
// รายการ Queue เริ่มต้น (สามารถเปลี่ยนแปลงได้) // ดึง ref ของ QueueItem[] จาก Store
Queue: [ const Queues = computed(() => queueStore.Queues.value);
{
queueID: 1, // Drag/Drop & Resize
machineID: 'MC1', const draggingQueue = ref<QueueItem | null>(null);
orderID: 5, const dragOffset = ref(0);
pageNumber: 1, const resizingQueue = ref<QueueItem | null>(null);
startTime: '1/1/2025 09:00', const resizeDirection = ref<string | null>(null);
finishTime: '1/1/2025 11:00',
status: 'Process', const ghostQueue = ref<QueueItem | null>(null);
bottleSize: '600ml', const ghostStyle = reactive({
producedQuantity: 400,
},
{
queueID: 2,
machineID: 'MC2',
orderID: 1,
pageNumber: 1,
startTime: '1/1/2025 13:00',
finishTime: '1/1/2025 15:00',
status: 'Waiting',
bottleSize: '500ml',
producedQuantity: 200,
},
],
// Drag/Drop และ Resize
draggingQueue: null,
dragOffset: 0,
resizingQueue: null,
resizeDirection: null,
// Ghost Queue (ขณะลาก)
ghostQueue: null,
ghostStyle: {
position: 'fixed', position: 'fixed',
top: '0px', top: '0px',
left: '0px', left: '0px',
...@@ -202,113 +159,162 @@ export default { ...@@ -202,113 +159,162 @@ export default {
backgroundColor: '#4caf50', backgroundColor: '#4caf50',
color: '#fff', color: '#fff',
zIndex: 9999, zIndex: 9999,
}, });
// Pagination // Pagination & Dialog
pages: [1, 2], const pages = ref<number[]>([1, 2]);
currentPage: 1, const currentPage = ref(1);
pageToShowDelete: null, const pageToShowDelete = ref<number | null>(null);
const selectedQueueItem = ref<QueueItem | null>(null);
// Dialog
selectedQueueItem: null, watch(() => queueStore.Queues.value, (newVal) => {
}; console.log("🔄 Queues updated in GanttChart.vue:", newVal);
}, }, { deep: true });
computed: {
// สร้าง list ของชั่วโมงทั้งหมดจาก startTime ถึง endTime
hours() { // Computed
const startHour = parseInt(this.startTime.split(':')[0]); const hours = computed(() => {
const endHour = parseInt(this.endTime.split(':')[0]); const startHour = parseInt(startTime.value.split(':')[0]);
const endHour = parseInt(endTime.value.split(':')[0]);
return Array.from({ length: endHour - startHour + 1 }, (_, i) => startHour + i); return Array.from({ length: endHour - startHour + 1 }, (_, i) => startHour + i);
},
},
methods: {
// รับข้อมูล Queue ที่ส่งมาจาก MakequeueBtn
handleUpdateQueue(newQueue) {
console.log("Received new queue:", newQueue);
// ในที่นี้เราสามารถแทนที่ Queue เดิมหรือ merge กับข้อมูลที่มีอยู่
// ตัวอย่างนี้ใช้แทนที่ Queue ทั้งหมด
this.Queue = newQueue;
},
// -------------------- Dialog & Edit --------------------
openQueueDialog(item) {
this.selectedQueueItem = { ...item };
},
closeQueueDialog() {
this.selectedQueueItem = null;
},
openAddQueueDialog() {
this.selectedQueueItem = null;
this.showAddDialog = true;
},
deleteQueueItem(item) {
this.Queue = this.Queue.filter(q => q.queueID !== item.queueID);
this.closeQueueDialog();
},
openAddQueueDialogForEdit(queueItem) {
this.selectedQueueItem = { ...queueItem };
this.showAddDialog = true;
},
closeAddQueueDialog() {
this.showAddDialog = false;
this.selectedQueueItem = null;
},
handleAddQueue(newQueueItem) {
this.Queue.push({
queueID: this.Queue.length + 1,
...newQueueItem,
}); });
this.showAddDialog = false;
}, // Methods
handleEditQueue(updatedQueueItem) { function handleUpdateQueue(newQueue: QueueItem[]) {
const index = this.Queue.findIndex(q => q.queueID === updatedQueueItem.queueID); console.log("Received new queue:", newQueue);
// อัปเดตลงใน store
Queues.value = newQueue;
}
function openQueueDialog(item: QueueItem) {
selectedQueueItem.value = { ...item };
}
function closeQueueDialog() {
selectedQueueItem.value = null;
}
function openAddQueueDialog() {
selectedQueueItem.value = null;
showAddDialog.value = true;
}
function deleteQueueItem(item: QueueItem) {
// ลบคิวใน store
Queues.value = Queues.value.filter(q => q.queueID !== item.queueID);
closeQueueDialog();
}
function openAddQueueDialogForEdit(queueItem: QueueItem) {
selectedQueueItem.value = { ...queueItem };
showAddDialog.value = true;
}
function closeAddQueueDialog() {
showAddDialog.value = false;
selectedQueueItem.value = null;
}
function handleAddQueue(newQueueItem: Omit<QueueItem, 'queueID'>) {
const newQueueID = Queues.value.length + 1;
Queues.value.push({ queueID: newQueueID, ...newQueueItem });
showAddDialog.value = false;
}
function handleEditQueue(updatedQueueItem: QueueItem) {
const index = Queues.value.findIndex(q => q.queueID === updatedQueueItem.queueID);
if (index !== -1) { if (index !== -1) {
this.Queue.splice(index, 1, { ...updatedQueueItem }); Queues.value.splice(index, 1, { ...updatedQueueItem });
}
selectedQueueItem.value = null;
showAddDialog.value = false;
} }
this.selectedQueueItem = null;
this.showAddDialog = false;
},
// -------------------- Utility Functions -------------------- // Utility Functions
formatHour(hour) { function formatHour(hour: number): string {
return (hour < 10 ? '0' + hour : hour) + ':00'; return (hour < 10 ? '0' + hour : hour) + ':00';
}, }
getDateString(dateTimeStr) {
function getDateString(dateTimeStr: string): string {
return dateTimeStr.split(' ')[0]; return dateTimeStr.split(' ')[0];
}, }
getTimeString(dateTimeStr) {
function getTimeString(dateTimeStr: string): string {
return dateTimeStr.split(' ')[1]; return dateTimeStr.split(' ')[1];
}, }
filteredQueue(machineID) {
return this.Queue.filter((q) => { function filteredQueue(machineID: string) {
const queueDate = this.getDateString(q.startTime); if (!queueStore.Queues?.values || typeof queueStore.Queues.values !== "function") {
console.warn("queueStore.Queues.values is not a function or is undefined");
return [];
}
return queueStore.Queues.filter(q => {
const queueDate = getDateString(q.startTime);
console.log(queueDate)
return ( return (
q.machineID === machineID && q.machineID === machineID &&
q.pageNumber === this.currentPage && q.pageNumber === currentPage.value &&
queueDate === this.selectedDate queueDate === selectedDate.value
); );
}); });
}, }
getQueueColor(orderID) {
const colors = ['#F58181', '#FDDC5C', '#C5A1BC', '#49E060'];
return colors[(orderID - 1) % colors.length];
},
getQueueStyle(item) { const colorMap: Record<string, string> = {}; // Map เก็บสีของแต่ละ orderID
const start = this.getTimeString(item.startTime);
const end = this.getTimeString(item.finishTime); function getQueueColor(orderID: string | number): string {
const startDecimal = this.timeToDecimal(start); const key = String(orderID);
const endDecimal = this.timeToDecimal(end);
const timelineStart = this.timeToDecimal(this.startTime); // 🔴 ถ้าเป็น "ผลิตเผื่อ" → ใช้สีแดงพาสเทล
const timelineEnd = this.timeToDecimal(this.endTime); if (key === "ผลิตเผื่อ") {
return "#FFC1C1"; // Light Pastel Red
const ratioStart = (startDecimal - timelineStart) / (timelineEnd - timelineStart); }
const ratioEnd = (endDecimal - timelineStart) / (timelineEnd - timelineStart);
// 💜 ถ้าเป็น "เปลี่ยนขนาด" → ใช้สีม่วงชมพูพาสเทล
if (key === "เปลี่ยนขนาด") {
return "#E1BEE7"; // Light Pastel Pink-Purple (Lavender)
}
// ✅ ถ้ามีสีอยู่แล้ว ให้ใช้สีเดิม
if (colorMap[key]) return colorMap[key];
// 🌊 ถ้าเป็นคิวปกติ → ใช้สีธีมน้ำ
const newColor = generateWaterPastel();
colorMap[key] = newColor;
return newColor;
}
function generateWaterPastel(): string {
// สุ่มเฉพาะสีโทนน้ำทะเล (ฟ้า, น้ำเงิน, เขียวมิ้นต์)
const waterHues = [180, 190, 200, 210, 220, 230, 240];
const hue = waterHues[Math.floor(Math.random() * waterHues.length)];
return `hsl(${hue}, 60%, 80%)`;
}
function getQueueStyle(item: QueueItem) {
const start = getTimeString(item.startTime);
const end = getTimeString(item.finishTime);
const startDecimal = timeToDecimal(start);
const endDecimal = timeToDecimal(end);
const timelineStart = timeToDecimal(startTime.value);
const timelineEnd = timeToDecimal(endTime.value);
const ratioStart = Math.max(0, Math.min(1, (startDecimal - timelineStart) / (timelineEnd - timelineStart)));
const ratioEnd = Math.max(0, Math.min(1, (endDecimal - timelineStart) / (timelineEnd - timelineStart)));
const backgroundColor = getQueueColor(item.orderID); // ใช้ฟังก์ชันที่แก้ไขแล้ว
console.log(`Queue ${item.queueID}: orderID=${item.orderID}, color=${backgroundColor}`);
return { return {
left: ratioStart * 100 + '%', left: ratioStart * 100 + '%',
width: (ratioEnd - ratioStart) * 100 + '%', width: (ratioEnd - ratioStart) * 100 + '%',
backgroundColor: this.getQueueColor(item.orderID), backgroundColor: backgroundColor, // ใช้สีแดงพาสเทลหรือสีธีมน้ำ
color: '#fff', color: '#333',
borderRadius: '10px', borderRadius: '10px',
textAlign: 'center', textAlign: 'center',
display: 'flex', display: 'flex',
...@@ -316,46 +322,57 @@ export default { ...@@ -316,46 +322,57 @@ export default {
justifyContent: 'center', justifyContent: 'center',
padding: '5px', padding: '5px',
fontSize: '14px', fontSize: '14px',
fontWeight: 'bold',
boxShadow: '0px 4px 6px rgba(0, 0, 0, 0.1)',
}; };
}, }
getLineStyle(hour) {
const timelineStart = this.timeToDecimal(this.startTime);
const timelineEnd = this.timeToDecimal(this.endTime);
function getLineStyle(hour: number) {
const timelineStart = timeToDecimal(startTime.value);
const timelineEnd = timeToDecimal(endTime.value);
const ratio = (hour - timelineStart) / (timelineEnd - timelineStart); const ratio = (hour - timelineStart) / (timelineEnd - timelineStart);
return { left: ratio * 100 + '%' }; return { left: ratio * 100 + '%' };
}, }
timeToDecimal(timeStr) {
function timeToDecimal(timeStr: string): number {
const [hours, minutes] = timeStr.split(':').map(Number); const [hours, minutes] = timeStr.split(':').map(Number);
return hours + minutes / 60; return hours + minutes / 60;
}, }
decimalToTime(decimal) {
function decimalToTime(decimal: number): string {
const hours = Math.floor(decimal); const hours = Math.floor(decimal);
const minutes = Math.round((decimal - hours) * 60); const minutes = Math.round((decimal - hours) * 60);
return (hours < 10 ? '0' + hours : hours) + ':' + (minutes < 10 ? '0' + minutes : minutes); return (hours < 10 ? '0' + hours : hours) + ':' + (minutes < 10 ? '0' + minutes : minutes);
}, }
// -------------------- Drag/Drop & Resize -------------------- // Drag/Drop & Resize Functions
onDragStart(event, item) { function onDragStart(event: DragEvent, item: QueueItem) {
this.draggingQueue = item; draggingQueue.value = item;
const rect = event.target.getBoundingClientRect(); const target = event.target as HTMLElement;
this.dragOffset = rect.width / 2; const rect = target.getBoundingClientRect();
const emptyImg = document.createElement('img'); dragOffset.value = rect.width / 2;
const emptyImg = new Image();
emptyImg.src = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs='; emptyImg.src = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
event.dataTransfer.setDragImage(emptyImg, 0, 0); event.dataTransfer?.setDragImage(emptyImg, 0, 0);
this.ghostQueue = { ...item }; ghostQueue.value = { ...item };
this.ghostStyle.display = 'block'; ghostStyle.display = 'block';
this.ghostStyle.backgroundColor = this.getQueueColor(item.orderID); ghostStyle.backgroundColor = getQueueColor(item.orderID);
document.addEventListener('dragover', this.onDragOverGlobal); document.addEventListener('dragover', onDragOverGlobal);
document.addEventListener('dragend', this.onDragEndGlobal); document.addEventListener('dragend', onDragEndGlobal);
}, }
onDragOver(event) {
function onDragOver(event: DragEvent) {
event.preventDefault(); event.preventDefault();
}, }
onDragOverGlobal(event) {
function onDragOverGlobal(event: DragEvent) {
event.preventDefault(); event.preventDefault();
const rowTimeline = document.querySelector('.row-timeline'); const rowTimeline = document.querySelector('.row-timeline') as HTMLElement;
if (!rowTimeline) return; if (!rowTimeline) return;
const timelineRect = rowTimeline.getBoundingClientRect(); const timelineRect = rowTimeline.getBoundingClientRect();
const timelineWidth = timelineRect.width; const timelineWidth = timelineRect.width;
...@@ -364,89 +381,100 @@ export default { ...@@ -364,89 +381,100 @@ export default {
let ratio = offsetX / timelineWidth; let ratio = offsetX / timelineWidth;
ratio = Math.min(Math.max(ratio, 0), 1); ratio = Math.min(Math.max(ratio, 0), 1);
const timelineStart = this.timeToDecimal(this.startTime); const timelineStartDec = timeToDecimal(startTime.value);
const timelineEnd = this.timeToDecimal(this.endTime); const timelineEndDec = timeToDecimal(endTime.value);
const startDecimal = this.timeToDecimal(this.getTimeString(this.draggingQueue.startTime)); if (!draggingQueue.value) return;
const endDecimal = this.timeToDecimal(this.getTimeString(this.draggingQueue.finishTime)); const startDecimal = timeToDecimal(getTimeString(draggingQueue.value.startTime));
const endDecimal = timeToDecimal(getTimeString(draggingQueue.value.finishTime));
const duration = endDecimal - startDecimal; const duration = endDecimal - startDecimal;
let newStartDecimal = timelineStart + ratio * (timelineEnd - timelineStart); let newStartDecimal = timelineStartDec + ratio * (timelineEndDec - timelineStartDec);
newStartDecimal = Math.round(newStartDecimal * 2) / 2; newStartDecimal = Math.round(newStartDecimal * 2) / 2;
let newEndDecimal = newStartDecimal + duration; let newEndDecimal = newStartDecimal + duration;
if (newEndDecimal > timelineEnd) { if (newEndDecimal > timelineEndDec) {
newEndDecimal = timelineEnd; newEndDecimal = timelineEndDec;
newStartDecimal = newEndDecimal - duration; newStartDecimal = newEndDecimal - duration;
} }
const datePart = this.getDateString(this.draggingQueue.startTime); const datePart = getDateString(draggingQueue.value.startTime);
const newStartStr = datePart + ' ' + this.decimalToTime(newStartDecimal); const newStartStr = datePart + ' ' + decimalToTime(newStartDecimal);
const newEndStr = datePart + ' ' + this.decimalToTime(newEndDecimal); const newEndStr = datePart + ' ' + decimalToTime(newEndDecimal);
this.ghostQueue.startTime = newStartStr; if (ghostQueue.value) {
this.ghostQueue.finishTime = newEndStr; ghostQueue.value.startTime = newStartStr;
ghostQueue.value.finishTime = newEndStr;
}
const snappedRatioStart = (newStartDecimal - timelineStart) / (timelineEnd - timelineStart); const snappedRatioStart = (newStartDecimal - timelineStartDec) / (timelineEndDec - timelineStartDec);
const snappedLeft = timelineRect.left + snappedRatioStart * timelineWidth; const snappedLeft = timelineRect.left + snappedRatioStart * timelineWidth;
const snappedRatioEnd = (newEndDecimal - timelineStart) / (timelineEnd - timelineStart); const snappedRatioEnd = (newEndDecimal - timelineStartDec) / (timelineEndDec - timelineStartDec);
const snappedWidth = (snappedRatioEnd - snappedRatioStart) * timelineWidth; const snappedWidth = (snappedRatioEnd - snappedRatioStart) * timelineWidth;
const rows = document.querySelectorAll('.row'); const rows = document.querySelectorAll('.row');
let closestRow = null; let closestRow: HTMLElement | null = null;
let minDistance = Infinity; let minDistance = Infinity;
rows.forEach((row) => { rows.forEach((row) => {
const rect = row.getBoundingClientRect(); const rect = (row as HTMLElement).getBoundingClientRect();
const distance = Math.abs(event.clientY - rect.top); const distance = Math.abs(event.clientY - rect.top);
if (distance < minDistance) { if (distance < minDistance) {
minDistance = distance; minDistance = distance;
closestRow = row; closestRow = row as HTMLElement;
} }
}); });
if (closestRow) { if (closestRow) {
this.ghostQueue.machineID = closestRow.querySelector('.machine-label').textContent.trim(); const labelEl = closestRow.querySelector('.machine-label') as HTMLElement;
this.ghostStyle.top = closestRow.getBoundingClientRect().top + 'px'; if (labelEl) {
} ghostQueue.value!.machineID = labelEl.textContent?.trim() || ghostQueue.value!.machineID;
}
this.ghostStyle.left = snappedLeft + 'px'; ghostStyle.top = closestRow.getBoundingClientRect().top + 'px';
this.ghostStyle.width = snappedWidth + 'px'; }
},
onDragEndGlobal() { ghostStyle.left = snappedLeft + 'px';
this.ghostQueue = null; ghostStyle.width = snappedWidth + 'px';
this.ghostStyle.display = 'none'; }
this.draggingQueue = null;
document.removeEventListener('dragend', this.onDragEndGlobal); function onDragEndGlobal() {
}, ghostQueue.value = null;
onDragEnd(event, item) { ghostStyle.display = 'none';
if (!this.ghostQueue) return; draggingQueue.value = null;
item.startTime = this.ghostQueue.startTime; document.removeEventListener('dragend', onDragEndGlobal);
item.finishTime = this.ghostQueue.finishTime; }
item.machineID = this.ghostQueue.machineID;
document.removeEventListener('dragover', this.onDragOverGlobal); function onDragEnd(event: DragEvent, item: QueueItem) {
this.ghostQueue = null; if (!ghostQueue.value) return;
this.ghostStyle.display = 'none'; item.startTime = ghostQueue.value.startTime;
this.draggingQueue = null; item.finishTime = ghostQueue.value.finishTime;
}, item.machineID = ghostQueue.value.machineID;
onDrop(event, newMachine) { document.removeEventListener('dragover', onDragOverGlobal);
ghostQueue.value = null;
ghostStyle.display = 'none';
draggingQueue.value = null;
}
function onDrop(event: DragEvent, newMachine: string) {
event.preventDefault(); event.preventDefault();
if (this.draggingQueue && this.ghostQueue) { if (draggingQueue.value && ghostQueue.value) {
this.draggingQueue.startTime = this.ghostQueue.startTime; draggingQueue.value.startTime = ghostQueue.value.startTime;
this.draggingQueue.finishTime = this.ghostQueue.finishTime; draggingQueue.value.finishTime = ghostQueue.value.finishTime;
this.draggingQueue.machineID = newMachine; draggingQueue.value.machineID = newMachine;
} }
this.ghostQueue = null; ghostQueue.value = null;
this.ghostStyle.display = 'none'; ghostStyle.display = 'none';
this.draggingQueue = null; draggingQueue.value = null;
}, }
onResizeStart(event, item, direction) {
function onResizeStart(event: MouseEvent, item: QueueItem, direction: string) {
event.preventDefault(); event.preventDefault();
this.resizingQueue = item; resizingQueue.value = item;
this.resizeDirection = direction; resizeDirection.value = direction;
document.addEventListener('mousemove', this.onResizing); document.addEventListener('mousemove', onResizing);
document.addEventListener('mouseup', this.onResizeEnd); document.addEventListener('mouseup', onResizeEnd);
}, }
onResizing(event) {
if (!this.resizingQueue) return; function onResizing(event: MouseEvent) {
const rowTimeline = document.querySelector('.row-timeline'); if (!resizingQueue.value) return;
const rowTimeline = document.querySelector('.row-timeline') as HTMLElement;
if (!rowTimeline) return; if (!rowTimeline) return;
const timelineRect = rowTimeline.getBoundingClientRect(); const timelineRect = rowTimeline.getBoundingClientRect();
const timelineWidth = timelineRect.width; const timelineWidth = timelineRect.width;
...@@ -455,63 +483,71 @@ export default { ...@@ -455,63 +483,71 @@ export default {
let ratio = offsetX / timelineWidth; let ratio = offsetX / timelineWidth;
ratio = Math.min(Math.max(ratio, 0), 1); ratio = Math.min(Math.max(ratio, 0), 1);
const timelineStart = this.timeToDecimal(this.startTime); const timelineStartDec = timeToDecimal(startTime.value);
const timelineEnd = this.timeToDecimal(this.endTime); const timelineEndDec = timeToDecimal(endTime.value);
const startDec = this.timeToDecimal(this.getTimeString(this.resizingQueue.startTime)); const startDec = timeToDecimal(getTimeString(resizingQueue.value.startTime));
const endDec = this.timeToDecimal(this.getTimeString(this.resizingQueue.finishTime)); const endDec = timeToDecimal(getTimeString(resizingQueue.value.finishTime));
let newTimeDecimal = timelineStart + ratio * (timelineEnd - timelineStart); let newTimeDecimal = timelineStartDec + ratio * (timelineEndDec - timelineStartDec);
newTimeDecimal = Math.round(newTimeDecimal * 2) / 2; newTimeDecimal = Math.round(newTimeDecimal * 2) / 2;
const datePart = this.getDateString(this.resizingQueue.startTime); const datePart = getDateString(resizingQueue.value.startTime);
if (this.resizeDirection === 'left') { if (resizeDirection.value === 'left') {
if (newTimeDecimal >= endDec) return; if (newTimeDecimal >= endDec) return;
const newStartStr = datePart + ' ' + this.decimalToTime(newTimeDecimal); const newStartStr = datePart + ' ' + decimalToTime(newTimeDecimal);
this.resizingQueue.startTime = newStartStr; resizingQueue.value.startTime = newStartStr;
} else if (this.resizeDirection === 'right') { } else if (resizeDirection.value === 'right') {
if (newTimeDecimal <= startDec) return; if (newTimeDecimal <= startDec) return;
const newEndStr = datePart + ' ' + this.decimalToTime(newTimeDecimal); const newEndStr = datePart + ' ' + decimalToTime(newTimeDecimal);
this.resizingQueue.finishTime = newEndStr; resizingQueue.value.finishTime = newEndStr;
} }
}, }
onResizeEnd() {
this.resizingQueue = null; function onResizeEnd() {
this.resizeDirection = null; resizingQueue.value = null;
document.removeEventListener('mousemove', this.onResizing); resizeDirection.value = null;
document.removeEventListener('mouseup', this.onResizeEnd); document.removeEventListener('mousemove', onResizing);
}, document.removeEventListener('mouseup', onResizeEnd);
addPage() { }
if (this.pages.length < 10) {
const newPage = this.pages.length + 1; // Pagination Functions
this.pages.push(newPage); function addPage() {
if (pages.value.length < 10) {
const newPage = pages.value.length + 1;
pages.value.push(newPage);
} else { } else {
alert("Maximum of 10 pages allowed."); alert("Maximum of 10 pages allowed.");
} }
}, }
onPageRightClick(page, event) {
function onPageRightClick(page: number, event: MouseEvent) {
event.preventDefault(); event.preventDefault();
this.pageToShowDelete = page; pageToShowDelete.value = page;
}, }
deletePage(page) {
const index = this.pages.indexOf(page); function deletePage(page: number) {
const index = pages.value.indexOf(page);
if (index !== -1) { if (index !== -1) {
this.pages.splice(index, 1); pages.value.splice(index, 1);
if (this.currentPage === page) { if (currentPage.value === page) {
this.currentPage = this.pages.length > 0 ? this.pages[0] : 1; currentPage.value = pages.value.length > 0 ? pages.value[0] : 1;
} }
} }
this.pageToShowDelete = null; pageToShowDelete.value = null;
}, }
onDocumentClick(event) {
if (!event.target.closest('.page-btn')) { function onDocumentClick(event: MouseEvent) {
this.pageToShowDelete = null; const target = event.target as HTMLElement;
} if (!target.closest('.page-btn')) {
}, pageToShowDelete.value = null;
}, }
mounted() { }
document.addEventListener('click', this.onDocumentClick);
}, // Lifecycle Hooks
beforeUnmount() { onMounted(() => {
document.removeEventListener('click', this.onDocumentClick); document.addEventListener('click', onDocumentClick);
}, });
};
onBeforeUnmount(() => {
document.removeEventListener('click', onDocumentClick);
});
</script> </script>
<template> <template>
<div> <div>
<button @click="makeQueue">Make a queue</button> <v-btn>Make a queue</v-btn>
</div> </div>
</template> </template>
<script> <script lang="ts" setup>
import { scheduleAllOrders } from './scheduleCalculator.js'; import { defineEmits } from 'vue';
import { useQueueStore } from '@/stores/queue.js';
import LoadingDialog from '../LoadingDialog.vue';
export default {
name: 'MakequeueBtn',
methods: {
makeQueue() {
const queueItems = scheduleAllOrders();
this.$emit('updateQueue', queueItems);
}
}
}
</script> </script>
<style scoped> <style scoped>
button { .v-btn {
/* พื้นหลังสีน้ำเงิน */ background-color: #333647;
background-color: #2196f3; color: #ffffff;
/* สีตัวอักษรเป็นสีขาว */
color: #fff;
/* ไม่มีขอบ */
border: none;
/* มุมโค้ง */
border-radius: 8px; border-radius: 8px;
/* ขนาดตัวอักษรใหญ่ขึ้น */
font-size: 18px; font-size: 18px;
font-weight: bold; font-weight: bold;
/* ปรับขนาดปุ่มให้กว้างขึ้น */
width: 200px; width: 200px;
height: 60px; height: 60px;
/* กึ่งกลางตัวอักษร */
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
/* เปลี่ยนเคอร์เซอร์เป็นแบบคลิกได้ */
cursor: pointer; cursor: pointer;
transition: background-color 0.2s ease; transition: background-color 0.2s ease;
} }
/* เมื่อเอาเมาส์ไปชี้ */ .v-btn:hover {
button:hover { background-color: #7e9cd3;
background-color: #1976d2;
} }
</style> </style>
\ No newline at end of file
...@@ -25,20 +25,27 @@ const loadingStore = useLoadingStore() ...@@ -25,20 +25,27 @@ const loadingStore = useLoadingStore()
background-repeat: no-repeat; background-repeat: no-repeat;
animation: l2 1s infinite alternate; animation: l2 1s infinite alternate;
} }
@keyframes l2 { @keyframes l2 {
0%,25% {
0%,
25% {
background-size: 8px 0, 8px 4px, 8px 4px, 8px 0, 8px 4px, 8px 4px, 8px 0, 8px 4px, 8px 4px; background-size: 8px 0, 8px 4px, 8px 4px, 8px 0, 8px 4px, 8px 4px, 8px 0, 8px 4px, 8px 4px;
background-position: 0 50%, 0 calc(50% - 2px), 0 calc(50% + 2px), 50% 50%, 50% calc(50% - 2px), 50% calc(50% + 2px), 100% 50%, 100% calc(50% - 2px), 100% calc(50% + 2px); background-position: 0 50%, 0 calc(50% - 2px), 0 calc(50% + 2px), 50% 50%, 50% calc(50% - 2px), 50% calc(50% + 2px), 100% 50%, 100% calc(50% - 2px), 100% calc(50% + 2px);
} }
50% { 50% {
background-size: 8px 100%, 8px 4px, 8px 4px, 8px 0, 8px 4px, 8px 4px, 8px 0, 8px 4px, 8px 4px; background-size: 8px 100%, 8px 4px, 8px 4px, 8px 0, 8px 4px, 8px 4px, 8px 0, 8px 4px, 8px 4px;
background-position: 0 50%, 0 calc(0% - 2px), 0 calc(100% + 2px), 50% 50%, 50% calc(50% - 2px), 50% calc(50% + 2px), 100% 50%, 100% calc(50% - 2px), 100% calc(50% + 2px); background-position: 0 50%, 0 calc(0% - 2px), 0 calc(100% + 2px), 50% 50%, 50% calc(50% - 2px), 50% calc(50% + 2px), 100% 50%, 100% calc(50% - 2px), 100% calc(50% + 2px);
} }
75% { 75% {
background-size: 8px 100%, 8px 4px, 8px 4px, 8px 100%, 8px 4px, 8px 4px, 8px 0, 8px 4px, 8px 4px; background-size: 8px 100%, 8px 4px, 8px 4px, 8px 100%, 8px 4px, 8px 4px, 8px 0, 8px 4px, 8px 4px;
background-position: 0 50%, 0 calc(0% - 2px), 0 calc(100% + 2px), 50% 50%, 50% calc(0% - 2px), 50% calc(100% + 2px), 100% 50%, 100% calc(50% - 2px), 100% calc(50% + 2px); background-position: 0 50%, 0 calc(0% - 2px), 0 calc(100% + 2px), 50% 50%, 50% calc(0% - 2px), 50% calc(100% + 2px), 100% 50%, 100% calc(50% - 2px), 100% calc(50% + 2px);
} }
95%,100% {
95%,
100% {
background-size: 8px 100%, 8px 4px, 8px 4px, 8px 100%, 8px 4px, 8px 4px, 8px 100%, 8px 4px, 8px 4px; background-size: 8px 100%, 8px 4px, 8px 4px, 8px 100%, 8px 4px, 8px 4px, 8px 100%, 8px 4px, 8px 4px;
background-position: 0 50%, 0 calc(0% - 2px), 0 calc(100% + 2px), 50% 50%, 50% calc(0% - 2px), 50% calc(100% + 2px), 100% 50%, 100% calc(0% - 2px), 100% calc(100% + 2px); background-position: 0 50%, 0 calc(0% - 2px), 0 calc(100% + 2px), 50% 50%, 50% calc(0% - 2px), 50% calc(100% + 2px), 100% 50%, 100% calc(0% - 2px), 100% calc(100% + 2px);
} }
......
...@@ -18,6 +18,9 @@ watchEffect(() => { ...@@ -18,6 +18,9 @@ watchEffect(() => {
if (route.name === 'pq') { if (route.name === 'pq') {
icon.value = 'mdi-text-box' icon.value = 'mdi-text-box'
title.value = ' คิวการผลิต' title.value = ' คิวการผลิต'
} else if (route.name === 'pq-aof') {
icon.value = 'mdi-package-variant'
title.value = 'คิวการผลิตออฟ'
} else if (route.name === 'stocks') { } else if (route.name === 'stocks') {
icon.value = 'mdi-package-variant' icon.value = 'mdi-package-variant'
title.value = 'Stock Management' title.value = 'Stock Management'
......
...@@ -9,8 +9,7 @@ const authStore = useAuthStore() ...@@ -9,8 +9,7 @@ const authStore = useAuthStore()
</script> </script>
<template> <template>
<v-navigation-drawer expand-on-hover <v-navigation-drawer expand-on-hover rail style="background-color: #2B2E3F; color:white;">
rail style="background-color: #2B2E3F; color:white;">
<!-- profile --> <!-- profile -->
<v-list> <v-list>
<v-list-item <v-list-item
...@@ -74,7 +73,8 @@ const authStore = useAuthStore() ...@@ -74,7 +73,8 @@ const authStore = useAuthStore()
<v-list density="compact" nav style="position: absolute; bottom: 0; width: 100%;"> <v-list density="compact" nav style="position: absolute; bottom: 0; width: 100%;">
<v-divider></v-divider> <v-divider></v-divider>
<v-list-item prepend-icon="mdi-cog" title="ตั้งค่า" value="setting"></v-list-item> <v-list-item prepend-icon="mdi-cog" title="ตั้งค่า" value="setting"></v-list-item>
<v-list-item prepend-icon="mdi-logout" title="ออกจากระบบ" value="logout" @click="authStore.logout()"></v-list-item> <v-list-item prepend-icon="mdi-logout" title="ออกจากระบบ" value="logout"
@click="authStore.logout()"></v-list-item>
</v-list> </v-list>
</v-navigation-drawer> </v-navigation-drawer>
......
import type { Queue } from '@/types/Queue' import axios from 'axios';
import type { QueueItem } from '@/types/QueueItem';
const makeQueue = async (): Promise<QueueItem[] | undefined> => {
try {
// เรียก API
const response = await axios.post('http://127.0.0.1:9000/run-simulation');
console.log('📌 Response:', response);
const queueData = response.data.production_log;
console.log('this is queueData queueservice', queueData);
if (!Array.isArray(queueData)) {
console.error('❌ Error: Invalid queue data format');
return undefined;
}
// ✅ แปลงข้อมูลเป็น JSON String แล้ว Parse กลับมาเป็น Object ใหม่
const queueItems: QueueItem[] = JSON.parse(
JSON.stringify(queueData.map(q => ({
queueID: q.queueID,
machineID: q.machineID,
orderID: q.orderID,
pageNumber: q.pageNumber ?? 1, // ถ้าไม่มี ให้ใช้ค่า default เป็น 1
startTime: q.startTime,
finishTime: q.finishTime,
status: q.status,
bottleSize: q.bottleSize,
producedQuantity: q.producedQuantity,
})))
);
return queueItems; // ✅ คืนค่าเป็น `QueueItem[]`
} catch (error: any) {
console.error('❌ Error:', error.message || error);
return undefined;
}
};
export default { makeQueue };
export class QueueService { export class QueueService {
private queue: Queue[] = []; private queue: Queue[] = [];
...@@ -47,4 +88,5 @@ export class QueueService { ...@@ -47,4 +88,5 @@ export class QueueService {
} }
return null; return null;
} }
} }
import { defineStore } from 'pinia';
import QueueService from '@/services/queue';
import type { QueueItem } from '@/types/QueueItem'
import { ref } from 'vue';
import { useLoadingStore } from './loading';
// ฟังก์ชัน Sleep ให้รอจริง ๆ
function sleep(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms));
}
export const useQueueStore = defineStore('queue', () => {
const Queues = ref<QueueItem[]>([]);
const loadingStore = useLoadingStore();
const makeQueue = async () => {
try {
// ✅ รอ 3 วินาทีจริง ๆ ก่อนเริ่มโหลด
loadingStore.isLoading = true;
console.log("Waiting for 3 seconds...");
await sleep(3000);
console.log("Fetching queue data...");
const queueData = await QueueService.makeQueue(); // ✅ รอ API
if (queueData && Array.isArray(queueData)) {
console.log("Appending data to Queues...");
Queues.value = [...queueData]; // ✅ ใช้ spread แทน forEach()
} else {
console.log("No queue data received.");
}
loadingStore.isLoading = false; // ✅ ปิด loading เมื่อโหลดเสร็จ
} catch (error) {
console.error('❌ Error fetching queue:', error);
loadingStore.isLoading = false; // ปิด loading ถ้า error
}
};
return { Queues, makeQueue };
});
export type Queue = { export type Queue = {
queueID: number; // PK - รหัส Queue queueID: number | string;
machineID: number; // FK - รหัสเครื่องจักรที่ใช้ machineID: number | string;
orderID: number; // FK - รหัสออเดอร์ที่เกี่ยวข้อง orderID: number | string;
pageID: number; // FK - รหัสหน้าที่แสดงใน Gantt Chart pageID: number;
startTime: Date; // เวลาเริ่มต้น startTime: Date;
finishTime: Date; // เวลาสิ้นสุด finishTime: Date;
status: 'pending' | 'in-progress' | 'completed' | 'canceled'; // สถานะของคิว status: 'pending' | 'in-progress' | 'completed' | 'canceled';
bottleSize: string; // ขนาดขวดที่ผลิต bottleSize: string;
producedQuantity: number; // จำนวนที่ผลิตได้ producedQuantity: number;
}; };
\ No newline at end of file
export type QueueItem = {
queueID: number;
machineID: string;
orderID: number;
pageNumber: number;
startTime: string;
finishTime: string;
status: string;
bottleSize: string;
producedQuantity: number;
}
\ No newline at end of file
<script setup>
import { ref } from 'vue'
import GanttChart from '@/components/GanttChartAof/GanttChart.vue'
import EmployeeSector from '@/components/EmployeeSector.vue'
const employees = Array.from({ length: 10 }, (_, i) => `EM${i + 1}`)
const orders = ref([
{ id: 1, name: 'ORDER1' },
{ id: 2, name: 'ORDER2' },
{ id: 3, name: 'ORDER3' }
])
// Drag and drop functionality
const draggedOrder = ref(null)
const handleDragStart = (order) => {
draggedOrder.value = order
}
const handleDragOver = (event) => {
event.preventDefault()
}
const handleDrop = (targetOrder) => {
if (!draggedOrder.value || draggedOrder.value === targetOrder) return
// Find the indices of the dragged and target orders
const draggedIndex = orders.value.findIndex((o) => o.id === draggedOrder.value.id)
const targetIndex = orders.value.findIndex((o) => o.id === targetOrder.id)
// Remove the dragged order from its original position
const [removed] = orders.value.splice(draggedIndex, 1)
// Insert the dragged order at the target position
orders.value.splice(targetIndex, 0, removed)
// Reset the dragged order
draggedOrder.value = null
}
</script>
<template>
<v-container class="pa-0">
<!-- Gantt chart -->
<v-sheet class="pa-1 mb-3"
style="border-radius: 15px; max-width: 98%; margin-left: auto; margin-right: auto; min-height: 460px;">
<!-- Gantt Chart -->
<GanttChart />
</v-sheet>
<!-- Bottom Section -->
<v-row class="mt-1" style="max-width: 100%; margin-left: auto; margin-right: auto;">
<!-- Employee Selection -->
<v-col cols="6">
<v-card class="pa-4" style="background-color: white; border-radius: 15px; min-height: 280px;">
<!-- เลือกพนักงานวางทับตรงนี้ -->
<EmployeeSector :employees="employees" />
</v-card>
</v-col>
<!-- Order Priority -->
<v-col cols="6">
<v-card class="pa-5" style="background-color: white; border-radius: 15px; min-height: 280px">
<p class="text-center font-weight-bold mb-4 text-black" style="font-size: 20px;">ลำดับความสำคัญ</p>
<div v-for="order in orders" :key="order.id" class="order-item" draggable="true"
@dragstart="() => handleDragStart(order)" @dragover="handleDragOver" @drop="() => handleDrop(order)">
{{ order.name }}
</div>
</v-card>
</v-col>
</v-row>
</v-container>
</template>
<!-- ส่วน UI ของปุ่ม Order1--2 -->
<style scoped>
.order-item {
background-color: #2b2e3f;
color: white;
padding: 7px;
margin-bottom: 8px;
text-align: center;
cursor: move;
user-select: none;
}
.order-item:hover {
opacity: 0.9;
}
</style>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment