[add] QRCode
This commit is contained in:
parent
0d33233e22
commit
3e7e2df173
23
.gitignore
vendored
Normal file
23
.gitignore
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
11
.gitlab-ci.yml
Normal file
11
.gitlab-ci.yml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
image: alpine:latest
|
||||||
|
pages:
|
||||||
|
stage: deploy
|
||||||
|
script:
|
||||||
|
- mv -f ./dist/* public
|
||||||
|
- echo "Release Complete"
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- public
|
||||||
|
only:
|
||||||
|
- master
|
3
.vscode/extensions.json
vendored
Normal file
3
.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"recommendations": ["johnsoncodehk.volar"]
|
||||||
|
}
|
136
QRScan.php
136
QRScan.php
@ -1,136 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<title>掃QRCode-Instascan</title>
|
|
||||||
<html lang="utf8">
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale = 1,user-scalable = no">
|
|
||||||
|
|
||||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/webrtc-adapter/3.3.3/adapter.min.js"
|
|
||||||
charset="utf8"></script>
|
|
||||||
|
|
||||||
|
|
||||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.10/vue.min.js"
|
|
||||||
charset="utf8"></script>
|
|
||||||
|
|
||||||
<script type="text/javascript" src="https://rawgit.com/schmich/instascan-builds/master/instascan.min.js"
|
|
||||||
charset="utf8"></script>
|
|
||||||
|
|
||||||
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"
|
|
||||||
integrity="sha256-4+XzXVhsDmqanXGHaHvgh1gMQKX40OUvDEBTu8JcmNs=" crossorigin="anonymous" charset="utf8"></script>
|
|
||||||
|
|
||||||
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
|
|
||||||
<link href="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
|
|
||||||
<script src="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
|
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.1/css/all.min.css" />
|
|
||||||
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
background: #FFFFFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-container {
|
|
||||||
|
|
||||||
width: 300px;
|
|
||||||
height: 300px;
|
|
||||||
overflow: hidden;
|
|
||||||
border: 3px dotted #ccc;
|
|
||||||
margin: 0px auto;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div id="Webapp">
|
|
||||||
<div class="sidebar">
|
|
||||||
<h2 style="text-align:center;">
|
|
||||||
<? echo $_SERVER['HTTP_HOST']?> <br>掃描位置 QRCode
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="preview-container">
|
|
||||||
<div style="text-align:center;" id="saysome">QR 掃描器(掃描時請對焦)</div>
|
|
||||||
<video id="preview"></video>
|
|
||||||
</div>
|
|
||||||
<section class="cameras text-center">
|
|
||||||
<h2 class="text-center">鏡頭選擇</h2>
|
|
||||||
<span v-if="cameras.length === 0" class="empty">找不到鏡頭</span>
|
|
||||||
<span v-for="camera in cameras">
|
|
||||||
<span v-if="camera.id != activeCameraId" :title="formatName(camera.name)">
|
|
||||||
<a @click.stop="selectCamera(camera)" class="btn btn-info text-white btn-lg"><i
|
|
||||||
class="fas fa-camera-retro"></i> 切換鏡頭</a>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</section>
|
|
||||||
<section class="scans">
|
|
||||||
<!--
|
|
||||||
<h2>評分位置</h2>
|
|
||||||
<ul v-if="scans.length === 0">
|
|
||||||
<li class="empty">未掃描任何位置</li>
|
|
||||||
</ul>
|
|
||||||
-->
|
|
||||||
<transition-group name="scans" tag="ul">
|
|
||||||
<!--li v-for="scan in scans" :key="scan.date" :title="scan.content">{{ scan.content }}</li-->
|
|
||||||
|
|
||||||
</transition-group>
|
|
||||||
<!-- <div><a href="#" id="meL"></a></div> -->
|
|
||||||
<div id="meL"></div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<!--script type="text/javascript" src="Webapp.js" charset="utf8"></script-->
|
|
||||||
<script>
|
|
||||||
var Webapp = new Vue({
|
|
||||||
el: '#Webapp',
|
|
||||||
data: {
|
|
||||||
|
|
||||||
scanner: null,
|
|
||||||
activeCameraId: 1,
|
|
||||||
cameras: [],
|
|
||||||
scans: []
|
|
||||||
},
|
|
||||||
mounted: function () {
|
|
||||||
var self = this;
|
|
||||||
self.scanner = new Instascan.Scanner({ video: document.getElementById('preview'), mirror: false, scanPeriod: 5 });
|
|
||||||
self.scanner.addListener('scan', function (content, image) {
|
|
||||||
self.scans.unshift({ date: +(Date.now()), content: content });
|
|
||||||
if (content) {
|
|
||||||
document.getElementById("meL").innerHTML = content;
|
|
||||||
// document.getElementById("meL").href = content;
|
|
||||||
// document.getElementById("meL").click();
|
|
||||||
} else {
|
|
||||||
alert("QRcode 可能不正確!!");
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
Instascan.Camera.getCameras().then(function (cameras) {
|
|
||||||
self.cameras = cameras;
|
|
||||||
if (cameras.length > 0) {
|
|
||||||
self.activeCameraId = cameras[cameras.length - 1].id;
|
|
||||||
self.scanner.start(cameras[cameras.length - 1]);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
console.error('沒找到相機元素!!');
|
|
||||||
}
|
|
||||||
}).catch(function (e) {
|
|
||||||
console.error(e);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
formatName: function (name) {
|
|
||||||
return name || '(unknown)';
|
|
||||||
},
|
|
||||||
selectCamera: function (camera) {
|
|
||||||
this.activeCameraId = camera.id;
|
|
||||||
this.scanner.start(camera);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
16
README.md
Normal file
16
README.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# Vue 3 + TypeScript + Vite
|
||||||
|
|
||||||
|
This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
||||||
|
|
||||||
|
## Recommended IDE Setup
|
||||||
|
|
||||||
|
- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.volar)
|
||||||
|
|
||||||
|
## Type Support For `.vue` Imports in TS
|
||||||
|
|
||||||
|
Since TypeScript cannot handle type information for `.vue` imports, they are shimmed to be a generic Vue component type by default. In most cases this is fine if you don't really care about component prop types outside of templates. However, if you wish to get actual prop types in `.vue` imports (for example to get props validation when using manual `h(...)` calls), you can enable Volar's Take Over mode by following these steps:
|
||||||
|
|
||||||
|
1. Run `Extensions: Show Built-in Extensions` from VS Code's command palette, look for `TypeScript and JavaScript Language Features`, then right click and select `Disable (Workspace)`. By default, Take Over mode will enable itself if the default TypeScript extension is disabled.
|
||||||
|
2. Reload the VS Code window by running `Developer: Reload Window` from the command palette.
|
||||||
|
|
||||||
|
You can learn more about Take Over mode [here](https://github.com/johnsoncodehk/volar/discussions/471).
|
BIN
assets/qr.png
BIN
assets/qr.png
Binary file not shown.
Before Width: | Height: | Size: 556 B |
BIN
assets/setup.jpg
BIN
assets/setup.jpg
Binary file not shown.
Before Width: | Height: | Size: 276 KiB |
1
dist/assets/index.06d14ce2.css
vendored
Normal file
1
dist/assets/index.06d14ce2.css
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
#app{font-family:Avenir,Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-align:center;color:#2c3e50;margin-top:60px}
|
33
dist/assets/index.42417b10.js
vendored
Normal file
33
dist/assets/index.42417b10.js
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
dist/favicon.ico
vendored
Normal file
BIN
dist/favicon.ico
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
19
dist/index.html
vendored
Normal file
19
dist/index.html
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" href="./favicon.ico" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>HTML5 QR Code with Vue js</title>
|
||||||
|
<script type="module" crossorigin src="./assets/index.42417b10.js"></script>
|
||||||
|
<link rel="stylesheet" href="./assets/index.06d14ce2.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
21
index.html
21
index.html
@ -1,10 +1,17 @@
|
|||||||
<!doctype html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html lang="en">
|
||||||
<script>
|
|
||||||
(function () {
|
<head>
|
||||||
window.open("./QRScan.php", '_self');
|
<meta charset="UTF-8" />
|
||||||
})()
|
<link rel="icon" href="/favicon.ico" />
|
||||||
</script>
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>HTML5 QR Code with Vue js</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.ts"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
48
instascan.min.js
vendored
48
instascan.min.js
vendored
File diff suppressed because one or more lines are too long
1281
package-lock.json
generated
Normal file
1281
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
20
package.json
Normal file
20
package.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "qrcode",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vue-tsc --noEmit && vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"html5-qrcode": "^2.2.1",
|
||||||
|
"vue": "^3.2.25"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitejs/plugin-vue": "^2.3.1",
|
||||||
|
"typescript": "^4.5.4",
|
||||||
|
"vite": "^2.9.5",
|
||||||
|
"vue-tsc": "^0.34.7"
|
||||||
|
}
|
||||||
|
}
|
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
20
src/App.vue
Normal file
20
src/App.vue
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
// This starter template is using Vue 3 <script setup> SFCs
|
||||||
|
// Check out https://vuejs.org/api/sfc-script-setup.html#script-setup
|
||||||
|
import QRCode from './components/QRCode.vue';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<QRCode />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#app {
|
||||||
|
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
text-align: center;
|
||||||
|
color: #2c3e50;
|
||||||
|
margin-top: 60px;
|
||||||
|
}
|
||||||
|
</style>
|
BIN
src/assets/logo.png
Normal file
BIN
src/assets/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.7 KiB |
52
src/components/HelloWorld.vue
Normal file
52
src/components/HelloWorld.vue
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
defineProps<{ msg: string }>()
|
||||||
|
|
||||||
|
const count = ref(0)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<h1>{{ msg }}</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Recommended IDE setup:
|
||||||
|
<a href="https://code.visualstudio.com/" target="_blank">VS Code</a>
|
||||||
|
+
|
||||||
|
<a href="https://github.com/johnsoncodehk/volar" target="_blank">Volar</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>See <code>README.md</code> for more information.</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<a href="https://vitejs.dev/guide/features.html" target="_blank">
|
||||||
|
Vite Docs
|
||||||
|
</a>
|
||||||
|
|
|
||||||
|
<a href="https://v3.vuejs.org/" target="_blank">Vue 3 Docs</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<button type="button" @click="count++">count is: {{ count }}</button>
|
||||||
|
<p>
|
||||||
|
Edit
|
||||||
|
<code>components/HelloWorld.vue</code> to test hot module replacement.
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
a {
|
||||||
|
color: #42b983;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
margin: 0 0.5em;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
background-color: #eee;
|
||||||
|
padding: 2px 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #304455;
|
||||||
|
}
|
||||||
|
</style>
|
22
src/components/QRCode.vue
Normal file
22
src/components/QRCode.vue
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import QRCodeScanner from './QRCodeScanner.vue';
|
||||||
|
|
||||||
|
// defineProps<{ msg: string }>()
|
||||||
|
|
||||||
|
let result = ref('')
|
||||||
|
|
||||||
|
const decodedCode = function (decodedText: string, decodedResult: any) {
|
||||||
|
console.log(`Scan result ${decodedText}`, decodedResult);
|
||||||
|
result.value = `內容:${decodedText}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="section" style="width: 500px; margin: auto;">
|
||||||
|
<QRCodeScanner :qrbox="250" :fps="10" style="width: 500px;" @decodedCode="decodedCode">
|
||||||
|
</QRCodeScanner>
|
||||||
|
</div>
|
||||||
|
<div> {{ result }} </div>
|
||||||
|
</template>
|
44
src/components/QRCodeScanner.vue
Normal file
44
src/components/QRCodeScanner.vue
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { Html5QrcodeScanner } from "html5-qrcode";
|
||||||
|
import { nextTick } from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
qrbox: Number,
|
||||||
|
fps: Number,
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits(['decodedCode'])
|
||||||
|
|
||||||
|
var config: any = { fps: props.fps ? props.fps : 10 };
|
||||||
|
let nowresult = "";
|
||||||
|
if (props.qrbox) {
|
||||||
|
config['qrbox'] = props.qrbox;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onScanSuccess(decodedText: string, decodedResult: any) {
|
||||||
|
// $this.$root.$emit('decodedCode', decodedText, decodedResult);
|
||||||
|
if (nowresult === decodedText) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
nowresult = decodedText;
|
||||||
|
emit('decodedCode', decodedText, decodedResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onScanError(decodedText: string, decodedResult: any) {
|
||||||
|
// $this.$root.$emit('decodedCode', decodedText, decodedResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onLoad(): Promise<void> {
|
||||||
|
// 这里获取DOM的是旧值
|
||||||
|
await nextTick()
|
||||||
|
// nextTick 后获取DOM的是更新后的值
|
||||||
|
var html5QrcodeScanner = new Html5QrcodeScanner(
|
||||||
|
"qr-code-full-region", config, undefined);
|
||||||
|
html5QrcodeScanner.render(onScanSuccess, onScanError);
|
||||||
|
}
|
||||||
|
onLoad();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div id="qr-code-full-region"></div>
|
||||||
|
</template>
|
8
src/env.d.ts
vendored
Normal file
8
src/env.d.ts
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
|
declare module '*.vue' {
|
||||||
|
import type { DefineComponent } from 'vue'
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
|
||||||
|
const component: DefineComponent<{}, {}, any>
|
||||||
|
export default component
|
||||||
|
}
|
4
src/main.ts
Normal file
4
src/main.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import { createApp } from 'vue'
|
||||||
|
import App from './App.vue'
|
||||||
|
|
||||||
|
createApp(App).mount('#app')
|
18
tsconfig.json
Normal file
18
tsconfig.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "esnext",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"strict": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"sourceMap": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"lib": ["esnext", "dom"],
|
||||||
|
"skipLibCheck": true
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
|
||||||
|
"references": [{ "path": "./tsconfig.node.json" }]
|
||||||
|
}
|
8
tsconfig.node.json
Normal file
8
tsconfig.node.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "node"
|
||||||
|
},
|
||||||
|
"include": ["vite.config.ts"]
|
||||||
|
}
|
8
vite.config.ts
Normal file
8
vite.config.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import vue from "@vitejs/plugin-vue";
|
||||||
|
import { defineConfig } from "vite";
|
||||||
|
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
base: "./",
|
||||||
|
plugins: [vue()],
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user