1
0
Fork 0

First commit

This commit is contained in:
skywt2003 2022-05-26 08:53:53 +08:00
commit f21dd94017
57 changed files with 25984 additions and 0 deletions

2
.browserslistrc Normal file
View File

@ -0,0 +1,2 @@
> 1%
last 2 versions

6
.env.development Normal file
View File

@ -0,0 +1,6 @@
# just a flag
ENV = 'development'
# base api
VUE_APP_BASE_API = '/dev-api'
VUE_APP_BASE_PORT = 1024

6
.env.production Normal file
View File

@ -0,0 +1,6 @@
# just a flag
ENV = 'production'
# base api
VUE_APP_BASE_API = '/'

21
.gitignore vendored Normal file
View File

@ -0,0 +1,21 @@
.DS_Store
node_modules
dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

29
README.md Normal file
View File

@ -0,0 +1,29 @@
# vue-general
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Run your tests
```
npm run test
```
### Lints and fixes files
```
npm run lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).

12
babel.config.js Normal file
View File

@ -0,0 +1,12 @@
module.exports = {
presets: [
'@vue/app'
],
plugins: [
['import', {
libraryName: 'vant',
libraryDirectory: 'es',
style: true
}, 'vant']
]
}

23828
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

31
package.json Normal file
View File

@ -0,0 +1,31 @@
{
"name": "vue-general",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build"
},
"dependencies": {
"@nutui/nutui": "^3.1.20",
"axios": "0.19.0",
"core-js": "^2.6.5",
"exif-js": "^2.3.0",
"image-conversion": "^1.1.9",
"mockjs": "^1.1.0",
"normalize.css": "7.0.0",
"qrcodejs2": "^0.0.2",
"vant": "^2.1.1",
"vue": "^2.6.10",
"vue-router": "^3.0.3",
"weixin-js-sdk": "^1.4.0-test"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^3.11.0",
"@vue/cli-service": "^3.11.0",
"babel-plugin-import": "^1.11.0",
"stylus": "^0.54.5",
"stylus-loader": "^3.0.2",
"vue-template-compiler": "^2.6.10"
}
}

5
postcss.config.js Normal file
View File

@ -0,0 +1,5 @@
module.exports = {
plugins: {
autoprefixer: {}
}
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

14
public/index.html Normal file
View File

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= webpackConfig.name %></title>
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

23
src/App.vue Normal file
View File

@ -0,0 +1,23 @@
<template>
<div id="app">
<keep-alive>
<router-view/>
</keep-alive>
</div>
</template>
<style lang="stylus">
#app
font-family 'Avenir', Helvetica, Arial, sans-serif
-webkit-font-smoothing antialiased
-moz-osx-font-smoothing grayscale
color #2c3e50
min-height 100vh
position relative
padding 0
margin 0
a
font-weight bold
color #2c3e50
&.router-link-exact-active
color #42b983
</style>

16
src/api/QRCode.js Normal file
View File

@ -0,0 +1,16 @@
import request from 'utils/request'
/**
* 通过人脸认证返回的code获取个人二维码字符串
* @param {string} code
*/
export function GetQRCode(code){
return request({
url:'/api/v1/personverify/qrcode',
method:'get',
params:{
code
}
})
}

47
src/api/admin.js Normal file
View File

@ -0,0 +1,47 @@
import request from 'utils/request'
/**
* 传入页面url获取微信扫一扫调用签名
* @param {string} url
*/
export function GetScanParams(url){
return request({
url:'/app/v1/wechat/getjsparam',
method:'get',
params:{
url
}
})
}
/**
* 通过二维码字符串获取运动员个人信息
* @param {string} code
*/
export function GetPeopleData(code){
return request({
url:'/api/v1/checking/get',
method:'get',
params:{
code
}
})
}
/**
* 传入用户个人codemd5和编号进行绑定
* @param {string} CodeMd5
* @param {number} No
*/
export function AddNo(CodeMd5,No){
return request({
url:'/api/v1/checking/addno',
method:'post',
data:{
CodeMd5,
No
}
})
}

24
src/api/home.js Normal file
View File

@ -0,0 +1,24 @@
import request from 'utils/request'
/**
* 提交人脸及姓名身份证号码进行身份认证
* @param {object} data
*/
export function Login(data){
return request({
url:'/api/v1/wechat/runnerlogin',
method:'post',
data:{
Name: data.Name,
Phone:data.Phone
}
})
}
export function Runnerverify(data){
return request({
url:'/api/v1/wechat/runnerverify',
method:'post',
data
})
}

19
src/api/login.js Normal file
View File

@ -0,0 +1,19 @@
import request from 'utils/request'
/**
* 管理员登陆接口
* @param {number} Name
* @param {numebr} Password
*/
export function Login(loginForm){
return request({
url:'/api/v1/Users/login',
method:'post',
data:{
username:loginForm.username,
password:loginForm.password
}
})
}

BIN
src/assets/image/5g.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 364 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 426 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

BIN
src/assets/image/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

BIN
src/assets/image/lq.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

BIN
src/assets/image/scan1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

BIN
src/assets/image/scan2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
src/assets/image/track.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
src/assets/image/yidong.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

9
src/bus.js Normal file
View File

@ -0,0 +1,9 @@
import busVue from 'vue'
/**
* 在原型上挂载一个新的vue实例对象
* 用于储存公共值的同时借用vue的$emit/$on/$off实现数据监听
*/
export default function (vue){
vue.prototype.$bus = new busVue()
}

View File

@ -0,0 +1,34 @@
import LoadingComponent from './loading.vue'
const Loading = {};
// 注册Loading
Loading.install = function (Vue) {
// 生成一个Vue的子类
// 同时这个子类也就是组件
const LoadingConstructor = Vue.extend(LoadingComponent);
// 生成一个该子类的实例
const instance = new LoadingConstructor();
// 将这个实例挂载在我创建的div上
instance.$mount(document.createElement('div'));
// 并将此div加入全局挂载点内部
document.body.appendChild(instance.$el);
// 通过Vue的原型注册一个方法
// 让所有实例共享这个方法
function handlerloading(text = '', full) {
instance.text = text; //文字是以最后一个show的展示
instance.full = true; //控制填充满屏,但不开放使用;
}
const loading_main = {
show(text = ''){
instance.loadings.push('show') //执行一次添加一个
handlerloading(text) //把text填充
},
hidden(){
instance.loadings.pop() //执行一次减少一个通过数组长度控制显示与否考虑到同时打开多个loading情况
}
}
//将方法挂载全局
Vue.prototype.$Loading = loading_main;
}
export default Loading

View File

@ -0,0 +1,139 @@
<template>
<div id="vue-loading" v-show="loadings.length">
<div class="filter" v-show="full"></div>
<div class="loader-wrap">
<!-- <div class="loader"></div> -->
<div class="logo">
<img src="@/assets/image/loding-logo.png" alt="">
</div>
<div class="hint">{{text}}</div>
</div>
</div>
</template>
<script>
export default {
name:'Loading',
data(){
return {
loadings:[],
text:'',
full:false, //
}
},
}
</script>
<style lang="stylus" scoped>
#vue-loading
width 100%
height 100%
position absolute
left 0
top 0
z-index 999
.filter
width 100%
height 100%
background rgba(255,255,255,0.3);
.loader-wrap
position absolute
left 50%
top 50%
transform translate(-50%,-50%)
display flex
flex-direction column
justify-content center
.logo
// width 100%
text-align center
img
animation scal 0.8s infinite
opacity 0.8
width 16vw
.hint
margin-top 10px
font-size 14px
color #666
text-shadow 1px 1px 3px white
.loader
width 2.5em
height 2.5em
transform rotate(165deg)
position relative
&:before
animation before 2s infinite
&:after
animation after 2s infinite
&:before,
&:after
content ''
position absolute
top 50%
left 50%
display block
width 0.5em
height 0.5em
border-radius 0.25em
transform translate(-50%, -50%)
</style>
<style>
@keyframes scal {
0%{
transform:scale(0.8);
}
50%{
transform:scale(1);
}
100%{
transform:scale(0.8);
}
}
@keyframes before {
0% {
width: 0.5em;
box-shadow: 1em -0.5em rgba(225, 20, 98, 0.75), -1em 0.5em rgba(111, 202, 220, 0.75);
}
35% {
width: 2.5em;
box-shadow: 0 -0.5em rgba(225, 20, 98, 0.75), 0 0.5em rgba(111, 202, 220, 0.75);
}
70% {
width: 0.5em;
box-shadow: -1em -0.5em rgba(225, 20, 98, 0.75), 1em 0.5em rgba(111, 202, 220, 0.75);
}
100% {
box-shadow: 1em -0.5em rgba(225, 20, 98, 0.75), -1em 0.5em rgba(111, 202, 220, 0.75);
}
}
@keyframes after {
0% {
height: 0.5em;
box-shadow: 0.5em 1em rgba(61, 184, 143, 0.75), -0.5em -1em rgba(233, 169, 32, 0.75);
}
35% {
height: 2.5em;
box-shadow: 0.5em 0 rgba(61, 184, 143, 0.75), -0.5em 0 rgba(233, 169, 32, 0.75);
}
70% {
height: 0.5em;
box-shadow: 0.5em -1em rgba(61, 184, 143, 0.75), -0.5em 1em rgba(233, 169, 32, 0.75);
}
100% {
box-shadow: 0.5em 1em rgba(61, 184, 143, 0.75), -0.5em -1em rgba(233, 169, 32, 0.75);
}
}
/**
* Attempt to center the whole thing!
*/
html,
body {
height: 100%;
}
.loader {
position: absolute;
top: calc(50% - 1.25em);
left: calc(50% - 1.25em);
}
</style>

View File

@ -0,0 +1,46 @@
import ToastComponent from './toast.vue'
const Toast = {};
// 注册Toast
Toast.install = function (Vue) {
// 生成一个Vue的子类
// 同时这个子类也就是组件
const ToastConstructor = Vue.extend(ToastComponent);
// 生成一个该子类的实例
const instance = new ToastConstructor();
// 将这个实例挂载在我创建的div上
instance.$mount(document.createElement('div'));
// 并将此div加入全局挂载点内部
document.body.appendChild(instance.$el);
//定义一个外部的变量,用于控制调用多次提示组件时,清除延时器
let timer;
// 通过Vue的原型注册一个方法
// 让所有实例共享这个方法
class message_main {
constructor(){}
static all_message(msg, type, duration) {
clearTimeout(timer);
timer = setTimeout(() => {
instance.successMessage = []
instance.errorMessage = [];
}, duration);
if(type == 'success'){
instance.successMessage = [msg];
instance.errorMessage = [];
}else if (type == 'error'){
instance.successMessage = [];
instance.errorMessage = [msg];
}
instance.type = type;
}
success(msg, duration = 2000) { //默认两秒停留时长
message_main.all_message({txt:msg,key:new Date().getTime()}, 'success', duration);
}
error(msg, duration = 2000) {
message_main.all_message({txt:msg,key:new Date().getTime()}, 'error', duration);
}
}
//将方法挂载全局
Vue.prototype.$Toast = new message_main();
}
export default Toast

View File

@ -0,0 +1,57 @@
<template>
<div>
<!-- 全局提示框 -->
<transition-group name="fade">
<div name='fade' v-for="item in successMessage" :key='item.key' class="dialog-tips">
<span>{{item.txt}}</span>
</div>
</transition-group>
<transition-group name="fade">
<div v-for="item in errorMessage" :key='item.key' class="dialog-tips error">
<span>{{item.txt}}</span>
</div>
</transition-group>
</div>
</template>
<script>
export default {
data() {
return {
successMessage: [],
errorMessage:[],
type:'success',
};
},
};
</script>
<style lang="stylus" scoped>
$Height = 35px
.dialog-tips
position absolute
z-index 999
width 100vw
height $Height
background-color rgba(123,210,57,0.85)
color white
border-radius 0 0 3px 3px
left 50%
top 0
transform translate(-50%,0)
display flex
justify-content center
transition all 0.2s
align-items center
&.error
background-color rgba(255,70,0,0.85)
span
font-size 14px
line-height $Height
color red
//
.fade-enter-active, .fade-leave-active
transform translate(-50%,0%)
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */
transform translate(-50%,-100%)
</style>

37
src/main.js Normal file
View File

@ -0,0 +1,37 @@
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import 'normalize.css/normalize.css' // 重置、初始化css样式的库
import '@/permission' // 挂载路由后添加路由拦截器(守卫
// import NutUI from "@nutui/nutui";
// import "@nutui/nutui/dist/style.css";
// Vue.use(NutUI)
import Loading from 'globalComponents/Loading'
import Toast from 'globalComponents/Toast'
Vue.use(Toast) //启用全局提示组件
Vue.use(Loading) //使用全局loading组件
/**
* 如果需要bus总线模式
* this.$bus.$data.xxx = xxx
* this.$bus.emit('change',data)
* this.$bus.$on('change',data => console.log(data))
* this.$bus.$off('change')
*/
// import bus from './bus'
// Vue.use(bus) //使用BUS总线模式实现组件间任意传值
Vue.config.productionTip = false //生产模式下关闭vue在控制台的提示
if (process.env.NODE_ENV == 'development'){
require('./mock/index.js');
}
const vm = new Vue({
router,
render: h => h(App),
}).$mount('#app')
export default vm

11
src/mock/home.js Normal file
View File

@ -0,0 +1,11 @@
let runnerlogin = {
code: 0,
data: ['https://blog.skywt.cn/usr/themes/Daydream/assets/img/avatar.png'],
msg: '成功'
}
let home = {
runnerlogin
}
export default home

16
src/mock/index.js Normal file
View File

@ -0,0 +1,16 @@
import Mock from 'mockjs'
import login from './login'
import home from './home'
Mock.mock('/api/v1/Users/login', 'post', () => {
return login.login_request
})
Mock.mock('/api/v1/wechat/runnerlogin', 'post', () => {
return home.runnerlogin
})
Mock.mock('/dev-api/api/v1/wechat/runnerlogin', 'post', () => {
return home.runnerlogin
})

9
src/mock/login.js Normal file
View File

@ -0,0 +1,9 @@
let login_request = {
code: 200
}
let login = {
login_request
}
export default login

47
src/permission.js Normal file
View File

@ -0,0 +1,47 @@
import router from './router'
import getPageTitle from 'utils/getPageTitle'
import { Checkrunner } from 'api/home'
import Vue from 'vue';
import { Toast } from 'vant';
Vue.use(Toast);
// 路由拦截
// router.beforeEach(async(to, from, next) => {
// document.title = getPageTitle(to.meta.title) // set page title
// if(to.name ==='Admin'){
// if(sessionStorage.loginStatus){
// next()
// }else{
// next('/login')
// Toast('请登录!')
// }
// }else if(to.name === 'Home'){
// let skipUrl = 'http://5gmalasong.hnabc.cn/app'
// let wechatUrl = 'http://5gmalasong.hnabc.cn'
// let routerUrl = escape('/#/home')
// Checkrunner().then(res =>{
// if(res.code === 0){
// sessionStorage.Code = res.data.Code
// next('/QRCode')
// }else if(res.code === 3009){
// location.href = wechatUrl + '/wechat/login/runner?returnurl=' + skipUrl + routerUrl
// // alert(baseUrl + 'wechat/login/runner?returnurl=' + baseUrl + '#/home')
// }else{
// next()
// }
// })
// }else{
// next()
// }
// })
//跳转完成后
router.afterEach(() => {
//to do something
})

45
src/router.js Normal file
View File

@ -0,0 +1,45 @@
import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/home'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
redirect:'/login'
},
{
path:'/login',
name:'Login',
component:() => import('./views/login'),
meta:{ title:'登陆' }
},
{
path:'/home',
name:'Home',
component:() => import('./views/home'),
meta:{ title:'人脸注册' }
},
{
path:'/photos',
name:'Photos',
component:() => import('./views/photos'),
meta:{ title:'' }
},
// {
// path:'/QRCode',
// name:'QRCode',
// component:() => import('./views/QRCode'),
// meta:{ title:'身份二维码' }
// },
// {
// path:'/admin',
// name:'Admin',
// component:() => import('./views/admin'),
// meta:{ title:'物资发放' }
// },
]
})

2
src/settings.js Normal file
View File

@ -0,0 +1,2 @@
export const title = '2019长沙国际马拉松纪念照' //标签页title
export const globalSuccessToast = true //是否在request中开启全局请求成功的成功提示基于全局Toast

132
src/styles/variables.styl Normal file
View File

@ -0,0 +1,132 @@
$themeColor = rgb(128,51,134);
$fontColor = rgb(77,9,86)
Button()
background linear-gradient(135deg,#fa2c19 0%,#fa6419 100%)
color white
padding 10px
width 85%
text-align center
border-radius 25px
border: 1px solid transparent
hint()
width 100%
box-sizing border-box
position absolute
text-indent 2em
z-index 9999
padding 10px
background rgba(255,255,255,0.8)
color rgb(0,133,208)
font-size 12px
line-height 1.4em
.home-wrap
overflow hidden
height: 100vh
width: 100%
.bottom-img
position absolute
bottom 0px
width 100%
left 0
.hint
width 100%
box-sizing border-box
position absolute
text-indent 2em
z-index 9999
padding 10px
background rgba(255,255,255,0.3)
backdrop-filter: blur(30)
color white
font-size 12px
line-height 1.4em
bottom 0
.background-wrap
position: fixed
z-index: -1
// .background-image
.top-brand-wrap
color: white
text-align: center
margin-top: 15vh
.info-card
position relative
// z-index 999
width 90%
box-shadow: 0 4px 10px #00000012
-webkit-box-shadow: 0px 4px 10px 0px rgba(0,0,0,.07)
background rgba(255, 255, 255, 0.7)
backdrop-filter: blur(20px)
-webkit-backdrop-filter: blur(20px)
margin 0 auto
margin-top 10vh
z-index 500
padding-bottom 30px
padding-top 30px
.form
width 100%
padding 30px
box-sizing border-box
position relative
// z-index 999
.van-cell
height: 48px
background: transparent
&::after
// border-bottom: 2px solid #f5f6f7
border-bottom: 2px solid #323233
>>>input
height 48px
// >>>field__body
.button
background linear-gradient(135deg,#fa2c19 0%,#fa6419 100%)
color white
padding 10px
width 85%
text-align center
border-radius 25px
border: 1px solid transparent
margin-bottom: 20px
.register
background linear-gradient(135deg,rgb(255,158,13) 0%,rgb(255,167,13) 45%,rgb(255,182,13) 83%,rgb(255,190,13) 100%)
.flex-container
display flex
flex-direction column
justify-content center
align-items center
.main
width 100vw
position relative
z-index 800
.hint
width 70%
border-radius 10px
height 100px
background rgba(255,255,255,0.9)
position absolute
left 50%
top 50%
transform translate(-50%,-50%)
display flex
justify-content center
align-items center
color rgba(0,133,208,1)
.imgs-wrap
overflow hidden
width 100%
padding 8px
padding-top 40px
padding-bottom 50px
box-sizing border-box
.imgs-item
font-size 0
border 1px solid white
width 100%
margin-bottom 10px
border-radius 10px
overflow hidden
img
width 100%

55
src/utils/WeChatScan.js Normal file
View File

@ -0,0 +1,55 @@
// 微信扫一扫封装传入callback在扫码执行并接收一个code参数修改api即可
import wx from 'weixin-js-sdk'
import { GetScanParams } from 'api/admin'
import vm from '@/main.js'
export default function WeChatScan(callback){
GetScanParams(window.location.href.split('#')[0]).then(res => {
if(res.code === 0){
openScan(res.data,callback)
}else{
vm.$Toast.error('签名信息获取失败!')
}
}).catch(err => vm.$Toast.error('签名信息获取失败,请重试!'))
}
function openScan(data,callback){
wx.config({
debug: false, // 开启调试模式
appId: data.appid, // 公众号的唯一标识
timestamp: data.Timestamp, // 生成签名的时间戳
nonceStr: data.NonceStr, // 生成签名的随机串
signature: data.Signature, // 签名
jsApiList: [ 'scanQRCode' ] // 需要使用的JS接口列表
});
wx.ready(function () {
var isBack = callback
wx.checkJsApi({ // 判断当前客户端版本是否支持指定JS接口
jsApiList: [ 'scanQRCode' ],
success: function (res) { // 以键值对的形式返回可用true不可用false。如{"checkResult":{"scanQRCode":true},"errMsg":"checkJsApi:ok"}
if (res.checkResult.scanQRCode === true) {
wx.scanQRCode({ // 微信扫一扫接口
needResult: 1, // 默认为0扫描结果由微信处理1则直接返回扫描结果
scanType: ['qrCode', 'barCode'], // 可以指定扫二维码还是一维码,默认二者都有
success: function (res) {
const getCode = res.resultStr // 当needResult 为 1 时,扫码返回的结果
isBack(getCode)
}
})
} else {
vm.$Toast.error('抱歉,当前客户端版本不支持扫一扫!')
}
},
fail: function (res) { // 检测getNetworkType该功能失败时处理
vm.$Toast.error('失败' + res)
}
})
})
/* 处理失败验证 */
wx.error(function (res) {
vm.$Toast.error('签名信息错误: ' + res.errMsg)
})
}

View File

@ -0,0 +1,8 @@
import { title } from '@/settings'
export default function getPageTitle(pageTitle) {
if (pageTitle) {
return `${pageTitle} - ${title}`
}
return `${title}`
}

167
src/utils/handleImage.js Normal file
View File

@ -0,0 +1,167 @@
import EXIF from 'exif-js' //获取拍照时的信息的库(角度),用于调正图片展示角度
import imageConversion from 'image-conversion' //图片压缩、转换处理库
/**
* 压缩文件函数
* 传入图片-压缩后的尺寸kb-压缩率
* 返回压缩后的文件
* @param {file} imageFile
* @param {number} compressSize
* @param {number} quality
*/
export function JudgeCompress(imageFile, compressSize, quality = 0.99) { //传入图片文件-规定压缩后的尺寸kb-压缩率
return new Promise(function (resolve,reject) {
if(imageFile.size < 100 * 1024) { //如果图片小于规定尺寸,则直接返回
resolve(imageFile)
return
}
imageConversion.compressAccurately(imageFile,{size:compressSize,accuracy:quality,scale:0.5}).then(res =>{ //压缩函数
resolve(res)
})
})
}
/**
* 图片文件转base64 DataUrl
* @param {file} file
*/
export function FiletoDataURL(file){
return new Promise((resolve,reject) => {
imageConversion.filetoDataURL(file).then(res => {
resolve(res)
})
})
}
/**
* 判断文件类型是否为image
* @param {file} file
*/
export function CheckFile(file) {
if (!/image\/\w+/.test(file.type)) {
return false;
}
return file;
}
/**
* 旋转图片总体函数
* 传入文件返回旋转后的文件
* @param {file} imageFile
*/
export function RotateImage(imageFile) {
return new Promise((resolve,reject)=>{
imageConversion.filetoDataURL(imageFile).then(res =>{ //把文件转成图片格式用于旋转
var image = new window.Image();
image.src = res
image.onload = function () {
// 旋转图片
let newImage = handleRotate(image)
imageConversion.dataURLtoFile(newImage.src).then(res => {
resolve(res) //返回的是file文件
})
}
})
})
// 使用如上filetoDataURL插件方法实现文件转url 而 替代本手写方法 by:luqiang 20191012
// var reader = new FileReader();
// reader.onload = function(e){
// }
// reader.readAsDataURL(imageFile); //先挂载onload方法再绑定read事件
}
/**
* 处理旋转函数传入图片返回旋转正确的图片
* @param {img} image
*/
function handleRotate(image){ //处理旋转的函数
var width = image.width;
var height = image.height;
var canvas = document.createElement("canvas")
var ctx = canvas.getContext('2d');
var newImage = new Image();
//旋转图片操作
EXIF.getData(image, function () {
var orientation = EXIF.getTag(this, 'Orientation');
let imageDate = null
switch (orientation) {
//正常状态
case 1:
// alert('旋转0°');
newImage.src = image.src;
break;
//旋转90度
case 6:
// alert('旋转90°');
canvas.height = width;
canvas.width = height;
ctx.rotate(Math.PI / 2);
ctx.translate(0, -height);
ctx.drawImage(image,0,0)
imageDate = canvas.toDataURL('image/jpeg', 1)
newImage.src = imageDate;
break;
//旋转180°
case 3:
// alert('旋转180°');
canvas.height = height;
canvas.width = width;
ctx.rotate(Math.PI);
ctx.translate(-width, -height);
ctx.drawImage(image,0,0)
imageDate = canvas.toDataURL('image/jpeg', 1)
newImage.src = imageDate;
break;
//旋转270°
case 8:
// alert('旋转270°');
canvas.height = width;
canvas.width = height;
ctx.rotate(-Math.PI / 2);
ctx.translate(-height, 0);
ctx.drawImage(image, -810,0) //????安卓要-810
// ctx.drawImage(image)
imageDate = canvas.toDataURL('image/jpeg', 1)
newImage.src = imageDate;
break;
//undefined时不旋转
case undefined:
// alert('旋转0°');
newImage.src = image.src;
break;
default:
newImage.src = image.src;
break;
}
}
);
return newImage;
}
// 压缩图片函数 --> by:luqiang 20191012 使用imageConversion库代替手写操作以精准控制压缩后的文件尺寸
// function Compress(image, quality) {
// var canvas = document.createElement('canvas')
// var ctx = canvas.getContext('2d');
// var width = image.width;
// var height = image.height;
// canvas.width = width;
// canvas.height = height;
// ctx.drawImage(image, 0, 0, width, height);
// //压缩操作
// var imageData = canvas.toDataURL("image/jpeg", quality);
// var imageLength = image.src.length;
// console.log("压缩前:" + imageLength);
// console.log("压缩后:" + imageData.length,imageData);
// console.log("压缩率:" + ~~(100 * (imageLength - imageData.length) / imageLength) + "%");
// return imageData;
// }

52
src/utils/request.js Normal file
View File

@ -0,0 +1,52 @@
import axios from 'axios'
import router from '@/router'
import vm from '@/main.js'
import { globalSuccessToast } from '@/settings'
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
withCredentials: true, // 设置是否在请求中携带cookie
timeout: 30000 // 请求超时时间
})
// request interceptor请求拦截器
service.interceptors.request.use(
config => {
vm.$Loading.show()
// 给config统一配置属性
return config
},
error => {
return Promise.reject(error)
}
)
//response (响应拦截器)
service.interceptors.response.use(
response => {
vm.$Loading.hidden()
const res = response.data
const code = response.status
if (code !== 200) {
vm.$Toast.error(res.msg)
return Promise.reject(new Error(res.msg || 'Error'))
} else {
if (globalSuccessToast && res.code == 0) { //全局成功提示
// vm.$Toast.success('查询成功!') //暂时不要
}
return res
}
},
error => {
vm.$Loading.hidden()
if (error.response.status && error.response.status == 401) { //拦截401代表登陆失效触发页面刷新后再触发路由拦截拦截到重新登录
// 401一般是权限问题或者登录失效
// router.push('/login') //如果项目存在登录验证,应该在这里返回登录页面重新登录;
console.log('response status is 401')
}
vm.$Toast.error(error)
return Promise.reject(error) //请求错误不return出去直接在这里处理toast
}
)
export default service

33
src/utils/waterMark.js Normal file
View File

@ -0,0 +1,33 @@
import waterMark from '@/assets/image/waterMark.png'
function drawAndShareImage(imgUrl){
return new Promise((resolve,reject) => {
var canvas = document.createElement("canvas");
canvas.width = 1280;
canvas.height = 720;
var context = canvas.getContext("2d");
// context.rect(0 , 0 , canvas.width , canvas.height);
// context.fillStyle = "#fff";
// context.fill();
var myImage = new Image();
myImage.crossOrigin = 'Anonymous'; //允许跨域
myImage.src = imgUrl; //背景图片
myImage.onload = function(){
context.drawImage(myImage , 0 , 0 , 1280 , 720);
// context.font = "60px Courier New";
// context.fillText("我是文字",350,450);
var waterMarkImg = new Image();
waterMarkImg.crossOrigin = 'Anonymous';
waterMarkImg.src = waterMark; //水印图
waterMarkImg.onload = function(){ //第二次绘制绘制logo即可
context.drawImage(waterMarkImg , 0 , 0 , 499 , 210);
var base64 = canvas.toDataURL("image/jpg"); //"image/png" 这里注意一下
resolve(base64)
}
}
})
}
export default drawAndShareImage

152
src/views/QRCode/index.vue Normal file
View File

@ -0,0 +1,152 @@
<template>
<div class='QR-code-wrap'>
<div class="info-card">
<div class="title">身份二维码</div>
<div class="QR-wrap">
<div class="temp">
<div class="QRImg" ref="qrCodeUrl"></div>
</div>
<div class="hint">
<div>本码用于领取物资请妥善保管二维码</div>
<div>刷新倒计时<span style="color:red">{{times}}s</span>
</div>
</div>
</div>
<div class="button" @click="Update">刷新</div>
</div>
<img class="bottom-img" src="@/assets/image/logo.png" alt="">
</div>
</template>
<script>
import QRCode from 'qrcodejs2'
import { GetQRCode } from 'api/QRCode'
export default {
name:'QRCode',
data(){
return {
times: 0,
Code: null,
timer: null,
upDateTime:30
}
},
activated(){
this.Code = sessionStorage.Code
if(this.Code){
this.getQRCode(this.Code)
}else{
this.$Toast.error('未认证人脸信息!即将返回!')
setTimeout(() => {
this.$router.push('/home')
},800)
}
},
deactivated(){
clearInterval(this.timer)
},
methods:{
getQRCode(code,flag){
GetQRCode(code).then(res =>{
if(res.code === 0){
this.QRCode = res.data.Code
this.creatQrCode(this.QRCode)
if(flag){ //
this.$Toast.success('二维码刷新成功')
}
}else{
this.$Toast.error('查询二维码失败!即将返回!')
setTimeout(() => {
this.$router.push('/home')
},800)
}
})
},
Update(){ //
this.getQRCode(this.Code,true)
},
creatQrCode(text) { //
clearInterval(this.timer)
this.times = this.upDateTime
this.$refs.qrCodeUrl.innerHTML = '' //
var qrcode = new QRCode(this.$refs.qrCodeUrl, {
text:text,
width:200,
height:200,
colorDark: '#000000',
colorLight: '#ffffff',
correctLevel: QRCode.CorrectLevel.H
})
//
this.timer = setInterval(() => {
if(this.times == 0){
this.getQRCode(this.Code,true)
}else{
this.times --
}
},1000)
},
}
}
</script>
<style lang="stylus" scoped>
.QR-code-wrap
box-sizing border-box
width 100%
overflow hidden
height 100vh
position relative
.bottom-img
position absolute
bottom 0px
width 100%
left 0
.info-card
position relative
z-index 999
width 90%
border-radius 15px
background white
margin 0 auto
display flex
flex-direction column
justify-content center
align-items center
padding-bottom 30px
overflow hidden
margin-top 70px
.title
Title()
.button
Button()
.QR-wrap
width 100%
display flex
justify-content center
flex-direction column
align-items center
padding 20px
box-sizing border-box
.temp
width 224px
height 224px
box-sizing border-box
padding 10px
border 2px dotted #eee
border-radius 5px
.QRImg
display flex
justify-content center
font-size 0
.hint
font-size 12px
color #666
margin-top 10px
text-align center
div
margin-top 15px
</style>

296
src/views/admin/index.vue Normal file
View File

@ -0,0 +1,296 @@
<template>
<div class="admin-wrap">
<!-- <div class="title">操作中心</div> -->
<div class="scan-wrap">
<div class="main" @click="onScan">
<img src="@/assets/image/scan1.png" alt="" class="outside">
<img src="@/assets/image/scan2.png" alt="" class="interior">
</div>
</div>
<div class="info">
<div class="main">
<div class="detail">
<div class="main">
<div class="temp">
<div class="userInfo">
<span>姓名</span>
<span class="value">{{userData.Name}}</span>
</div>
<div class="userInfo">
<span class='type'>身份证号</span>
<span class="value">{{idCard}}</span>
</div>
</div>
</div>
</div>
<div class="img-wrap">
<!-- <div class="imgs" :style="{backgroundImage:`url(${userData.imgUrl})`}" @click="showPreview = true"></div> -->
<van-image
class="imgs"
fit="contain"
:src="userData.FaceImgUrl"
@click="showPreview = true"
/>
</div>
</div>
</div>
<div class="input-wrap">
<input v-model="code" type="text" placeholder='请输入编号'>
<div class="button" @click="onSubmit">确认发放</div>
</div>
<van-image-preview
v-model="showPreview"
:images="images"
>
</van-image-preview>
<img class="bottom-img" src="@/assets/image/logo.png" alt="">
</div>
</template>
<script>
import {Icon, ImagePreview, Image,Dialog } from 'vant'
import WeChatScan from 'utils/WeChatScan.js'
import Vue from 'vue';
Vue.use(ImagePreview);
import { GetPeopleData, AddNo } from 'api/admin'
export default {
name:"Admin",
data(){
return {
userData:{IdCard:'',Name:''},
showPreview:false,
code:'',
}
},
components:{
[Icon.name] :Icon,
[Image.name]:Image,
[Dialog.name]:Dialog,
},
computed:{
images(){
return [this.userData.FaceImgUrl]
},
idCard(){
if(this.userData.IdCard){
let str = this.userData.IdCard
let difference = str.length - 5 - 3;
return str.slice(0, 5) + '*'.repeat(difference) + str.slice(-3)
}else{
return ''
}
// let replaceStr = new Array(difference + 1).join('*')
// return this.userData.IdCard.replace(/^(.{5})(?:\d+)(.{3})$/,'$1' + replaceStr + '$2');
}
},
activated(){
this.$Toast.success('欢迎您!管理员!')
},
methods:{
onScan(){
WeChatScan(this.handleScanCode) //
},
handleScanCode(ScanCode){
this.userData = {}
GetPeopleData(ScanCode).then(res => {
if(res.code === 0){
this.$Toast.success('查询成功!')
this.userData = res.data
if(res.data.No){
Dialog.alert({
title: '改用户已经绑定,无需再次绑定',
message: '绑定编号为:' + this.userData.No
});
}
}else{
this.$Toast.error(res.msg)
}
})
},
onSubmit(){
var reg = new RegExp('^[0-9]*$')
if(this.code){
if(!reg.test(this.code)){
this.$Toast.error('请输入数字!')
return
}
AddNo(this.userData.CodeMd5,this.code).then(res=> {
if(res.code != 0 && res.code != 2018){
this.$Toast.error('绑定失败:' + res.msg)
}
if(res.code === 0){
this.$Toast.success('绑定成功!')
}else if(res.code === 2018){
Dialog.alert({
title: '改用户已经绑定,请勿重复绑定',
message: '绑定编号为:' + this.userData.No
});
}
this.userData = {} //
this.code = ''
})
}else{
this.$Toast.error('请输入编号')
}
}
}
}
</script>
<style>
@keyframes rotate{
0%{
transform: rotate(0deg) scale(1);
}
50%{
transform:rotate(180deg) scale(1.2)
}
100%{
transform: rotate(360deg) scale(1);
}
}
</style>
<style lang="stylus" scoped>
.admin-wrap
width 100%
.bottom-img
position absolute
bottom 0px
width 100%
left 0
.title
padding 10px
font-size 14px
background rgb(64,158,255)
text-align center
color white
.scan-wrap
display flex
justify-content center
padding 30px 0
.main
width 25%
position relative
.outside
width 100%
animation rotate 5s linear 0s normal infinite
.interior
position absolute
left 50%
top 50%
transform translate(-50%,-50%)
width 50%
.info
width 90%
border-radius 5px
margin 0 auto
min-height 100px
background white
overflow hidden
.title
background #eee
color black
.main
width 100%
padding 10px
box-sizing border-box
display flex
justify-content space-between
.img-wrap
width 26%
height 0
background rgb(250,250,250)
padding-bottom 30%
box-sizing border-box
position relative
.imgs
position absolute
left 5px
top 5px
right 5px
bottom 5px
.detail
width 72%
box-sizing border-box
vertical-align top
height 0
padding-bottom 30%
background rgb(250,250,250)
position relative
font-size 16px
.main
position absolute
left 0
right 0
top 0
bottom 0
display flex
justify-content center
align-items center
.temp
width 100%
.userInfo
color #666
margin 10px 0
word-wrap break-word
white-space:normal
font-size 14px
.type
width 5em
display inline-block
vertical-align top
.value
width calc(100% - 5em)
display inline-block
color #0086ff
font-size 15px
.input-wrap
background white
margin 0 auto
width 90%
border-radius 5px
overflow hidden
padding 20px 0
display flex
flex-direction column
align-items center
margin-top 5px
box-sizing border-box
position relative
z-index 999
input
border none
border-bottom 1px solid rgb(64,158,255)
width 110px
text-align center
padding 10px
border-radius 0
font-size 25px
color rgb(64,158,255)
&::-webkit-input-placeholder
text-align center
font-size 16px
color rgba(64,158,255,0.7)
.button
Button()
margin-top 25px
</style>

View File

@ -0,0 +1,185 @@
<template>
<div class="addPhoto-wrap">
<div class="main-wrap">
<!-- <div class="img-title">
<span>请上传人脸照片</span>
</div> -->
<div class="main">
<div class="img-item" >
<div class="template-wrap">
<!-- 使用背景图展示 -->
<!-- <div class="imgs" :style="{backgroundImage:`url(${photo.url})`}"></div> -->
<!-- 用image组件展示 -->
<div class="img-wrap">
<van-image
v-show="photo"
class="imgs"
fit="contain"
:src="photo"
/>
<div class="temp" v-show="!photo" >
<!-- <img class="cameraIcon" src="@/assets/image/camera-icon.png" alt=""> -->
<svg class="cameraIcon" viewBox="0 0 24 24"><path fill="currentColor" d="M20,4H16.83L15,2H9L7.17,4H4A2,2 0 0,0 2,6V18A2,2 0 0,0 4,20H20A2,2 0 0,0 22,18V6A2,2 0 0,0 20,4M20,18H4V6H8.05L9.88,4H14.12L15.95,6H20V18M12,7A5,5 0 0,0 7,12A5,5 0 0,0 12,17A5,5 0 0,0 17,12A5,5 0 0,0 12,7M12,15A3,3 0 0,1 9,12A3,3 0 0,1 12,9A3,3 0 0,1 15,12A3,3 0 0,1 12,15Z" /></svg>
<span>头像上传</span>
</div>
</div>
<input :key="isKey" @change="onChange($event)" id="myInput" ref="myInput" type="file" accept="image/*">
<!-- <input :key="isKey" @change="onChange($event)" id="myInput" ref="myInput" type="file" accept="image/*"> -->
</div>
</div>
<!-- 用来放添加按钮 -->
<!-- <div class="img-item" v-show="!photo.url">
<div class="template-wrap">
<input :key="isKey" @change="onChange($event)" id="myInput" ref="myInput" type="file" accept="image/*" capture="camera">
<div class="img-wrap">
<div class="temp">
<img class="cameraIcon" src="@/assets/image/camera-icon.png" alt="">
<span>头像上传</span>
</div>
</div>
</div>
</div> -->
</div>
</div>
</div>
</template>
<script>
import { JudgeCompress, RotateImage, CheckFile, FiletoDataURL } from '@/utils/handleImage'
import { Image } from 'vant'
export default {
name:'AddPhoto',
// props:{
// photo:Object,
// },
components:{
[Image.name]:Image,
},
data(){
return {
isKey:0,
compressSize:100,
photo:'',
}
},
methods:{
onChange(e) {
if(!e.target.files[0]){ //
return
}
this.$emit('readyHandle') //.
this.photo = ''
let file = e.target.files[0];
let _this = this
if(file){
var imageFile = CheckFile(file);
if(!imageFile){
this.$Toast.error('文件格式错误!')
return
}else{
RotateImage(imageFile).then(res =>{ //
JudgeCompress(res,this.compressSize,0.95).then(res => { //
FiletoDataURL(res).then(res => { //base64 DataURL
_this.photo = res
_this.$emit('changePhoto', res)
_this.isKey = (new Date()).getTime() //keychange
})
})
})
}
}
},
}
}
</script>
<style lang="stylus" scoped>
.addPhoto-wrap
width 100%
position relative
z-index 999
.main-wrap
width 100%
padding 0
box-sizing border-box
.img-title
// background rgb(64,158,255)
padding 10px
text-align center
font-size 14px
align-items center
color black
padding-left 5px
.main
width 100%
box-sizing border-box
padding 40px 5px
display flex
justify-content center
.img-item
width 50%
height 0
padding-bottom 50%
position relative
overflow hidden
box-sizing border-box
display inline-block
.template-wrap
box-sizing border-box
position absolute
overflow hidden
bottom 5px
top 5px
left 5px
overflow hidden
right 5px
// background rgba(0,133,208,0.7)
padding 5px
.img-wrap
box-sizing border-box
width 100%
height 100%
border-radius 5px
overflow hidden
.imgs
width 100%
height 100%
.temp
position absolute
top 5px
bottom 5px
left 5px
right 5px
border-radius 5px
overflow hidden
position absolute
left 50%
top 50%
transform translate(-50%, -50%)
display flex
flex-direction column
justify-content center
align-items center
span
color black
margin-top 10px
font-size 14px
.cameraIcon
width 60px
height 60px
color black
#myInput
position absolute
left 0
top 0
z-index 500
display inline-block
opacity 0
width 100%
height 100%
</style>

123
src/views/home/index.vue Normal file
View File

@ -0,0 +1,123 @@
<template>
<div class='home-wrap'>
<div class="background-wrap">
<img class="background-image" src="@/assets/image/track.jpg" />
</div>
<!-- <div class="top-bgimg-wrap">
<img class="top-img" src="@/assets/image/bgimg-top.png"/>
<div class="logo-wrap">
<img class="yidong-img" src="@/assets/image/yidong.png"/>
<img class="fiveG-img" src="@/assets/image/5g.png"/>
</div>
</div> -->
<div class="info-card flex-container">
<add-photo @changePhoto="changePhoto" @readyHandle='readyHandle'></add-photo>
<div class="form">
<van-field required v-model="formData.Name" clearable placeholder="请输入姓名" />
<van-field required type='number' v-model="formData.Phone" clearable placeholder="请输入手机号" />
<!-- <van-field required type='text' v-model="formData.Code" clearable placeholder="请输入参赛编号" /> -->
</div>
<div class="button" @click="onSubmit">提交</div>
</div>
<!-- <div class="hint">
温馨提示请使用只包含本人正面头像的清晰照片进行注册
</div> -->
<!-- <img class="bottom-img" src="@/assets/image/bgimg-bottom.png" alt=""> -->
<!-- <van-image-preview v-model="showPreview" :images="images"> </van-image-preview> -->
</div>
</template>
<script>
import addPhoto from "./components/addPhoto"
import { Field, Dialog } from 'vant';
import { Runnerverify} from 'api/home'
export default {
name:'Home',
components:{
addPhoto,
[Field.name]:Field,
[Dialog.name]:Dialog
},
activated(){
},
watch:{
Code(val){
sessionStorage.Code = val
}
},
data(){
return {
photo:'',
formData:{
Name:'',
Phone:'',
Code:''
},
Code:null, //code
}
},
methods:{
changePhoto(imgBase64){
this.photo = imgBase64
this.$Loading.hidden()
},
readyHandle(){
this.$Loading.show('正在处理图片...')
},
onSubmit(){
if(this.verifyForm()){
let data = {
Name: this.formData.Name,
facePic: this.photo,
Phone:this.formData.Phone
}
this.$Loading.show('正在注册...')
Runnerverify(data).then(res => {
if(res.code === 0){
this.$Toast.success('注册成功!')
this.$router.push('/photos')
localStorage.images = JSON.stringify(res.data)
}else if(res.code === 2004){ //使
Dialog.alert({
title: '温馨提示',
message: '号码已被占用,请使用新的手机号码进行注册!'
});
}else if(res.code == 2021){ //
localStorage.images = []
this.$router.push({
path:'/photos',
query:{
type:'hintPage'
}
})
}else{
this.$Toast.error('注册失败:' + res.msg)
}
this.$Loading.hidden()
}).catch(err => {
this.$Loading.hidden()
})
}
},
verifyForm(){ //
// let idcardReg = /^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$|^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X|x)$/;
// let IDcardFlag = idcardReg.test(this.formData.IDcard) ? true : false;
let NameFlag = this.formData.Name.length && this.formData.Name.length > 0 ? true : false
if(!this.photo){
this.$Toast.error('请上传人脸照片!')
return
}else if(!NameFlag){
this.$Toast.error('请填写姓名!')
return
}else if(this.formData.Phone.length !== 11){
this.$Toast.error('请填写正确的手机号码!')
return
}else{
return true
}
}
}
}
</script>

93
src/views/login/index.vue Normal file
View File

@ -0,0 +1,93 @@
<template>
<div class='home-wrap'>
<div class="background-wrap">
<img class="background-image" src="@/assets/image/track.jpg" />
</div>
<!-- <div class="top-bgimg-wrap">
<img class="top-img" src="@/assets/image/bgimg-top.png"/>
<div class="logo-wrap">
<img class="yidong-img" src="@/assets/image/yidong.png"/>
<img class="fiveG-img" src="@/assets/image/5g.png"/>
</div>
</div> -->
<div class="top-brand-wrap">
<h1 class="title">5G 赛道</h1>
</div>
<div class="info-card flex-container">
<div class="form">
<van-field required v-model="formData.Name" clearable placeholder="请输入姓名" />
<van-field required type='number' v-model="formData.Phone" clearable placeholder="请输入手机号" />
</div>
<div class="button register" @click="onRegister">注册</div>
<div class="button" @click="onLogin">登陆</div>
</div>
<!-- <div class="hint">
已使用5G赛道小程序完成人脸注册的用户请直接输入注册时的姓名和手机号码登陆并领取照片
</div> -->
<!-- <img class="bottom-img" src="@/assets/image/bgimg-bottom.png" alt=""> -->
<!-- <van-image-preview v-model="showPreview" :images="images"> </van-image-preview> -->
</div>
</template>
<script>
import { Field, Toast, CellGroup, Dialog } from 'vant';
import { Login } from 'api/home'
import imgs1 from '@/assets/image/imgs-01.jpg'
import imgs2 from '@/assets/image/imgs-02.jpg'
export default {
name:'Home',
components:{
[Field.name]:Field,
[CellGroup.name]:CellGroup,
},
activated(){
},
watch:{
Code(val){
sessionStorage.Code = val
}
},
data(){
return {
photo:'',
formData:{
Name:'',
Phone:'',
},
Code:null, //code
}
},
methods:{
onLogin(){
if(!this.formData.Name){
this.$Toast.error('请填写姓名!')
return
}else if(!this.formData.Phone || this.formData.Phone.length != 11){
this.$Toast.error('请填写正确的手机号!')
return
}else{
this.$Loading.show('正在登陆...')
Login(this.formData).then(res => {
if(res.code === 0){
this.$Toast.success('登陆成功!')
this.$router.push('/photos')
localStorage.images = JSON.stringify(res.data)
// localStorage.images = JSON.stringify([imgs1,imgs2,imgs1,imgs2])
}else{
this.$Toast.error('登陆失败:' + res.msg)
}
this.$Loading.hidden()
}).catch(err => {
this.$Loading.hidden()
})
}
},
onRegister(){
this.$router.push('/home')
}
}
}
</script>

104
src/views/photos/index.vue Normal file
View File

@ -0,0 +1,104 @@
<template>
<div class='home-wrap'>
<div class="background-wrap">
<img class="background-image" src="@/assets/image/track.jpg" />
</div>
<!-- <div class="top-bgimg-wrap">
<img class="top-img" src="@/assets/image/bgimg-top.png"/>
<div class="logo-wrap">
<img class="yidong-img" src="@/assets/image/yidong.png"/>
<img class="fiveG-img" src="@/assets/image/5g.png"/>
</div>
</div> -->
<div class="flex-container">
<template v-if="!hintFlag">
<div class="top-brand-wrap">
<h1 class="title">您的 5G 纪念册</h1>
</div>
<div class="main">
<div class="imgs-wrap">
<div class="imgs-item" v-for="(item,index) in imgs" :key="index">
<img :src="item" alt="" >
</div>
</div>
<div class="hint" v-show='showHint'>
<span>您没有任何纪念照哦</span>
</div>
</div>
</template>
<div class="button" @click="onRegister">重新注册</div>
<template v-if="hintFlag">
<div class="title">您的5G纪念册为空</div>
<div class="hint-wrap">
<div class="main">
<div class="info-title">温馨提示</div>
<div class="info">
<p>1为保证识别效果请使用只有自己正面头像的清晰照片注册</p>
<p>2若之前上传的照片效果不佳没有匹配到照片可以使用新的手机号码和头像照片重新注册</p>
</div>
</div>
</div>
</template>
</div>
<!-- <img class="bottom-img" src="@/assets/image/bgimg-bottom.png" alt=""> -->
<!-- <van-image-preview v-model="showPreview" :images="imgs"> </van-image-preview> -->
</div>
</template>
<script>
import { Field, CellGroup, Dialog } from 'vant';
// import { ImagePreview} from 'vant'
// import Vue from 'vue';
// Vue.use(ImagePreview);
import { GetUserAttestation } from 'api/home'
import drawAndShareImage from '@/utils/waterMark'
export default {
name:'Home',
components:{
[Field.name]:Field,
[CellGroup.name]:CellGroup,
[Dialog.name]:Dialog,
},
activated(){
this.hintFlag = this.$route.query.type
if(this.hintFlag) return
let imgs = JSON.parse(localStorage.images)
if(imgs){
console.log(imgs,47)
if(imgs.length === 0){
this.showHint = true
}
this.handlerMark(imgs)
}
},
data(){
return {
imgs:[],
hintFlag:false, //flag
// showPreview:false,
showHint:false
}
},
methods:{
onRegister(){
Dialog.alert({
title: '温馨提示',
message: '请务必使用新的手机号码重新注册!'
}).then(() => {
this.$router.replace('/home')
});
},
handlerMark(arr){
this.imgs = []
arr.forEach(ele => {
this.$Loading.show('正在处理图片...')
drawAndShareImage(ele).then(res =>{
this.imgs.push(res)
this.$Loading.hidden()
})
})
}
}
}
</script>

49
vue.config.js Normal file
View File

@ -0,0 +1,49 @@
'use strict'
const path = require('path')
const resolve = dir => path.join(__dirname, dir)
const titleName = '5G 赛道' // 页面默认title在路由中被覆盖刚进入页面路由之前会展示这一段之后会被路由中的逻辑覆盖
const port = process.env.VUE_APP_BASE_PORT || process.env.npm_config_port || 8088 // dev port 端口号
module.exports = {
publicPath: process.env.NODE_ENV === "production" ? "./" : "/", //生产环境改成相对路径
outputDir: 'dist',
assetsDir: 'static', //打包后其他静态资源所在文件夹
productionSourceMap: false, //设置成false加快打包速度同时放弃生产环境的镜像map也就是不能准确定位报错行数
devServer: {
port: port,
open: true,
proxy: {
[process.env.VUE_APP_BASE_API]: {
target: `http://5gmalasong.hnabc.cn`,
changeOrigin: true,
pathRewrite: {
['^' + process.env.VUE_APP_BASE_API]: ''
}
}
},
},
configureWebpack: {
name: titleName, //在index.html中可通过webpackConfig.name使用
resolve: {
alias: {
'@': resolve('src'),
'components': resolve('src/components'),
'globalComponents':resolve('src/globalComponents'),
'utils':resolve('src/utils'),
'assets':resolve('src/assets'),
'api':resolve('src/api'),
'styles':resolve('src/styles'),
}
}
},
css: {
loaderOptions: {
// 给 stylus-loader 传递选项,使得指定stylus公共变量可以全局使用
stylus: {
import: ['~@/styles/variables.styl']
}
}
}
}