[Update] Cetak barcode
This commit is contained in:
parent
e54a021b98
commit
e1639109c8
@ -69,32 +69,7 @@
|
||||
class="fixed inset-0 bg-black/75 flex items-center justify-center p-4 z-50 backdrop-blur-sm">
|
||||
<div
|
||||
class="bg-white rounded-xl shadow-lg max-w-sm w-full p-6 relative transform transition-all duration-300 scale-95 opacity-0 animate-fadeIn">
|
||||
<!-- QR Code -->
|
||||
<div class="flex justify-center mb-4">
|
||||
<div class="p-2 border border-C rounded-lg">
|
||||
<img :src="qrCodeUrl" alt="QR Code" class="w-36 h-36" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Info Produk -->
|
||||
<div class="text-center text-D font-bold text-lg mb-1">
|
||||
{{ selectedItem?.kode_item }}
|
||||
</div>
|
||||
<div class="text-center text-gray-700 font-medium mb-1">
|
||||
{{ selectedItem?.produk?.nama }}
|
||||
</div>
|
||||
<div class="text-center text-gray-500 text-sm mb-4">
|
||||
{{ selectedItem?.produk?.kategori }}
|
||||
</div>
|
||||
|
||||
<!-- Tombol Cetak -->
|
||||
<div class="flex justify-center gap-2 mb-4">
|
||||
<button @click="printQR" class="bg-D text-A px-4 py-2 rounded hover:bg-D/80 transition"
|
||||
title="Cetak menggunakan printer browser">
|
||||
<i class="fas fa-print mr-2"></i>
|
||||
Print
|
||||
</button>
|
||||
</div>
|
||||
<PrintBarcode :code="selectedItem?.kode_item" :item="selectedItem?.produk" />
|
||||
|
||||
<!-- Dropdown pilih nampan -->
|
||||
<div class="mb-4">
|
||||
@ -173,6 +148,7 @@
|
||||
import { ref, computed, onMounted } from "vue";
|
||||
import axios from "axios";
|
||||
import ConfirmDeleteModal from './ConfirmDeleteModal.vue';
|
||||
import PrintBarcode from './PrintBarcode.vue';
|
||||
|
||||
const props = defineProps({
|
||||
search: {
|
||||
@ -203,15 +179,6 @@ const confirmModalMessage = ref("");
|
||||
const confirmText = ref("Ya, Konfirmasi");
|
||||
const cancelText = ref("Batal");
|
||||
|
||||
// QR Code generator
|
||||
const qrCodeUrl = computed(() => {
|
||||
if (selectedItem.value) {
|
||||
const data = selectedItem.value.kode_item;
|
||||
return `https://api.qrserver.com/v1/create-qr-code/?size=150x150&data=${encodeURIComponent(data)}`;
|
||||
}
|
||||
return "";
|
||||
});
|
||||
|
||||
// Computed untuk statistik
|
||||
const totalWeight = computed(() => {
|
||||
const total = filteredItems.value.reduce((sum, item) => {
|
||||
@ -252,13 +219,6 @@ const confirmDelete = async () => {
|
||||
await axios.delete(`/api/item/${selectedItem.value.id}`, {
|
||||
headers: { Authorization: `Bearer ${localStorage.getItem("token")}` },
|
||||
});
|
||||
|
||||
// Tampilkan alert sukses
|
||||
alert.value = { success: `Item ${selectedItem.value.kode_item} berhasil dihapus.` };
|
||||
|
||||
// Refresh data
|
||||
await refreshData();
|
||||
|
||||
// Tutup modal & popup
|
||||
showDeleteConfirm.value = false;
|
||||
closePopup();
|
||||
@ -330,106 +290,6 @@ const handleConfirmAction = async () => {
|
||||
closeConfirmModal();
|
||||
};
|
||||
|
||||
// Print QR menggunakan browser
|
||||
const printQR = () => {
|
||||
if (qrCodeUrl.value && selectedItem.value) {
|
||||
const printWindow = window.open('', '_blank');
|
||||
const itemCode = selectedItem.value.kode_item;
|
||||
|
||||
printWindow.document.write(`
|
||||
<html>
|
||||
<head>
|
||||
<title>Print QR Code - ${itemCode}</title>
|
||||
<style>
|
||||
@page {
|
||||
size: 38mm 25mm;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
width: 38mm;
|
||||
height: 25mm;
|
||||
padding: 2mm;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.qr-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.qr-img {
|
||||
width: 18mm;
|
||||
height: 18mm;
|
||||
margin-bottom: 1mm;
|
||||
}
|
||||
|
||||
.item-code {
|
||||
font-weight: bold;
|
||||
font-size: 8pt;
|
||||
margin-bottom: 0.5mm;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 34mm;
|
||||
}
|
||||
|
||||
.item-name {
|
||||
font-size: 6pt;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 34mm;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.item-weight {
|
||||
font-size: 6pt;
|
||||
color: #666;
|
||||
margin-top: 0.5mm;
|
||||
}
|
||||
|
||||
@media print {
|
||||
body {
|
||||
-webkit-print-color-adjust: exact;
|
||||
print-color-adjust: exact;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="qr-container">
|
||||
<img id="qr-img" class="qr-img" src="${qrCodeUrl.value}" alt="QR Code" />
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`);
|
||||
|
||||
printWindow.document.close();
|
||||
|
||||
const img = printWindow.document.getElementById("qr-img");
|
||||
img.onload = () => {
|
||||
printWindow.focus();
|
||||
printWindow.print();
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const handleImageError = (event) => {
|
||||
event.target.style.display = 'none';
|
||||
};
|
||||
|
||||
@ -35,9 +35,7 @@
|
||||
|
||||
<!-- QR Code -->
|
||||
<div class="flex justify-center mb-4">
|
||||
<div class="p-2 border border-gray-300 rounded-lg">
|
||||
<img :src="qrCodeUrl" alt="QR Code" class="w-36 h-36" />
|
||||
</div>
|
||||
<PrintBarcode :code="createdItem?.kode_item" :item="product"/>
|
||||
</div>
|
||||
|
||||
<!-- Item Info -->
|
||||
@ -53,10 +51,6 @@
|
||||
class="flex-1 px-6 py-2 bg-gray-400 hover:bg-gray-500 text-white rounded-lg transition-colors">
|
||||
Selesai
|
||||
</button>
|
||||
<button @click="printQR"
|
||||
class="flex-1 px-6 py-2 bg-C hover:bg-B text-D rounded-lg transition-colors">
|
||||
<i class="fas fa-print mr-1"></i>Print
|
||||
</button>
|
||||
<button @click="addNewItem"
|
||||
class="flex-1 px-6 py-2 bg-C hover:bg-B text-D rounded-lg transition-colors">
|
||||
Buat Lagi
|
||||
@ -69,10 +63,11 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, watch } from 'vue';
|
||||
import { ref, watch } from 'vue';
|
||||
import axios from 'axios';
|
||||
import Modal from './Modal.vue';
|
||||
import InputSelect from './InputSelect.vue';
|
||||
import PrintBarcode from './PrintBarcode.vue';
|
||||
|
||||
// Props
|
||||
const props = defineProps({
|
||||
@ -99,15 +94,7 @@ const success = ref(false);
|
||||
const loading = ref(false);
|
||||
const createdItem = ref(null);
|
||||
|
||||
// QR Code generator - berdasarkan logika dari brankas list
|
||||
const qrCodeUrl = computed(() => {
|
||||
if (createdItem.value && props.product) {
|
||||
const kode_item = createdItem.value.kode_item;
|
||||
const data = kode_item;
|
||||
return `https://api.qrserver.com/v1/create-qr-code/?size=150x150&data=${encodeURIComponent(data)}`;
|
||||
}
|
||||
return "";
|
||||
});
|
||||
// QR Code rendering/printing moved to PrintBarcode component
|
||||
|
||||
|
||||
// Methods
|
||||
@ -174,105 +161,6 @@ const addNewItem = () => {
|
||||
createdItem.value = null;
|
||||
};
|
||||
|
||||
// Fungsi print berdasarkan logika dari brankas list
|
||||
const printQR = () => {
|
||||
if (qrCodeUrl.value && createdItem.value && props.product) {
|
||||
const printWindow = window.open('', '_blank');
|
||||
const itemCode = createdItem.value.kode_item;
|
||||
|
||||
printWindow.document.write(`
|
||||
<html>
|
||||
<head>
|
||||
<title>Print QR Code - ${itemCode}</title>
|
||||
<style>
|
||||
@page {
|
||||
size: 38mm 25mm;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
width: 38mm;
|
||||
height: 25mm;
|
||||
padding: 2mm;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.qr-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.qr-img {
|
||||
width: 18mm;
|
||||
height: 18mm;
|
||||
margin-bottom: 1mm;
|
||||
}
|
||||
|
||||
.item-code {
|
||||
font-weight: bold;
|
||||
font-size: 8pt;
|
||||
margin-bottom: 0.5mm;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 34mm;
|
||||
}
|
||||
|
||||
.item-name {
|
||||
font-size: 6pt;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 34mm;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.item-weight {
|
||||
font-size: 6pt;
|
||||
color: #666;
|
||||
margin-top: 0.5mm;
|
||||
}
|
||||
|
||||
@media print {
|
||||
body {
|
||||
-webkit-print-color-adjust: exact;
|
||||
print-color-adjust: exact;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="qr-container">
|
||||
<img id="qr-img" class="qr-img" src="${qrCodeUrl.value}" alt="QR Code" />
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`);
|
||||
|
||||
printWindow.document.close();
|
||||
|
||||
const img = printWindow.document.getElementById("qr-img");
|
||||
img.onload = () => {
|
||||
printWindow.focus();
|
||||
printWindow.print();
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
// Reset state
|
||||
|
||||
@ -55,7 +55,7 @@ const sizeClass = computed(() => {
|
||||
})
|
||||
|
||||
const handleOverlayClick = () => {
|
||||
if (clickOutside.value) {
|
||||
if (props.clickOutside.value) {
|
||||
emit('close')
|
||||
}
|
||||
}
|
||||
|
||||
141
resources/js/components/PrintBarcode.vue
Normal file
141
resources/js/components/PrintBarcode.vue
Normal file
@ -0,0 +1,141 @@
|
||||
<template>
|
||||
<div class="text-center">
|
||||
<div class="p-2 border rounded-lg inline-block">
|
||||
<img :src="barcodeUrl" alt="Barcode" class="w-36 h-12" id="barcode-img" />
|
||||
</div>
|
||||
|
||||
<div v-if="item.nama" class="mt-2 text-sm text-gray-700 font-medium">
|
||||
{{ item.nama }}
|
||||
(<span v-if="item.berat" class="mt-1 text-xs text-gray-500">{{ item.berat }} g</span>)
|
||||
</div>
|
||||
|
||||
<div class="flex justify-center mt-3">
|
||||
<button @click="printBarcode" class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-400 transition">
|
||||
<i class="fas fa-print mr-2"></i>Cetak
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
code: { type: String, required: true },
|
||||
item: { type: Object, required: true },
|
||||
});
|
||||
|
||||
const barcodeUrl = computed(() => {
|
||||
if (!props.code) return '';
|
||||
// Using bwip-js API for Code128 barcode
|
||||
return `https://bwipjs-api.metafloor.com/?bcid=code128&text=${encodeURIComponent(props.code)}&scale=2`;
|
||||
});
|
||||
|
||||
const printBarcode = () => {
|
||||
if (!barcodeUrl.value || !props.code) return;
|
||||
|
||||
const printWindow = window.open('', '_blank');
|
||||
const kode = props.code || 'N/A';
|
||||
const nama = props.item.nama || 'N/A';
|
||||
const berat = props.item.berat ? `(${props.item.berat} g)` : '';
|
||||
|
||||
printWindow.document.write(`
|
||||
<html>
|
||||
<head>
|
||||
<title>Print Barcode - ${kode}</title>
|
||||
<style>
|
||||
@page {
|
||||
size: 38mm 25mm;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
width: 38mm;
|
||||
height: 25mm;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.label {
|
||||
display: flex;
|
||||
width: 38mm;
|
||||
height: 25mm;
|
||||
}
|
||||
|
||||
.barcode-container {
|
||||
width: 19mm;
|
||||
height: 25mm;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.barcode-img {
|
||||
transform: rotate(90deg);
|
||||
transform-origin: center;
|
||||
max-height: 19mm;
|
||||
max-width: 23mm;
|
||||
}
|
||||
|
||||
.details-container {
|
||||
width: 19mm;
|
||||
height: 25mm;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.item-name {
|
||||
display: block;
|
||||
width: 23mm;
|
||||
transform: rotate(270deg);
|
||||
transform-origin: center;
|
||||
font-size: 6pt;
|
||||
line-height: 1.1;
|
||||
text-align: left;
|
||||
white-space: normal;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="label">
|
||||
<div class="barcode-container">
|
||||
<img id="barcode-img" class="barcode-img"
|
||||
src="${barcodeUrl.value}" alt="Barcode" />
|
||||
</div>
|
||||
|
||||
<div class="details-container">
|
||||
<div class="item-name">${nama} ${berat}</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`);
|
||||
|
||||
printWindow.document.close();
|
||||
|
||||
const img = printWindow.document.getElementById('barcode-img');
|
||||
if (img) {
|
||||
img.onload = () => {
|
||||
printWindow.focus();
|
||||
printWindow.print();
|
||||
};
|
||||
} else {
|
||||
// Fallback
|
||||
printWindow.focus();
|
||||
printWindow.print();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@ -86,24 +86,7 @@
|
||||
<!-- Pop-up pindah item -->
|
||||
<div v-if="isPopupVisible" class="fixed inset-0 bg-black/50 flex items-center justify-center p-4 z-50">
|
||||
<div class="bg-white rounded-xl shadow-lg max-w-sm w-full p-6 relative">
|
||||
<div class="flex justify-center mb-2">
|
||||
<div class="p-2 border rounded-lg">
|
||||
<img :src="qrCodeUrl" alt="QR Code" class="w-36 h-36" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-center text-D font-bold text-lg">
|
||||
{{ selectedItem.kode_item }}
|
||||
</div>
|
||||
<div class="text-center text-gray-700 font-medium mb-3">
|
||||
{{ selectedItem.produk.nama }}
|
||||
</div>
|
||||
|
||||
<div class="flex justify-center mb-4">
|
||||
<button @click="printQR" class="bg-D text-A px-4 py-2 rounded hover:bg-D/80 transition">
|
||||
<i class="fas fa-print mr-2"></i>Cetak
|
||||
</button>
|
||||
</div>
|
||||
<PrintBarcode :code="selectedItem.kode_item" :item="selectedItem.produk" />
|
||||
|
||||
<!-- Dropdown -->
|
||||
<div class="mb-4">
|
||||
@ -148,6 +131,7 @@ import { ref, onMounted, computed } from "vue";
|
||||
import axios from "axios";
|
||||
import InputSelect from "./InputSelect.vue";
|
||||
import ConfirmDeleteModal from './ConfirmDeleteModal.vue';
|
||||
import PrintBarcode from './PrintBarcode.vue';
|
||||
|
||||
const isAdmin = localStorage.getItem("role") === "owner";
|
||||
|
||||
@ -167,113 +151,6 @@ const selectedItem = ref(null);
|
||||
const selectedTrayId = ref("");
|
||||
const showDeleteConfirm = ref(false);
|
||||
|
||||
// QR Code generator
|
||||
const qrCodeUrl = computed(() => {
|
||||
if (selectedItem.value) {
|
||||
const data = selectedItem.value.kode_item;
|
||||
return `https://api.qrserver.com/v1/create-qr-code/?size=150x150&data=${encodeURIComponent(data)}`;
|
||||
}
|
||||
return "";
|
||||
});
|
||||
|
||||
const printQR = () => {
|
||||
if (qrCodeUrl.value && selectedItem.value) {
|
||||
const printWindow = window.open('', '_blank');
|
||||
const itemCode = selectedItem.value.kode_item;
|
||||
|
||||
printWindow.document.write(`
|
||||
<html>
|
||||
<head>
|
||||
<title>Print QR Code - ${itemCode}</title>
|
||||
<style>
|
||||
@page {
|
||||
size: 38mm 25mm;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
width: 38mm;
|
||||
height: 25mm;
|
||||
padding: 2mm;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.qr-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.qr-img {
|
||||
width: 18mm;
|
||||
height: 18mm;
|
||||
margin-bottom: 1mm;
|
||||
}
|
||||
|
||||
.item-code {
|
||||
font-weight: bold;
|
||||
font-size: 8pt;
|
||||
margin-bottom: 0.5mm;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 34mm;
|
||||
}
|
||||
|
||||
.item-name {
|
||||
font-size: 6pt;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 34mm;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.item-weight {
|
||||
font-size: 6pt;
|
||||
color: #666;
|
||||
margin-top: 0.5mm;
|
||||
}
|
||||
|
||||
@media print {
|
||||
body {
|
||||
-webkit-print-color-adjust: exact;
|
||||
print-color-adjust: exact;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="qr-container">
|
||||
<img id="qr-img" class="qr-img" src="${qrCodeUrl.value}" alt="QR Code" />
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`);
|
||||
|
||||
printWindow.document.close();
|
||||
|
||||
const img = printWindow.document.getElementById("qr-img");
|
||||
img.onload = () => {
|
||||
printWindow.focus();
|
||||
printWindow.print();
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const confirmDelete = async () => {
|
||||
if (!selectedItem.value) return;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user