1. Perfectly Solve the Cross-domain Problem of Vue2.0+Axios Development and Production Environment
Because bloggers mainly do back-end development and automated operation and maintenance, so the front-end is basically oriented to classmates and search engine programming.. This time, a simple and elegant cross-domain solution with Vue and Axios has been thoroughly worked out, which is suitable for the development environment and production environment!
(1) Configuring development environments across domains in config/index.js
proxyTable: { '/api': { target: 'https://211.64.32.228:8899/', secure: false, changeOrigin: true, pathRewrite: { '^/api': '' }, headers: { Referer: 'https://211.64.32.228:8899' } } }
(2) Configure automatic selection in main.js
import axios from 'axios' import QS from 'qs' Vue.prototype.$axios = axios Vue.prototype.$qs = QS Vue.prototype.baseUrl = process.env.NODE_ENV === "production" ? "https://211.64.32.228:8899" : "/api"
(3) Use Axios in Vue files
this.axios({ method: 'post', url: this.baseUrl + '/helloworld', data: {}, headers: {} }).then((response) => { // do some }).catch((error) => { // do some });
(4) SpringBook configuration allows cross-domain
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; @Configuration public class CorsConfig { private CorsConfiguration buildConfig() { CorsConfiguration corsConfiguration = new CorsConfiguration(); corsConfiguration.addAllowedOrigin("*"); corsConfiguration.addAllowedHeader("*"); corsConfiguration.addAllowedMethod("*"); return corsConfiguration; } @Bean public CorsFilter corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", buildConfig()); return new CorsFilter(source); } }
2. AES+Base64 Encryption and Decryption User Logon Credentials in SpringBook
This year, md5 can be reversed, no matter how old it is, it can't lose its teeth.
import org.apache.tomcat.util.codec.binary.Base64; import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; // Usage method: // PasswordUtil.Encrypt(String) // PasswordUtil.Decrypt(String) public class PasswordUtil { // openssl rand -hex 16 private static String salt = "38350e78e96b83e894b59cc9953af122"; public static String Encrypt(String password) throws Exception { byte[] raw = salt.getBytes(StandardCharsets.UTF_8); SecretKeySpec sRawSpec = new SecretKeySpec(raw, "AES"); Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, sRawSpec); byte[] encrypted = cipher.doFinal(password.getBytes(StandardCharsets.UTF_8)); return new Base64().encodeToString(encrypted); } public static String Decrypt(String password) throws Exception{ byte[] raw = salt.getBytes(StandardCharsets.UTF_8); SecretKeySpec sRawSpec = new SecretKeySpec(raw, "AES"); Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, sRawSpec); byte[] encrypted = new Base64().decode(password); byte[] original = cipher.doFinal(encrypted); return new String(original, StandardCharsets.UTF_8); } }
3. Pure CSS Custom Super-concise File Upload Control input
Mainly to solve the problem of custom CSS style and Vue upload files.... Notes do not write, calm down to a little read to understand!
<template> <div class="upload"> <div class="upload-demo-show"> <input accept="image/png,image/gif,image/jpeg" type="file" class="upload-demo-button" @change="handleUploadDemoButtonChange($event)"> <i class="el-icon-upload upload-btn-icon" :style="uploadTipsStyle"></i> <div class="upload-demo-text" :style="uploadTipsStyle">{{uploadTips}}</div> </div> <div class="upload-button"> <el-button :loading="isProcessUpload" type="success" icon="el-icon-right" circle @click="handleProcessUpload"></el-button> </div> </div> </template> <script> export default { name: "Upload", data: function () { return { uploadImageObject: '', isProcessUpload: false, uploadTips: 'Click Upload', uploadTipsStyle: { 'color': 'gray' } } }, mounted() { this.$store.dispatch('commitNormalStepNumber', 2) }, methods: { handleUploadDemoButtonChange: function (e) { if ((e.target.files[0].size / 1024) >= 400) { this.$message.error('Uploaded files exceed the specified size, Please re-select'); } else { this.uploadImageObject = e.target.files[0]; this.uploadTips = e.target.files[0].name; this.uploadTipsStyle.color = '#409EFF'; } }, handleProcessUpload: function () { this.isProcessUpload = true; // Using FormData to Solve the Problem of Not Obtaining Parameters in POST Remote API let formData = new FormData(); formData.append('uuid', this.$store.getters.getFormUsername); formData.append('file', this.uploadImageObject); this.$axios({ url: this.baseUrl + '/upload/image', method: 'post', headers: { 'Content-Type': 'multipart/form-data', token: this.$store.getters.getToken }, data: formData }).then((response) => { if (response.data === "OK") { this.isProcessUpload = false; this.$router.push({ path: '/finish' }); } else if (response.data === "UNAUTHORIZED"){ this.$message.error('Please login and try again'); } else if (response.data === "INTERNAL_SERVER_ERROR") { this.$message.error('I'm sorry, We made some mistakes.'); } else if (response.data === "BAD_REQUEST") { this.$message.error('Your request is incorrect., Documentation may be a bit problematic.'); } else { this.$message.error('An unpredictable error occurred., Please re-login'); console.log(response.data) } }).catch((err) => { this.$message.error('Network request error'); console.log(err) }); this.isProcessUpload = false; } } } </script> <style scoped> .upload { width: 50%; margin: 0 auto; padding-top: 35px; } .upload-button { padding-top: 25px; text-align: center; } .upload-demo-button { width: 349px; height: 149px; opacity: 0; } .upload-demo-button:hover { cursor: pointer; } .upload-demo-show { border-radius: 5px; border: lightgray 1px dashed; width: 350px; height: 150px; margin: 0 auto; position: relative; } .upload-btn-icon { position: absolute; top: 15%; left: 40%; font-size: 50pt; z-index: -1; } .upload-demo-text { z-index: -1; position: absolute; top: 58%; width: 250px; text-align: center; left: 50%; font-size: 10pt; margin-left: -125px; } </style>
IV. Vuex Best Practices
(1) Defining store/index.js should actually be modular beforehand, but I'm too lazy.
import Vue from 'vue'; import Vuex from 'vuex'; Vue.use(Vuex); const state = { token: '' }; const getters = { getToken(state) { return state.token } }; const mutations = { setToken(state, token) { state.token = token } }; const actions = { commitToken({commit}, token) { return commit('setToken', token) } }; const store = new Vuex.Store( { state, getters, mutations, actions } ); export default store;
(2) Reference in main.js
import store from './store' /* eslint-disable no-new */ new Vue({ el: '#app', router, components: {App}, template: '<App/>', store })
(3) Reference in Vue Components
this.$store.dispatch('commitToken', value); // Store data to Store this.$store.getters.getToken; // Read the data in Store
V. Vue-router jump
However, the official documents are clearly written, but I am not bothered to look through the official documents.
this.$router.push({ path: '/normal' });
6. Nginx cooperates with SpringBook to realize HTTPS strengthening and API gateway load balancing
user nginx; worker_processes 16; error_log logs/error.log; pid logs/nginx.pid; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log logs/access.log main; sendfile on; keepalive_timeout 65; gzip on; // Setting up reverse proxy upstream apiserver { server 127.0.0.1:8090 weight=1; server 127.0.0.1:8091 weight=1; server 127.0.0.1:8092 weight=1; server 127.0.0.1:8093 weight=1; } server { listen 80; server_name upload-image; // Setting up HTTPS forcefulness rewrite ^(.*)$ https://$host$1 permanent; } // API interface uses HTTPS server { listen 8899 ssl; server_name upload-image-api; // Configuring HTTPS ssl_certificate ../ssl/server.crt; ssl_certificate_key ../ssl/server.key; ssl_session_cache shared:SSL:1m; ssl_session_timeout 5m; ssl_ciphers ALL:!DH:!EXPORT:!RC4:+HIGH:+MEDIUM:-LOW:!aNULL:!eNULL; ssl_prefer_server_ciphers on; // Adding Supported HTTPS Protocol ssl_protocols TLSv1 TLSv1.1 TLSv1.2; location / { proxy_pass http://apiserver; } } server { // Jump the front-end static distribution settings to the interface listen 443 ssl; server_name upload-image-ssl; ssl_certificate ../ssl/server.crt; ssl_certificate_key ../ssl/server.key; ssl_session_cache shared:SSL:1m; ssl_session_timeout 5m; ssl_ciphers ALL:!DH:!EXPORT:!RC4:+HIGH:+MEDIUM:-LOW:!aNULL:!eNULL; ssl_prefer_server_ciphers on; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; location / { root html; index index.html index.htm; } error_page 404 /404.html; error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } }
VII. The Vue components are in the middle horizontally and vertically.
That's the problem. I can't remember how to do it, but I can always Baidu, even Google doesn't need to do it.
(1) index.html, set on the style tag
html, body { margin: 0; padding: 0; }
(2) Component style
.box { top: 50%; left: 50%; position: absolute; transform: translate(-50%, -50%); min-width: 450px; max-width: 550px; min-height: 500px; max-height: 550px; }
8. Interaction between Vue and PC Camera
Recently, I trained a neural network for face recognition with Caffe, and we can log in face in the future.~
So, let's get the PC's webcam first... because the photos taken by the computer are not very pleasant, so the mirror is flipped.
But, you're so ugly... It's my CSS that makes you look better, haha ha.~
<template> <div class="login-with-facedetection-main box"> <div class="login-with-facedetection-main-head"> <img src="../../assets/qimo2-blue.svg" alt="" width="65" height="65"> </div> <div class="login-with-title">Green Mang Cloud(Qimo Cloud)Console</div> <div class="login-with-subtitle">Face detection login, click on the picture to start detection</div> <div style="width: 100%; height: 10px"></div> <div class="login-with-form" @click="handleFaceDetection" v-loading="hasLoginFormLoading"> <video class="video-box" src="" autoplay="autoplay" v-if="hasCameraOpen"></video> <img class="photo-box" :src="faceImage" alt="" v-if="hasTakePhoto"> <canvas id="canvas" width="270" height="270" style="display: none;"></canvas> </div> <LoginType/> </div> </template> <script> import LoginType from "../common/LoginType"; export default { name: "LoginWithFaceDetection", components: {LoginType}, data: function () { return { streamPicture: '', faceImage: '', hasCameraOpen: true, hasTakePhoto: false, faceImageFile: '', hasLoginFormLoading: false, clickTimes: 0 } }, methods: { handleFaceDetection: function () { if (this.clickTimes === 0) { let video = document.querySelector('video'); this.takePhoto(); this.closeCamera(); this.postFaceDetection(); console.log("Face De"); this.clickTimes = 1; } // TODO: Show bullet windows, duplicate submissions }, connectToCamera: function () { let self = this; navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia; if (navigator.getUserMedia) { // Call User Media Device, Visit Camera navigator.getUserMedia({ video: { width: 270, height: 270 } }, function (stream) { let video = document.querySelector('video'); video.srcObject = stream; self.streamPicture = stream; video.onloadedmetadata = function (e) { video.play(); }; }, function (err) { // TODO: Display error pop-ups, unsupported media types }) } else { // TODO: Show the wrong pop-up window, unable to access the camera } }, closeCamera: function () { this.streamPicture.getTracks()[0].stop(); }, takePhoto: function () { let video = document.querySelector('video'); let canvas = document.getElementById('canvas'); let context = canvas.getContext('2d'); context.drawImage(video, 0, 0, 270, 270); let image = canvas.toDataURL('image/png'); this.hasCameraOpen = false; this.hasTakePhoto = true; this.faceImage = image; this.faceImageFile = this.dataURLtoFile(image, 'face-detection.png') }, dataURLtoFile: function (dataurl, filename) { let arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1], bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n); while (n--) { u8arr[n] = bstr.charCodeAt(n); } return new File([u8arr], filename, {type: mime}); }, postFaceDetection: function () { this.hasLoginFormLoading = true; // TODO: Send pictures for recognition setInterval(() => { this.hasLoginFormLoading = false; clearInterval(); }, 5000); } }, mounted() { this.connectToCamera(); }, destroyed() { this.closeCamera(); } } </script> <style scoped> .photo-box { margin-top: 0; width: 270px; height: 270px; border-radius: 20px; transform: rotateY(180deg); -webkit-transform: rotateY(180deg); /* Safari And Chrome */ -moz-transform: rotateY(180deg); } .video-box { transform: rotateY(180deg); -webkit-transform: rotateY(180deg); /* Safari And Chrome */ -moz-transform: rotateY(180deg); margin-top: 0; width: 270px; height: 270px; object-fit: contain; border-radius: 20px; } .login-with-facedetection-main { width: 450px; height: 500px; box-shadow: 0 0 10px lightgrey; } .login-with-facedetection-main-head { width: 100%; height: 65px; padding-top: 35px; text-align: center; } .login-with-form { width: 270px; margin: 0 auto; height: 270px; text-align: center; background-color: #F1F3F4; border-radius: 20px; } .login-with-title { font-size: 15pt; text-align: center; width: 100%; padding-top: 20px; } .login-with-subtitle { font-size: 11pt; text-align: center; width: 100%; padding-top: 5px; } .box { top: 50%; left: 50%; position: absolute; transform: translate(-50%, -50%); min-width: 450px; max-width: 550px; min-height: 500px; max-height: 550px; } </style>
9. Highly adaptive pricing in Vue
Let the height of this component always equal the height of the browser window!
(1) Component Binding CSS Styles
:style="sidebarStyle"
(2) Dynamic binding of JavaScript data
export default { name: "Admin", data: function () { return { isCollapse: true, sidebarStyle: { 'height': '' } } }, methods: { redressHeight: function () { this.sidebarStyle.height = window.innerHeight + 'px'; } }, created() { window.addEventListener('resize', this.redressHeight); this.redressHeight(); }, destroyed() { window.removeEventListener('resize', this.redressHeight); } }
Continuous updates are ongoing ___________.