Add a component to scan books
This commit is contained in:
40
front/package-lock.json
generated
40
front/package-lock.json
generated
@@ -8,6 +8,7 @@
|
||||
"name": "personal-library-manager",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@zxing/library": "^0.21.3",
|
||||
"bootstrap-icons-vue": "^1.11.3",
|
||||
"pinia": "^3.0.3",
|
||||
"vue": "^3.5.18",
|
||||
@@ -62,7 +63,6 @@
|
||||
"integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
"@babel/generator": "^7.28.3",
|
||||
@@ -1861,13 +1861,34 @@
|
||||
"integrity": "sha512-+2k1EQpnYuVuu3N7atWyG3/xoFWIVJZq4Mz8XNOdScFI0etES75fbny/oU4lKWk/577P1zmg0ioYvpGEDZ3DLw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@zxing/library": {
|
||||
"version": "0.21.3",
|
||||
"resolved": "https://registry.npmjs.org/@zxing/library/-/library-0.21.3.tgz",
|
||||
"integrity": "sha512-hZHqFe2JyH/ZxviJZosZjV+2s6EDSY0O24R+FQmlWZBZXP9IqMo7S3nb3+2LBWxodJQkSurdQGnqE7KXqrYgow==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ts-custom-error": "^3.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.4.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@zxing/text-encoding": "~0.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@zxing/text-encoding": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@zxing/text-encoding/-/text-encoding-0.9.0.tgz",
|
||||
"integrity": "sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==",
|
||||
"license": "(Unlicense OR Apache-2.0)",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.15.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@@ -1995,7 +2016,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"caniuse-lite": "^1.0.30001737",
|
||||
"electron-to-chromium": "^1.5.211",
|
||||
@@ -2324,7 +2344,6 @@
|
||||
"integrity": "sha512-QePbBFMJFjgmlE+cXAlbHZbHpdFVS2E/6vzCy7aKlebddvl1vadiC4JFV5u/wqTkNUwEV8WrQi257jf5f06hrg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.8.0",
|
||||
"@eslint-community/regexpp": "^4.12.1",
|
||||
@@ -2386,7 +2405,6 @@
|
||||
"integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"eslint-config-prettier": "bin/cli.js"
|
||||
},
|
||||
@@ -3324,7 +3342,6 @@
|
||||
"integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"prettier": "bin/prettier.cjs"
|
||||
},
|
||||
@@ -3578,6 +3595,15 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/ts-custom-error": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-custom-error/-/ts-custom-error-3.3.1.tgz",
|
||||
"integrity": "sha512-5OX1tzOjxWEgsr/YEUWSuPrQ00deKLh6D7OTWcvNHm12/7QPyRh8SYpyWvA4IZv8H/+GQWQEh/kwo95Q9OVW1A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/type-check": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
||||
@@ -3662,7 +3688,6 @@
|
||||
"integrity": "sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.0",
|
||||
"fdir": "^6.5.0",
|
||||
@@ -3848,7 +3873,6 @@
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.21.tgz",
|
||||
"integrity": "sha512-xxf9rum9KtOdwdRkiApWL+9hZEMWE90FHh8yS1+KJAiWYh+iGWV1FquPjoO9VUHQ+VIhsCXNNyZ5Sf4++RVZBA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "3.5.21",
|
||||
"@vue/compiler-sfc": "3.5.21",
|
||||
|
||||
@@ -7,13 +7,14 @@
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"dev": "vite --host",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"lint": "eslint . --fix",
|
||||
"format": "prettier --write src/"
|
||||
},
|
||||
"dependencies": {
|
||||
"@zxing/library": "^0.21.3",
|
||||
"bootstrap-icons-vue": "^1.11.3",
|
||||
"pinia": "^3.0.3",
|
||||
"vue": "^3.5.18",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { useRouter, RouterLink } from 'vue-router'
|
||||
import NavBarSearch from './NavBarSearch.vue'
|
||||
import { useAuthStore } from './auth.store.js'
|
||||
@@ -6,6 +7,8 @@
|
||||
const authStore = useAuthStore();
|
||||
const router = useRouter();
|
||||
|
||||
const popupDisplay = ref(false)
|
||||
|
||||
function logout() {
|
||||
authStore.logout();
|
||||
router.push('/');
|
||||
@@ -21,7 +24,7 @@
|
||||
</div>
|
||||
<div class="navbar-menu">
|
||||
<div class="navbar-start">
|
||||
<NavBarSearch/>
|
||||
<NavBarSearch @open-scan-popup="popupDisplay = true"/>
|
||||
<RouterLink v-if="authStore.user" to="/books" class="navbar-item" activeClass="is-active">
|
||||
{{ $t('navbar.mybooks')}}
|
||||
</RouterLink>
|
||||
|
||||
62
front/src/Modal.vue
Normal file
62
front/src/Modal.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<script setup>
|
||||
import ScanBook from './ScanBook.vue'
|
||||
import { ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const open = ref(false);
|
||||
const router = useRouter();
|
||||
|
||||
function onBarcodeDecode(isbn) {
|
||||
open.value = false;
|
||||
router.push('/search/' + isbn)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="control">
|
||||
<button @click="open = true" class="button">
|
||||
<span class="icon" title="Barcode">
|
||||
<b-icon-upc-scan />
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<Teleport to="body">
|
||||
<div v-if="open">
|
||||
<div @click="open = false" class="modal-backdrop"></div>
|
||||
<div class="modal has-background-dark">
|
||||
<ScanBook @read-barcode="onBarcodeDecode"/>
|
||||
</div>
|
||||
</div>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.modal-backdrop {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.modal {
|
||||
height: 70%;
|
||||
width: 70%;
|
||||
top: 10%;
|
||||
left: 15%;
|
||||
box-shadow: 2px 2px 20px 1px;
|
||||
overflow-x: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 15px;
|
||||
z-index: 1000;
|
||||
}
|
||||
</style>
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import Modal from './Modal.vue'
|
||||
|
||||
const searchterm = ref("");
|
||||
const router = useRouter();
|
||||
@@ -26,6 +27,7 @@
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<Modal/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
31
front/src/ScanBook.vue
Normal file
31
front/src/ScanBook.vue
Normal file
@@ -0,0 +1,31 @@
|
||||
<script setup>
|
||||
import { useTemplateRef, onMounted, ref } from 'vue'
|
||||
import { BrowserMultiFormatReader } from "@zxing/library";
|
||||
|
||||
const emit = defineEmits('readBarcode')
|
||||
|
||||
const scanResult = ref(null);
|
||||
const codeReader = new BrowserMultiFormatReader();
|
||||
const scannerElement = useTemplateRef("scanner");
|
||||
|
||||
onMounted(() => {
|
||||
codeReader.decodeFromVideoDevice(undefined, scannerElement.value, (result, err) => {
|
||||
if (result) {
|
||||
emit('readBarcode', result.text)
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function onResult(result) {
|
||||
scanResult.value = result
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1 class="title">{{$t('barcode.title')}}</h1>
|
||||
<div v-if="scanResult">{{scanResult}}</div>
|
||||
<video poster="data:image/gif,AAAA" ref="scanner"></video>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
@@ -10,6 +10,9 @@
|
||||
"signup": "Sign up",
|
||||
"login": "Log In"
|
||||
},
|
||||
"barcode": {
|
||||
"title": "Scan your barcode"
|
||||
},
|
||||
"addbook": {
|
||||
"title":"Title",
|
||||
"author":"Author",
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
"signup": "S'inscrire",
|
||||
"login": "Se connecter"
|
||||
},
|
||||
"barcode": {
|
||||
"title": "Scanner le code-barre"
|
||||
},
|
||||
"addbook": {
|
||||
"title":"Titre",
|
||||
"author":"Auteur",
|
||||
|
||||
@@ -7,12 +7,14 @@ import BookForm from './BookForm.vue'
|
||||
import SignUp from './SignUp.vue'
|
||||
import LogIn from './LogIn.vue'
|
||||
import Home from './Home.vue'
|
||||
import ScanBook from './ScanBook.vue'
|
||||
import SearchBook from './SearchBook.vue'
|
||||
import ImportInventaire from './ImportInventaire.vue'
|
||||
import { useAuthStore } from './auth.store'
|
||||
|
||||
const routes = [
|
||||
{ path: '/', component: Home },
|
||||
{ path: '/scan', component: ScanBook },
|
||||
{ path: '/books', component: BooksBrowser },
|
||||
{ path: '/book/:id', component: BookForm, props: true },
|
||||
{ path: '/author/:id', component: AuthorForm, props: true },
|
||||
|
||||
Reference in New Issue
Block a user