初始化

This commit is contained in:
hezhidong
2024-11-05 17:43:01 +08:00
parent 2a10e37c10
commit 4e1140419a
97 changed files with 8700 additions and 89 deletions
+24
View File
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
+4 -89
View File
@@ -1,92 +1,7 @@
# 鼎盛无际-访客系统-前端 # Vue 3 + Vite
This template should help get you started developing with Vue 3 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
## Getting started - [VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.volar)
To make it easy for you to get started with GitLab, here's a list of recommended next steps.
Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
## Add your files
- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command:
```
cd existing_repo
git remote add origin http://gitlab.lzyyj.com/hzd/visitor.git
git branch -M main
git push -uf origin main
```
## Integrate with your tools
- [ ] [Set up project integrations](http://gitlab.lzyyj.com/hzd/visitor/-/settings/integrations)
## Collaborate with your team
- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
- [ ] [Automatically merge when pipeline succeeds](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)
## Test and Deploy
Use the built-in continuous integration in GitLab.
- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html)
- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
***
# Editing this README
When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template.
## Suggestions for a good README
Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
## Name
Choose a self-explaining name for your project.
## Description
Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
## Badges
On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
## Visuals
Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
## Installation
Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
## Usage
Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
## Support
Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
## Roadmap
If you have ideas for releases in the future, it is a good idea to list them in the README.
## Contributing
State if you are open to contributions and what your requirements are for accepting them.
For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
## Authors and acknowledgment
Show your appreciation to those who have contributed to the project.
## License
For open source projects, say how it is licensed.
## Project status
If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
+14
View File
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/src/assets/log.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
<title>访客预约</title>
<script type="module" src="./lib-flexible/index.min.js"></script>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
+62
View File
@@ -0,0 +1,62 @@
html,
body,
ul,
li,
ol,
dl,
dd,
dt,
p,
h1,
h2,
h3,
h4,
h5,
h6,
form,
fieldset,
legend,
img,input{
margin: 0;
padding: 0;
}
fieldset,
img {
border: none;
}
img {
display: block;
}
ul,
ol {
list-style: none;
}
body {
color: #333;
font: 12px/20px "SimSun", "宋体", "Arial Narrow", HELVETICA;
background: #fff;
/* overflow-y:scroll;*/
/* overflow-x: hidden; */
}
a {
color: #666;
text-decoration: none;
}
a:visited {
color: #666;
}
a:hover,
a:active,
a:focus {
color: #ff8400;
text-decoration: underline;
}
+44
View File
@@ -0,0 +1,44 @@
(function flexible (window, document) {
var docEl = document.documentElement
var dpr = window.devicePixelRatio || 1
// adjust body font size
function setBodyFontSize () {
if (document.body) {
document.body.style.fontSize = (12 * dpr) + 'px'
}
else {
document.addEventListener('DOMContentLoaded', setBodyFontSize)
}
}
setBodyFontSize();
// set 1rem = viewWidth / 10
function setRemUnit () {
var rem = docEl.clientWidth / 10
docEl.style.fontSize = rem + 'px'
}
setRemUnit()
// reset rem unit on page resize
window.addEventListener('resize', setRemUnit)
window.addEventListener('pageshow', function (e) {
if (e.persisted) {
setRemUnit()
}
})
// detect 0.5px supports
if (dpr >= 2) {
var fakeBody = document.createElement('body')
var testElement = document.createElement('div')
testElement.style.border = '.5px solid transparent'
fakeBody.appendChild(testElement)
docEl.appendChild(fakeBody)
if (testElement.offsetHeight === 1) {
docEl.classList.add('hairlines')
}
docEl.removeChild(fakeBody)
}
}(window, document))
+1
View File
@@ -0,0 +1 @@
!function(e,t){function n(){t.body?t.body.style.fontSize=12*o+"px":t.addEventListener("DOMContentLoaded",n)}function d(){var e=i.clientWidth/10;i.style.fontSize=e+"px"}var i=t.documentElement,o=e.devicePixelRatio||1;if(n(),d(),e.addEventListener("resize",d),e.addEventListener("pageshow",function(e){e.persisted&&d()}),o>=2){var a=t.createElement("body"),s=t.createElement("div");s.style.border=".5px solid transparent",a.appendChild(s),i.appendChild(a),1===s.offsetHeight&&i.classList.add("hairlines"),i.removeChild(a)}}(window,document);
+1
View File
@@ -0,0 +1 @@
!function(e){function t(a){if(i[a])return i[a].exports;var n=i[a]={exports:{},id:a,loaded:!1};return e[a].call(n.exports,n,n.exports,t),n.loaded=!0,n.exports}var i={};return t.m=e,t.c=i,t.p="",t(0)}([function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=window;t["default"]=i.flex=function(normal,e,t){var a=e||100,n=t||1,r=i.document,o=navigator.userAgent,d=o.match(/Android[\S\s]+AppleWebkit\/(\d{3})/i),l=o.match(/U3\/((\d+|\.){5,})/i),c=l&&parseInt(l[1].split(".").join(""),10)>=80,p=navigator.appVersion.match(/(iphone|ipad|ipod)/gi),s=i.devicePixelRatio||1;p||d&&d[1]>534||c||(s=1);var u=normal?1:1/s,m=r.querySelector('meta[name="viewport"]');m||(m=r.createElement("meta"),m.setAttribute("name","viewport"),r.head.appendChild(m)),m.setAttribute("content","width=device-width,user-scalable=no,initial-scale="+u+",maximum-scale="+u+",minimum-scale="+u),r.documentElement.style.fontSize=normal?"50px": a/2*s*n+"px"},e.exports=t["default"]}]); flex(false,100, 1);
+61
View File
@@ -0,0 +1,61 @@
(function(doc, win) {
// html元素字体
// 这里基础字体设置为10在uc, WX上没效果, 不知道为什么
// 设置为10时, dpr=1的手机, 宽度360, 计算出来html的字体大小为5px, 可能是字体太小了
// 尽量设置大一些, 这样避免12px字体大小的限制
var _rootFontSize = window._rootFontSize || 100;
// 默认不支持缩放
var _remMetaScalable = typeof window._remMetaScalable === 'undefined'
? false
: !!window._remMetaScalable;
var docEl = doc.documentElement,
isIOS = navigator.userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/),
// 只针对IOS设备
// dpr = isIOS ? Math.min(win.devicePixelRatio, 3) : 1,
dpr = 1,
// 计算缩放比
scale = 1 / dpr,
// 检测支持的"缩放"事件
resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize';
docEl.dataset.dpr = dpr;
// 被iframe引用时,禁止缩放
dpr = window.top === window.self ? dpr : 1;
var metaEl = doc.createElement('meta');
metaEl.name = 'viewport';
var metaElContent = 'width=device-width, ';
// 支持缩放
if (_remMetaScalable) {
metaElContent += 'initial-scale=' + scale;
} else {
metaElContent += (
'initial-scale=' + scale
+ ',maximum-scale=' + scale
+ ', minimum-scale=' + scale
+ ', user-scalable=no');
}
metaEl.content = metaElContent;
docEl.firstElementChild.appendChild(metaEl);
// 缩放/旋转设备检测函数
var recalc = function() {
var width = docEl.clientWidth;
// 750为设计用的宽度, 比如它以iphone6标准(宽750)
// 此时, 按照设计稿的尺寸写就可以了
// 如: 设计稿为100px, 那么就写4rem(100 / 25), 25是自己设置的
// 也可以设置成100, 这样就写100px就写1rem
docEl.style.fontSize = _rootFontSize * (width / 375) + 'px';
};
recalc();
if (!doc.addEventListener) return;
win.addEventListener(resizeEvt, recalc, false);
doc.getElementsByTagName('body')[0].style.fontSize = '16px'
})(document, window);
+26
View File
@@ -0,0 +1,26 @@
{
"name": "styy-mobile",
"private": true,
"version": "0.0.0",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"axios": "^1.6.2",
"dingtalk-jsapi": "^3.0.29",
"html2canvas": "^1.4.1",
"vant": "^3.4.3",
"vite-plugin-style-import": "^1.4.1",
"vue": "^3.2.25",
"vue-router": "^4.0.14",
"vuex": "^4.0.2"
},
"devDependencies": {
"@vitejs/plugin-vue": "^2.3.0",
"sass": "^1.56.1",
"vite": "^2.9.0",
"vite-plugin-style-import": "^1.4.1"
}
}
+1
View File
@@ -0,0 +1 @@
w4cYOalJ3cgAIxxF
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

+51
View File
@@ -0,0 +1,51 @@
<template>
<router-view></router-view>
</template>
<script setup>
import { useRouter, useRoute } from 'vue-router'
const router = useRouter()
const route = useRoute()
console.log(route)
</script>
<style lang="scss">
@import url('./assets/style/base.css');
html,body{
width: 100%;
height: 100%;
color: #222222;
}
#app {
width: 100%;
height: 100%;
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
.box{
width: 100%;
height: 100%;
font-size: $size26;
}
.no-have{
line-height: 1.3333rem /* 100/75 */;
font-size: .4rem /* 30/75 */;
text-align: center;
}
}
.controls-fixed{
position: fixed;
right: 0;
bottom: 0;
width: 100%;
height: .8rem /* 60/75 */;
font-size: .4rem /* 30/75 */;
color: $main-color;
display: flex;
justify-content: center;
}
</style>
+32
View File
@@ -0,0 +1,32 @@
import request from "./adminRequest.js";
// 登录
export function getToken(authCode){
return request({
method: "get",
url: "/user/getToken?code="+authCode,
data: {
}
})
}
// 获取访客记录
export function getVisitorRecord(params ){
return request({
method: "get",
url: "/visitor/getVisitorRecordList",
params,
headers:{
'Authorization': localStorage.getItem('token')
}
})
}
// 获取二维码
export function getWhiteCode( ){
return request({
method: "get",
url: "/qr/getCommonBlackWhiteCode",
headers:{
'Authorization': localStorage.getItem('token')
}
})
}
+96
View File
@@ -0,0 +1,96 @@
import axios from 'axios'
import { useRoute, useRouter } from 'vue-router'
import { useStore } from 'vuex';
import { Toast, Dialog } from 'vant';
// import { getItem, setItem, removeItem, dalert } from '@/com/index'
const errorCode = {
'401': '登录状态已过期,即将返回首页重新登录。',
'403': '当前操作没有权限',
'404': '访问资源不存在',
'default': '系统未知错误,请反馈给管理员'
}
const route = useRoute()
const router = useRouter()
const store = useStore()
const loadNoCloseArr = ['/upload/face', ]
// 判断当前的运行环境 process.env.NODE_ENV
// isDev 为真 开发环境 --- npm run serve
// isDev 为假 非开发环境(测试环境,生产环境)- npm run build
const isDev = process.env.NODE_ENV === 'development'
const request = axios.create({
// 根据环境 设置不同的baseURL
// 如果配置了单个的反向代理 --- 字符串模式
// 如果配置了多个的反向代理 --- 对象模式
// baseURL: isDev ? 'http://visitor.scdswj.cn/prod-api' : 'http://visitor.scdswj.cn/prod-api'
baseURL: isDev ? 'http://192.168.31.118:8195' : 'http://visitor.scdswj.cn/prod-api'
})
// 'http://rap2api.taobao.org/app/mock/297766/example/1643070528065'
// 请求拦截器 - 所有的请求开始之前先到此处
request.interceptors.request.use((config) => {
// 可以设置 加载的动画效果 的展示
// 在必要的路由设置一些额外的参数 ---- token信息携带放在此处
console.log(config)
return config
}, (error) => {
return Promise.reject(error)
})
// 响应拦截器 --- 所有请求的相应先到此处
request.interceptors.response.use((response) => {
// 可以设置 加载的动画效果 的隐藏
const code = response.data.code || 200;
const message = errorCode[code] || response.data.msg || errorCode['default']
if(code == 200){
if(loadNoCloseArr.includes(response.config.url)){ // 设置请求不关闭加载动画接口
}else{
Toast.clear()
}
}else{
Toast.clear()
}
if (code === 401) {
Toast.fail(message)
setTimeout(()=>{
localStorage.removeItem('token');
localStorage.removeItem('user');
const redirectUrl = localStorage.getItem('redirect')
if(redirectUrl){
window.location.href = redirectUrl
}else{
window.location.href = '/userHome'
}
},2000)
return Promise.reject('登录状态已过期,即将返回首页重新登录。')
} else if (code === 500) {
// Message({ message: msg, type: 'error' })
Toast.fail(message)
return Promise.reject(response.data)
} else if (code === 601) {
// Message({ message: msg, type: 'warning' })
Toast.fail(message)
return Promise.reject('error')
} else if (code != 200) {
Toast.fail(message)
return Promise.reject('error')
} else {
return response.data
}
}, error => {
Toast.clear()
console.log(error.message)
let { message } = error;
if (message == "Network Error") {
message = "后端接口连接异常";
} else if (message.includes("timeout")) {
message = "系统接口请求超时";
} else if (message.includes("Request failed with status code")) {
message = "系统接口" + message.substr(message.length - 3) + "异常";
}
console.log(message)
Toast.fail(message)
return Promise.reject(error.response)
})
export default request
+97
View File
@@ -0,0 +1,97 @@
import request from "./adminRequest.js";
// 登录
export function getToken(authCode){
return request({
method: "get",
url: "/user/getToken?code="+authCode,
data: {
}
})
}
// 获取部门列表
export function getDept( ){
return request({
method: "get",
url: "/dept/getList",
headers:{
'Authorization': localStorage.getItem('token')
}
})
}
// 获取部门员工
export function getUserListByDept( deptId){
return request({
method: "get",
url: "/user/getUserListByDept",
params:{
deptId
},
headers:{
'Authorization': localStorage.getItem('token')
}
})
}
// 内部员工上传照片后,提交url
export function putUserId( data){
return request({
method: "put",
url: "/user/updateByUserId",
data,
headers:{
'Authorization': localStorage.getItem('token')
}
})
}
// http://localhost:8190/special/getList
// 获取特殊人员
export function getSpecial( params){
return request({
method: "get",
url: "/special/getList",
params,
headers:{
'Authorization': localStorage.getItem('token')
}
})
}
// 删除特殊人员
export function putSpecial( id){
return request({
method: "put",
url: "/special/remove",
params: {
id
},
headers:{
'Authorization': localStorage.getItem('token')
}
})
}
// 特殊人员新增
export function postSpecial( data){
return request({
method: "post",
url: "/special/add",
data,
headers:{
'Authorization': localStorage.getItem('token')
}
})
}
// 修改特殊人员相片
export function putSpecialFace( data){
return request({
method: "put",
url: "/special/update",
data,
headers:{
'Authorization': localStorage.getItem('token')
}
})
}
+98
View File
@@ -0,0 +1,98 @@
import axios from 'axios'
import { useRoute, useRouter } from 'vue-router'
import { useStore } from 'vuex';
import { Toast, Dialog } from 'vant';
// import { getItem, setItem, removeItem, dalert } from '@/com/index'
const errorCode = {
'401': '登录状态已过期,即将返回首页重新登录。',
'403': '当前操作没有权限',
'404': '访问资源不存在',
'default': '系统未知错误,请反馈给管理员'
}
const route = useRoute()
const router = useRouter()
const store = useStore()
const loadNoCloseArr = ['/upload/face', ]
// 判断当前的运行环境 process.env.NODE_ENV
// isDev 为真 开发环境 --- npm run serve
// isDev 为假 非开发环境(测试环境,生产环境)- npm run build
const isDev = process.env.NODE_ENV === 'development'
const request = axios.create({
// 根据环境 设置不同的baseURL
// 如果配置了单个的反向代理 --- 字符串模式
// 如果配置了多个的反向代理 --- 对象模式
// baseURL: isDev ? 'http://visitor.scdswj.cn/prod-api' : 'http://visitor.scdswj.cn/prod-api'
baseURL: isDev ? 'http://visitor.scdswj.cn/prod-api' : 'http://visitor.scdswj.cn/prod-api'
})
// 'http://rap2api.taobao.org/app/mock/297766/example/1643070528065'
// 请求拦截器 - 所有的请求开始之前先到此处
request.interceptors.request.use((config) => {
// 可以设置 加载的动画效果 的展示
// 在必要的路由设置一些额外的参数 ---- token信息携带放在此处
console.log(config)
return config
}, (error) => {
return Promise.reject(error)
})
// 响应拦截器 --- 所有请求的相应先到此处
request.interceptors.response.use((response) => {
// 可以设置 加载的动画效果 的隐藏
const code = response.data.code || 200;
const message = errorCode[code] || response.data.msg || errorCode['default']
if(code == 200){
if(loadNoCloseArr.includes(response.config.url)){ // 设置请求不关闭加载动画接口
}else{
Toast.clear()
}
}else{
Toast.clear()
}
if (code === 401) {
Toast.fail(message)
setTimeout(()=>{
localStorage.removeItem('token');
localStorage.removeItem('user');
localStorage.removeItem('userToken');
const redirectUrl = localStorage.getItem('redirect')
if(redirectUrl){
window.location.href = redirectUrl
}else{
window.location.href = '/userHome'
}
},2000)
return Promise.reject('登录状态已过期,即将返回首页重新登录。')
} else if (code === 500) {
// Message({ message: msg, type: 'error' })
Toast.fail(message)
return Promise.reject(response.data)
} else if (code === 601) {
// Message({ message: msg, type: 'warning' })
Toast.fail(message)
return Promise.reject('error')
} else if (code != 200) {
Toast.fail(message)
return Promise.reject('error')
} else {
return response.data
}
}, error => {
Toast.clear()
console.log(error.message)
let { message } = error;
if (message == "Network Error") {
message = "后端接口连接异常";
} else if (message.includes("timeout")) {
message = "系统接口请求超时";
} else if (message.includes("Request failed with status code")) {
message = "系统接口" + message.substr(message.length - 3) + "异常";
}
console.log(message)
Toast.fail(message)
return Promise.reject(error.response)
})
export default request
+14
View File
@@ -0,0 +1,14 @@
import request from "./index";
export function getShop(){
return request({
url: '/shop',
method: 'get'
})
}
export function getKind(){
return request({
url: '/hotkind',
method: 'get'
})
}
+9
View File
@@ -0,0 +1,9 @@
import request from "./index";
// 来访预约接口
export function uploadApi(data){
return request({
url: '/upload/face',
method: 'post',
data
})
}
+69
View File
@@ -0,0 +1,69 @@
import request from "./index";
// 来访预约接口
export function postApply(data){
return request({
url: '/visitor/add',
method: 'post',
data,
headers:{
'Authorization': localStorage.getItem('userToken')
}
})
}
// 拜访人列表
export function getVisitor(){
return request({
url: '/visitor/getVisitingInfo',
method: 'get',
headers:{
'Authorization': localStorage.getItem('userToken')
}
})
}
// 手机号查询预约记录
export function getByPhone(data){
return request({
url: '/visitor/getByPhone',
method: 'get',
params: data,
headers:{
'Authorization': localStorage.getItem('userToken')
}
})
}
// 手机号拜访人姓名
export function getUserNameByTel(phone){
return request({
url: '/user/getUserNameByTel',
method: 'get',
params: {
phone
},
headers:{
'Authorization': localStorage.getItem('userToken')
}
})
}
// 微信授权获得code
export function getWeChatCode(){
return request({
url: '/user/getWxLogin',
method: 'get'
})
}
// 微信登录
export function login(code){
return request({
url: '/user/getWxToken',
method: 'get',
params:{
code
}
})
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 350 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 562 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 527 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 665 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 577 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 646 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

+62
View File
@@ -0,0 +1,62 @@
html,
body,
ul,
li,
ol,
dl,
dd,
dt,
p,
h1,
h2,
h3,
h4,
h5,
h6,
form,
fieldset,
legend,
img,input{
margin: 0;
padding: 0;
}
fieldset,
img {
border: none;
}
img {
display: block;
}
ul,
ol {
list-style: none;
}
body {
color: #333;
font: 12px/20px "SimSun", "宋体", "Arial Narrow", HELVETICA;
background: #fff;
/* overflow-y:scroll;*/
/* overflow-x: hidden; */
}
a {
color: #666;
text-decoration: none;
}
a:visited {
color: #666;
}
a:hover,
a:active,
a:focus {
color: #ff8400;
text-decoration: underline;
}
+10
View File
@@ -0,0 +1,10 @@
$main-color: #30BF78; // 用户端主题色
// 字号大小
$sm-size: .32rem /* 24/75 */;
$size26: .3467rem /* 26/75 */;
$md-size: .3733rem /* 28/75 */;
$size30: .4rem /* 30/75 */;
$lg-size: .4267rem /* 32/75 */;
// 颜色
$white: #ffffff;
Binary file not shown.

After

Width:  |  Height:  |  Size: 270 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 547 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 432 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

+341
View File
@@ -0,0 +1,341 @@
import { Dialog } from 'vant';
/*
表单对象验证、 rulesForm
重置表单对象、resetForm
手机号验证、 rulesPhone
身份证验证、 rulesIdCard
计算器、 calculator
时间格式化、formatDate
获取星期几、weekDate
检查数据类型、checkType
表单去除空格、trimFrom
数组是否包含数组、isIncludeArray
根据id递归查询对象、recursion
计算两个时间戳相差的时间、timeDiff
判断开始时间是否大于结束时间 compareTime
*/
/*
表单验证
@param { Object } form(必填) 验证的表单
@param { Object } rules(选填) 需要验证的字段 试例rules:{ name: '请输入名称' } name为需要验证的字段
@return { Boolean } true 通过 false 失败
*/
export function rulesForm ( form, rules ) {
let flage = true
let JSONrules = JSON.stringify(rules)
if(JSONrules !== JSON.stringify({})){
for( let key in rules){
if(Array.isArray( form[key] ) && form[key].length == 0 ){
// uni.showToast({
// title: rules[key],
// })
Dialog.alert({
message: rules[key],
})
flage = false
return
break;
}else if( !form[key] && form[key] !== 0 ){
// uni.showToast({
// title: rules[key],
// })
// Dialog(rules[key])
Dialog.alert({
message: rules[key],
})
flage = false
break;
}
}
}
return flage
}
export function rulesFormFBoolean ( form ) {
let flage = true
let JSONform = JSON.stringify(form)
if(JSONform !== JSON.stringify({})){
for( let key in form){
if(Array.isArray( form[key] ) && form[key].length == 0 ){
flage = false
return
break;
}else if( !form[key] && form[key] !== 0 ){
flage = false
break;
}
}
}
return flage
}
/*
重置表单对象
@param { Object } form(必填) 需要重置的表单
@param { Object } rules(选填) 指定需要重置的值默认设为null 试例rules:{ name: 1 } name字段重置为1
@return { Object } 重置后的值
*/
export function resetForm(form, rules = {}) {
for( let key in form){
if(checkType(form[key]) == "Array"){
if(rules.hasOwnProperty(key)){ // 是否包含需要更改的
form[key] = rules[key]
}else{
form[key] = []
}
}else if(checkType(form[key]) == "Object"){
if(rules.hasOwnProperty(key)){ // 是否包含需要更改的
form[key] = rules[key]
}else{
form[key] = {}
}
}else if(checkType(form[key]) == "Boolean"){
if(rules.hasOwnProperty(key)){ // 是否包含需要更改的
form[key] = rules[key]
}else{
form[key] = false
}
}else{
if(rules.hasOwnProperty(key)){ // 是否包含需要更改的
form[key] = rules[key]
}else{
form[key] = null
}
}
}
return form
}
/* 手机号验证
@param { Number } IdcardVal(必填) 手机号
@return { Boolean } true 通过 false 失败
*/
export function rulesPhone ( phoneVal ) {
let phoneReg = /(^1\d{10}$)|(^[0-9]\d{7}$)/;
if(!phoneReg.test(phoneVal)) {
Dialog.alert({
message: '请输入正确的手机号!',
})
return false;
}else{
return true
}
}
/*
@param { Number } email(必填) 邮箱地址
@return { Boolean } true 通过 false 失败
*/
export function rulesEmail(email) {
var reg = /^([\w+\.])+@\w+([.]\w+)+$/
return reg.test(email)
}
/*
身份证验证
@param { String } IdcardVal(必填) 需要检查的参数
@return { Boolean } true 通过 false 失败
*/
export function rulesIdCard ( IdcardVal ) {
var idCardReg = /^(\d{15}$|^\d{18}$|^\d{17}(\d|X|x))$/;
if(!idCardReg.test(IdcardVal)) {
// uni.showToast({
// title: '身份证号码格式错误!',
// })
Dialog.alert({ message: '身份证号码格式错误!' })
return false;
} else{
return true
}
}
/*
计算器
startNum 类型 Number||String 开始的参数
endNum 类型 Number||String 结束参数
operator 类型 String 计算方式 + - * ÷
返回值 为数值类型计算结果
*/
export function calculator ( startNum, endNum, operator = '+'){
if(operator == '+'){
return (Number(startNum * 1000) + Number(endNum * 1000)) / 1000
}else if( operator == '-' ){
return (Number(startNum * 1000) - Number(endNum * 1000)) / 1000
}else if( operator == '/' ){
return (Number(startNum * 1000) / Number(endNum * 1000)) / 1000000
}else if( operator == '*' ){
return (Number(startNum * 1000) * Number(endNum * 1000)) / 1000000
}
}
/*
时间日期格式化
@param { Date||String } timeStr(必填) 默认值当前时间 传入时间
@param { String } separator(必填) 默认值 '-' 分隔符号 chinese返回中文汉字
@param { String } type(选填) 是否返回时分秒 yaer表示只返回年月日
@return { String } 日期
*/
export function formatDate (timeStr = (new Date()), separator = '-', type){
const date = new Date(timeStr)
let yaer = date.getFullYear()
let month = date.getMonth() + 1
let day = date.getDate()
let hour = date.getHours()
let minute = date.getMinutes()
let second = date.getSeconds()
if(separator == '-'){
if(type == 'year'){
return yaer + separator + (month >= 10 ? month : ('0' + month)) + separator + (day >= 10 ? day : ('0' + day))
}else {
return yaer + separator + (month >= 10 ? month : ('0' + month)) + separator + (day >= 10 ? day : ('0' + day)) + ' ' + (hour >= 10 ? hour : ('0' + hour)) + ':' + (minute >= 10 ? minute : ('0' + minute)) + ':' + (second >= 10 ? second : ('0' + second))
}
}else if (separator == 'chinese') {
if(type == 'year'){
return yaer + '年' + month+ '月' + day+ '日'
}else {
return yaer + '年' + month+ '月' + day+ '日' + hour + '时' + minute + '分' + second + '秒'
}
}
}
/*
获取星期几
@param { Date||String } time(必填) 默认值当前时间 传入时间
@return { String } 星期几
*/
export function weekDate (time = (new Date())){
const date = new Date(time)
let arr = ['星期天', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
return arr[date.getDay()]
}
/*
判断数据类型值
@param { any } value(必填) 需要检查的参数
@return { String } 'Number', 'String', 'Boolean', 'Array', 'Object', 'Function', 'Undefined', 'null'
*/
export function checkType(value) {
const toString = Object.prototype.toString;
if(toString.call(value) === '[object Number]'){
return 'Number'
}else if(toString.call(value) === '[object String]') {
return 'String'
}else if(toString.call(value) === '[object Boolean]') {
return 'Boolean'
}else if(toString.call(value) === '[object Array]') {
return 'Array'
}else if(toString.call(value) === '[object Object]') {
return 'Object'
}else if(toString.call(value) === '[object Function]') {
return 'Function'
}else if(toString.call(value) === '[object Undefined]') {
return 'Undefined'
}else if(toString.call(value) === '[object Null]') {
return 'null'
}else if(toString.call(value) === '[object Date]') {
return 'Date'
}else if(toString.call(value) === '[object Error]') {
return 'Error'
}
}
/*
表单去除二端空格
该方法需要依赖checkType方法
@param { Object || Array } form(必填) 需要去除空格的表单
@return { Object || Array } 为去空后的数据对象数组
*/
export function trimFrom ( form ) {
let newForm = null
if(checkType(form)== 'Object'){
newForm = { ...form }
}else if(checkType(form)== 'Array') {
newForm = [...form ]
}
for( let key in form){
if( checkType(form[key] ) == 'String' ){
console.log(newForm[key])
newForm[key] = form[key].trim()
}else if( checkType(form[key] ) == 'Object' ){
newForm[key] = trim(form[key])
}else if( checkType(form[key]) == 'Array' ) {
newForm[key] = trim(form[key])
}
}
return newForm
}
/*
数组是否包含数组
@param { Array } arr(必填) 包含数组
@param { Array } arr2(必填) 被包含数组
@return { Boolean } 包含true 不包含false
*/
export function isIncludeArray(arr1, arr2) {
return arr2.every((item) => {
return arr1.includes(item)
})
}
/*
根据id递归查询对象
@param { Array } arr(必填) 查询的数组
@param { Number||String } value(必填) 查询的数组
@param { Number||String } unique(选填) 默认值'id' 查询的字段名
@param { Array } children(选填) 默认值'children' 查询递归数组
@return { Object||null } 查询到的值
*/
export function recursion (arr, value, unique = 'id', children = 'children' ) {
let obj = null
arr.forEach(( item, index ) => {
if(item[unique] == value){
obj = item
}else {
if(item[children] && item[children].length != 0){
if(recursion(item[children], value, unique, children)){
obj = recursion(item[children], value, unique, children)
}
}
}
});
return obj
}
/*
计算两个时间戳相差的时间
@param { startTime, endTime } 一个时间,另一个时间
@return { Object } 比较后对象返回 天时分秒对象集合
*/
export function timeDiff (startTime, endTime) {
const time2 = new Date(endTime)
const time1 = new Date(startTime)
const diffTime = {
day: 0,
hour: 0,
min: 0,
sec: 0
}
const diff = Math.abs(time2.getTime() - time1.getTime());
const remain_hour = diff % (24 * 60 * 60 * 1000);
const remain_min = remain_hour % ( 60 * 60 * 1000);
diffTime.day = Math.floor(diff / (24 * 60 * 60 * 1000));
diffTime.hour = Math.floor(remain_hour / (60 * 60 * 1000));
diffTime.min = Math.floor(remain_min / (60* 1000));
diffTime.sec = remain_min % (60 * 1000) / 1000;
return diffTime;
}
/*
判断开始时间是否大于结束时间 compareTime
@param { startTime, endTime } 一个时间,另一个时间
@return { boolean } 结束时间大于开始时间 返回true 否者false
*/
export function compareTime (startTime, endTime) {
const time1 = new Date(startTime)
const time2 = new Date(endTime)
if(time2.getTime() - time1.getTime() > 0){
return true
}else{
return false
}
}
+40
View File
@@ -0,0 +1,40 @@
<script setup>
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">VSCode</a>
+
<a href="https://github.com/johnsoncodehk/volar" target="_blank">Volar</a>
</p>
<p>
<a href="https://vitejs.dev/guide/features.html" target="_blank">
Vite Documentation
</a>
|
<a href="https://v3.vuejs.org/" target="_blank">Vue 3 Documentation</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;
}
</style>
+191
View File
@@ -0,0 +1,191 @@
<template>
<div @click.self="onClickCancel" class='fixed-model'>
<div class="model">
<header>
<h3>新增特殊人员</h3>
</header>
<div class="content">
<input v-model="form.name" class="name-input" type="text" placeholder="请输入人员姓名" />
<input v-model="form.userId" class="tel-input" type="text" placeholder="请输入人员联系方式" />
<p>人脸上传请上传正脸无遮挡照片</p>
<div class="upload-box">
<img v-if="!faceImg" class="icon-img" src="../assets/img/xiangji.png" alt="">
<img v-else class="photo" :src="faceImg" alt="">
<input @change="onChangeFile" type="file">
</div>
<div class="button-box">
<div @click="onClickCancel" class="cancel-button">取消</div>
<div @click="onClickSave" class="save-button">保存</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, reactive } from 'vue'
import { useRouter } from 'vue-router'
import { useStore } from 'vuex'
import { rulesForm, resetForm, rulesPhone } from '@/com/index'
/* @data */
const router = useRouter()
const store = useStore()
const emits = defineEmits(['change', 'cancel', 'save'])
const form = reactive({
face: '',
userId: '',
name: '',
})
const faceImg = ref('')
/* @setup */
/* @method */
// 上传图片
const onChangeFile = (e) =>{
if(e.target.files[0]){
form.face = e.target.files[0]
let reader = new FileReader()
reader.readAsDataURL(e.target.files[0]) // 转base64
reader.onload = () =>{ // 简化代码 - 转换完成
faceImg.value = reader.result
}
}
}
const onClickCancel = () =>{
emits('cancel')
}
const onClickSave = () =>{
emits('save', form)
}
</script>
<style lang='scss' scoped>
.fixed-model{
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: rgba(60,60,67,0.6);
.model{
position: absolute;
left: 50%;
top: 50%;
transform: translateX(-50%) translateY(-60%) ;
width: 8.1867rem /* 614/75 */;
// height: 11.7333rem /* 880/75 */;
background: #FFFFFF;
border-radius: .5333rem /* 40/75 */;
header{
width: 100%;
height: 3.7333rem /* 280/75 */;
background: url('../assets/img/xinzengbeijing.png') left top/100% 100% no-repeat;
padding: .8267rem /* 62/75 */ 0 0;
box-sizing: border-box;
h3{
line-height: .8rem /* 60/75 */;
font-size: .5333rem /* 40/75 */;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 500;
color: #222222;
text-align: center;
}
}
.content{
width: 100%;
padding: 0 .72rem /* 54/75 */ .8rem /* 60/75 */ .8rem /* 60/75 */;
box-sizing: border-box;
margin-top: -1.6267rem /* 122/75 */;
.name-input, .tel-input{
width: 100%;
height: 1.1467rem /* 86/75 */;
background: #F5F5F5;
border-radius: 1.3333rem /* 100/75 */;
opacity: 1;
border: none;
outline: none;
padding: 0 .4533rem /* 34/75 */ 0;
box-sizing: border-box;
font-size: $md-size;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
margin-bottom: .48rem /* 36/75 */;
}
.tel-input{
margin-bottom: .3733rem /* 28/75 */;
}
.name-input::-webkit-input-placeholder{
color: #B6B7B8;
}
.tel-input::-webkit-input-placeholder{
color: #B6B7B8;
}
p{
line-height: .5333rem /* 40/75 */;
font-size: $size26;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #000000;
padding: 0 0 0 .2667rem /* 20/75 */;
box-sizing: border-box;
margin-bottom: .2667rem /* 20/75 */;
}
.upload-box{
position: relative;
width: 3.28rem /* 246/75 */;
height: 2.6667rem /* 200/75 */;
background: #F5F5F5;
border-radius: .2667rem /* 20/75 */;
opacity: 1;
border: .0267rem /* 2/75 */ solid #D1D1D6;
display: flex;
justify-content: center;
align-items: center;
.icon-img{
width: 1.0667rem /* 80/75 */;
height: 1.0667rem /* 80/75 */;
}
.photo{
display: block;
width: 100%;
height: 100%;
border-radius: .2667rem /* 20/75 */;
}
input{
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
opacity: 0;
}
}
.button-box{
margin-top: 1.1467rem /* 86/75 */;
display: flex;
div{
// width: 240px;
flex: 1;
height: 1.0667rem /* 80/75 */;
line-height: 1.0667rem /* 80/75 */;
background: #448CF7;
border-radius: 1.3333rem /* 100/75 */;
text-align: center;
font-size: $md-size;
color: #FFFFFF;
}
.cancel-button{
margin-right: .2667rem /* 20/75 */;
background: #D9D9D9;
color: #999999;
}
}
}
}
}
</style>
+162
View File
@@ -0,0 +1,162 @@
<template>
<div @click.self="onClickCancel" class='fixed-model'>
<div class="model">
<header>
<h3>员工人脸照片</h3>
<div class="upload-box">
<div class="span">点击上传</div>
<img v-if="faceImg" class="face-img" :src="props.faceImg" alt="">
<input @change="onChangeFile" type="file" accept="image/*"/>
</div>
</header>
<p class="tips">点击照片框进行人脸照片上传</p>
<div class="button-box">
<div @click="onClickCancel" class="cancel-button">取消</div>
<div @click="onClickSave" class="save-button">保存</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import { useStore } from 'vuex'
import { rulesForm, resetForm, rulesPhone } from '@/com/index'
/* @data */
const router = useRouter()
const store = useStore()
const emits = defineEmits(['change', 'cancel', 'save'])
const props = defineProps({
faceImg: {
default: ''
},
})
const file = ref()
/* @setup */
/* @method */
// 上传图片
const onChangeFile = (e) =>{
file.value = e.target.files[0]
emits('change', e.target.files[0])
}
const onClickCancel = () =>{
emits('cancel')
}
const onClickSave = () =>{
emits('save', file.value)
}
</script>
<style lang='scss' scoped>
.fixed-model{
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: rgba(60,60,67,0.6);
.model{
position: absolute;
left: 50%;
top: 50%;
transform: translateX(-50%) translateY(-65%) ;
width: 8.1867rem /* 614/75 */;
// height: 394px;
background: #FFFFFF;
border-radius: .5333rem /* 40/75 */;
padding: 0 0 .7733rem /* 58/75 */;
box-sizing: border-box;
header{
width: 100%;
height: 10.5333rem /* 790/75 */;
background: #FFFFFF;
border-radius: .5333rem /* 40/75 */;
background: url('../assets/img/renlianbeijing.png') left top/100% 100% no-repeat;
h3{
line-height: .7733rem /* 58/75 */;
font-size: .5333rem /* 40/75 */;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 500;
color: #222222;
padding: .8533rem /* 64/75 */ 0 0 ;
text-align: center;
box-sizing: border-box;
}
.upload-box{
position: relative;
width: 4.4rem /* 330/75 */;
height: 4.4rem /* 330/75 */;
background: #FFFFFF;
box-shadow: 0px .1067rem /* 8/75 */ .8rem /* 60/75 */ .2667rem /* 20/75 */ rgba(68,140,247,0.4);
border-radius: .2667rem /* 20/75 */;
margin: 1.28rem /* 96/75 */ auto 0;
overflow: hidden;
.span{
text-align: center;
line-height: 4.4rem /* 330/75 */;
font-size: $size30;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #999999;
}
// display: flex;
.face-img, input{
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
border-radius: .2667rem /* 20/75 */;
opacity: 0;
}
.face-img{
opacity: 1;
}
}
}
.tips{
line-height: .5333rem /* 40/75 */;
font-size: $md-size;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #5F6583;
text-align: center;
margin-top: -1.6rem /* 120/75 */;
}
.button-box{
// margin-top: 1.1467rem /* 86/75 */;
display: flex;
padding: 0 .7733rem /* 58/75 */;
box-sizing: border-box;
margin-top: .6133rem /* 46/75 */;
div{
// width: 240px;
flex: 1;
height: 1.0667rem /* 80/75 */;
line-height: 1.0667rem /* 80/75 */;
background: #448CF7;
border-radius: 1.3333rem /* 100/75 */;
text-align: center;
font-size: $md-size;
color: #FFFFFF;
}
.cancel-button{
margin-right: .2667rem /* 20/75 */;
background: #D9D9D9;
color: #999999;
}
}
}
}
</style>
+115
View File
@@ -0,0 +1,115 @@
<!-- label 为字段名 -->
<!-- type 为输入框类型 默认text -->
<!-- isMust 为是否存在* 默认有 -->
<!-- disabled 是否禁用 -->
<template>
<div class="input-box" :class=" isline ? 'select-active' : ''">
<div class="label"> <span v-if="props.isMust && props.isMustPosition == 'left'">*&nbsp;</span>{{ props.label }} <span v-if="props.isMust && props.isMustPosition == 'right'">*</span></div>
<input @blur="onClickBlur" @touchstart="onClickInput" :type="props.type" v-model="sea" @input="valChange(sea)" :disabled="props.disabled" :placeholder="'请输入'+props.label">
</div>
</template>
<script lang='ts' setup>
import { ref, watch } from 'vue'
import { useRouter } from 'vue-router'
/* props */
const props = defineProps({
modelValue:{
default: '' as any,
},
type:{
type: String,
default: 'text'
},
label: {
type: String,
default: ''
},
isline: {
default: false,
},
isMust: {
type: Boolean,
default: true,
},
isMustPosition: {
type: String,
default: 'right',
},
disabled: {
type: Boolean,
default: false,
},
})
/* emits */
const emits = defineEmits(['update:modelValue', 'click', 'input', 'blur'])
/* watch */
// 因为props.modelValue是非引用类型,改变时,需要监听,对sea重新赋值
watch(() => props.modelValue, () => { sea.value = props.modelValue })
// 数据双向绑定
const valChange = (e: string) =>{
emits('update:modelValue', e)
emits('input', e)
}
/* @data */
const router = useRouter()
const sea = ref(props.modelValue)
/* @method */
const onClickInput = () =>{
emits('click')
}
const onClickBlur = () =>{
emits('blur', sea.value)
}
/* setup */
// console.log(props.modelValue)
</script>
<style lang='scss' scoped>
.input-box{
width: 100%;
height: 1.5733rem /* 118/75 */;
// background: #F9F9F9;
// border-radius: .2667rem /* 20/75 */;
padding: 0 .0267rem /* 2/75 */ 0;
box-sizing: border-box;
display: flex;
// margin: 0 auto .2667rem /* 20/75 */;
.label{
flex: 1;
font-size: $size26;
color: #333333;
line-height: 1.5733rem /* 118/75 */;
span{
color: #FF0000;
}
}
input{
flex: 1;
border: none;
outline: none;
text-align: right;
font-size: $size26;
color: #333333;
// background: #F9F9F9;
padding-right: 0.3733rem;
}
input::-webkit-input-placeholder {
font-size: $size26;
color: #999999;
}
}
.select-active{
border-bottom: .0267rem /* 2/75 */ dashed #D9D9D9;
}
</style>
+98
View File
@@ -0,0 +1,98 @@
<template>
<div class='fixed-model'>
<div class="model">
<h3>删除事件提醒</h3>
<p>确认删除此员工人脸信息</p>
<div class="button-box">
<div @click="onClickCancel" class="cancel-button">取消</div>
<div @click="onClickSave" class="save-button">删除</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import { useStore } from 'vuex'
import { rulesForm, resetForm, rulesPhone } from '@/com/index'
const emits = defineEmits(['change', 'cancel', 'delete'])
/* @data */
const router = useRouter()
const store = useStore()
/* @setup */
/* @method */
const onClickCancel = () =>{
emits('cancel')
}
const onClickSave = () =>{
emits('delete')
}
</script>
<style lang='scss' scoped>
.fixed-model{
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: rgba(60,60,67,0.6);
.model{
position: absolute;
left: 50%;
top: 50%;
transform: translateX(-50%) translateY(-65%) ;
width: 8.1867rem /* 614/75 */;
// height: 394px;
background: #FFFFFF;
border-radius: .5333rem /* 40/75 */;
padding: .48rem /* 36/75 */ 0 .7733rem /* 58/75 */;
box-sizing: border-box;
h3{
line-height: .6667rem /* 50/75 */;
font-size: .4533rem /* 34/75 */;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 500;
text-align: center;
color: #222222;
}
p{
line-height: .5867rem /* 44/75 */;
font-size: $size30;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #5F6583;
margin: .6933rem /* 52/75 */ 0 .9867rem /* 74/75 */;
text-align: center;
}
.button-box{
// margin-top: 1.1467rem /* 86/75 */;
display: flex;
padding: 0 .7733rem /* 58/75 */;
box-sizing: border-box;
div{
// width: 240px;
flex: 1;
height: 1.0667rem /* 80/75 */;
line-height: 1.0667rem /* 80/75 */;
background: #FF6643;
border-radius: 1.3333rem /* 100/75 */;
text-align: center;
font-size: $md-size;
color: #FFFFFF;
}
.cancel-button{
margin-right: .2667rem /* 20/75 */;
background: #D9D9D9;
color: #999999;
}
}
}
}
</style>
+84
View File
@@ -0,0 +1,84 @@
<template>
<div class='success-model-fixed'>
<div class="success-model">
<img src="../assets/chenggong.png" alt="">
<div class="title">保存成功</div>
<p>请在手机相册查看访客邀约海报</p>
<button class="affirm-button">确定</button>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import { useStore } from 'vuex'
import { rulesForm, resetForm, rulesPhone } from '@/com/index'
/* @data */
const router = useRouter()
const store = useStore()
/* @setup */
/* @method */
</script>
<style lang='scss' scoped>
.success-model-fixed{
position: fixed;
left: 0;
top: 0;
width: 100vw;
height: 100vh;
background: rgba(60,60,67,0.6);
.success-model{
position: absolute;
left: 50%;
top: 24%;
transform: translateX(-50%);
width: 7.1467rem /* 536/75 */;
height: 7.6267rem /* 572/75 */;
padding: .56rem /* 42/75 */ .9333rem /* 70/75 */;
box-sizing: border-box;
background: #FFFFFF;
border-radius: .2667rem /* 20/75 */ .2667rem /* 20/75 */ .2667rem /* 20/75 */ .2667rem /* 20/75 */;
display: flex;
flex-direction: column;
align-items: center;
img{
width: 2.6667rem /* 200/75 */;
height: 2.6667rem;
}
.title{
text-align: center;
font-size: $md-size;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 500;
color: #222222;
line-height: .56rem /* 42/75 */;
margin: .3467rem /* 26/75 */ auto .4533rem /* 34/75 */;
}
p{
// width: 5.2rem /* 390/75 */;
font-size: $sm-size;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #999999;
line-height: .48rem /* 36/75 */;
}
.affirm-button{
width: 2.24rem /* 168/75 */;
height: .9867rem /* 74/75 */;
background: #30BF78;
border-radius: .1333rem /* 10/75 */ ;
outline: none;
border: none;
font-size: $size30;
color: #FFFFFF;
margin-top: 1.04rem /* 78/75 */;
}
}
}
</style>
+84
View File
@@ -0,0 +1,84 @@
<template>
<div class='success-model-fixed'>
<div class="success-model">
<img src="../assets/chenggong.png" alt="">
<div class="title">保存成功</div>
<p>请在手机相册查看访客邀约海报</p>
<button class="affirm-button">确定</button>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import { useStore } from 'vuex'
import { rulesForm, resetForm, rulesPhone } from '@/com/index'
/* @data */
const router = useRouter()
const store = useStore()
/* @setup */
/* @method */
</script>
<style lang='scss' scoped>
.success-model-fixed{
position: fixed;
left: 0;
top: 0;
width: 100vw;
height: 100vh;
background: rgba(60,60,67,0.6);
.success-model{
position: absolute;
left: 50%;
top: 24%;
transform: translateX(-50%);
width: 7.1467rem /* 536/75 */;
height: 7.6267rem /* 572/75 */;
padding: .56rem /* 42/75 */ .9333rem /* 70/75 */;
box-sizing: border-box;
background: #FFFFFF;
border-radius: .2667rem /* 20/75 */ .2667rem /* 20/75 */ .2667rem /* 20/75 */ .2667rem /* 20/75 */;
display: flex;
flex-direction: column;
align-items: center;
img{
width: 2.6667rem /* 200/75 */;
height: 2.6667rem;
}
.title{
text-align: center;
font-size: $md-size;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 500;
color: #222222;
line-height: .56rem /* 42/75 */;
margin: .3467rem /* 26/75 */ auto .4533rem /* 34/75 */;
}
p{
// width: 5.2rem /* 390/75 */;
font-size: $sm-size;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #999999;
line-height: .48rem /* 36/75 */;
}
.affirm-button{
width: 2.24rem /* 168/75 */;
height: .9867rem /* 74/75 */;
background: #30BF78;
border-radius: .1333rem /* 10/75 */ ;
outline: none;
border: none;
font-size: $size30;
color: #FFFFFF;
margin-top: 1.04rem /* 78/75 */;
}
}
}
</style>
+127
View File
@@ -0,0 +1,127 @@
<!-- label 为字段名 -->
<!-- isMust 为是否存在* 默认无 -->
<template>
<div class="select-box" :class=" isline ? 'select-active' : ''">
<div class="label"><span v-if="props.isMust">*&nbsp;</span>{{ props.label }} </div>
<div class="select-button">
<span v-if="date" style="color: #333333;">{{ date }}</span>
<span v-else>请选择{{ props.label }}</span>
<input @change="onConfirm" class="date" type="datetime-local" v-model="date" :min="minDate" >
</div>
<!-- <img src="../../assets/employment/youjiantou.png" alt=""> -->
</div>
</template>
<script setup>
import { useRouter } from 'vue-router'
import { ref, watch } from 'vue'
import { formatDate } from '../com/index.js'
/* props */
const props = defineProps({
isMust:{
default: false
},
label:{
default: ''
},
modelValue:{
},
isline: {
default: false,
},
minDate: {
default: '2023-01-01 08:00:00'
}
})
/* emits */
const emits = defineEmits(['update:modelValue'])
/* @data */
let date = ref(props.modelValue)
const router = useRouter()
/* watch */
// 因为props.modelValue是非引用类型,改变时,需要监听,对sea重新赋值
watch(() => props.modelValue, () => {
if(props.modelValue == null){
date.value = ''
}else{
date.value = props.modelValue
}
})
/* @setup */
// console.log(props.modelValue)
/* @method */
const onConfirm = () => {
// console.log(date.value)
date.value = formatDate(date.value, '-')
emits('update:modelValue', date.value)
console.log(date.value)
};
</script>
<style lang='scss' scoped>
.select-box{
position: relative;
width: 9.2rem /* 690/75 */;
width: 100%;
height: 1.5733rem /* 118/75 */;
// background: #F9F9F9;
// border-radius: .2667rem /* 20/75 */;
padding: 0 .0267rem /* 2/75 */ 0;
box-sizing: border-box;
display: flex;
margin-bottom: .2667rem /* 20/75 */;
.label{
flex: 1;
font-size: $size26;
color: #333333;
line-height: 1.5733rem /* 118/75 */;
span{
color: #FF0000;
margin-right: .0267rem /* 2/75 */;
}
}
.select-button{
position: relative;
width: 5.2rem /* 390/75 */;
text-align: right;
font-size: $size26;
color: #999999;
line-height: 1.5733rem /* 118/75 */;
padding-right: .3733rem /* 28/75 */;
box-sizing: border-box;
overflow: hidden;
.date{
position: absolute;
right: 0;
top: 0;
width: 100%;
height: 100%;
opacity: 0;
}
}
.active{
color: #333333;
}
img{
position: absolute;
right: .4rem /* 30/75 */;
top: 50%;
width: .1467rem /* 11/75 */;
height: .2667rem /* 20/75 */;
margin-top: -.16rem /* 12/75 */;
}
}
.select-active{
border-bottom: .0267rem /* 2/75 */ dashed #D9D9D9;
}
</style>
+163
View File
@@ -0,0 +1,163 @@
<!-- label 为字段名 -->
<!-- isMust 为是否存在* 默认无 -->
<!-- isline 是否有下虚线 -->
<template>
<div class="select-box" :class=" isline ? 'select-active' : ''">
<div class="label"> <span v-if="props.isMust">*&nbsp;</span>{{ props.label }}</div>
<div class="select-button" @click="showPicker = true">
<span v-if="selectVal" style="color: #333333;">{{ selectVal }}</span>
<span v-else>请选择{{ props.label }}</span>
</div>
<!-- <div class="select-line"></div> -->
<!-- <img src="../../assets/employment/youjiantou.png" alt=""> -->
<Popup v-model:show="showPicker" round position="bottom">
<!-- title="标题" -->
<Picker
:columns="props.columns"
:columns-field-names="customFieldName"
:default-index="defaultIndex"
@confirm="onConfirm"
@cancel="showPicker = false"
/>
</Popup>
</div>
</template>
<script setup>
import { useRouter } from 'vue-router'
import { ref, watch } from 'vue'
import { Picker, Popup } from 'vant';
/* props */
const props = defineProps({
isMust:{
default: false
},
label:{
default: ''
},
isline: {
default: false,
},
columns:{
default: []
},
modelValue: {
},
name: {
default: 'title'
},
nameId: {
default: 'id'
}
})
/* emits */
const emits = defineEmits(['update:modelValue'])
/* @data */
const customFieldName = {
text: props.name,
children: 'cities',
};
let selectVal = ref('')
const router = useRouter()
let showPicker = ref(false)
let defaultIndex = ref(0)
/* @setup */
/* watch */
// 因为props.modelValue是非引用类型,改变时,需要监听,对sea重新赋值
watch(() => props.modelValue, () => {
if(props.modelValue == null){
// console.log('props.modelValue')
// console.log(props.modelValue)
selectVal.value = ''
}
})
// 根据数据赋值
watch(() => props.columns, () => {
if(props.modelValue != undefined || props.modelValue != null){
props.columns.forEach(( item ,index ) => {
// console.log('知悉了'+ item[props.name] + item[props.nameId])
if(item[props.nameId] == props.modelValue){
selectVal.value = item[props.name]
defaultIndex.value = index
}
})
}
})
/* @method */
const onConfirm = (value , index) => {
let str = ''
let ids = []
if(value.length){
value.forEach((item) => {
str = str + item[props.name]
ids.push(item[props.nameId])
});
}else{
str = value[props.name]
ids = value[props.nameId]
}
selectVal.value = str
emits('update:modelValue', ids)
showPicker.value = false
};
</script>
<style lang='scss' scoped>
.select-box{
position: relative;
width: 100%;
height: 1.5733rem /* 118/75 */;
// background: #F9F9F9;
// border-radius: .2667rem /* 20/75 */;
padding: 0 .0267rem /* 2/75 */ 0;
box-sizing: border-box;
display: flex;
// margin-bottom: .2667rem /* 20/75 */;
.label{
flex: 1;
font-size: $size26;
color: #333333;
line-height: 1.5733rem /* 118/75 */;
span{
color: #FF0000;
margin-right: .0267rem /* 2/75 */;
}
}
.select-button{
width: 4.8rem /* 360/75 */;
text-align: right;
font-size: $size26;
color: #999999;
line-height: 1.5733rem /* 118/75 */;
padding-right: .3733rem /* 28/75 */;
box-sizing: border-box;
}
.active{
color: #333333;
}
img{
position: absolute;
right: .4rem /* 30/75 */;
top: 50%;
width: .1467rem /* 11/75 */;
height: .2667rem /* 20/75 */;
margin-top: -.16rem /* 12/75 */;
}
}
.select-active{
border-bottom: .0267rem /* 2/75 */ dashed #D9D9D9;
}
</style>
+88
View File
@@ -0,0 +1,88 @@
<template>
<div class='success-model-fixed'>
<div class="success-model">
<img src="../assets/chenggong.png" alt="">
<div class="title">登记成功等待审核</div>
<p>请在预约记录里面输入联系方式查看审核进度及结果反馈</p>
<button @click="onClickAffirm" class="affirm-button">确定</button>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import { useStore } from 'vuex'
import { rulesForm, resetForm, rulesPhone } from '@/com/index'
/* @data */
const router = useRouter()
const store = useStore()
const emits = defineEmits(['click'])
/* @setup */
/* @method */
const onClickAffirm = () =>{
emits('click')
}
</script>
<style lang='scss' scoped>
.success-model-fixed{
position: fixed;
left: 0;
top: 0;
width: 100vw;
height: 100vh;
background: rgba(60,60,67,0.6);
.success-model{
position: absolute;
left: 50%;
top: 24%;
transform: translateX(-50%);
width: 7.1467rem /* 536/75 */;
height: 7.6267rem /* 572/75 */;
padding: .56rem /* 42/75 */ .9333rem /* 70/75 */;
box-sizing: border-box;
background: #FFFFFF;
border-radius: .2667rem /* 20/75 */ .2667rem /* 20/75 */ .2667rem /* 20/75 */ .2667rem /* 20/75 */;
display: flex;
flex-direction: column;
align-items: center;
img{
width: 2.6667rem /* 200/75 */;
height: 2.6667rem;
}
.title{
text-align: center;
font-size: $md-size;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 500;
color: #222222;
line-height: .56rem /* 42/75 */;
margin: .3467rem /* 26/75 */ auto .4533rem /* 34/75 */;
}
p{
// width: 5.2rem /* 390/75 */;
font-size: $sm-size;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #999999;
line-height: .48rem /* 36/75 */;
text-align: center;
}
.affirm-button{
width: 2.24rem /* 168/75 */;
height: .9867rem /* 74/75 */;
background: #30BF78;
border-radius: .1333rem /* 10/75 */ ;
outline: none;
border: none;
font-size: $size30;
color: #FFFFFF;
margin-top: .56rem /* 42/75 */;
}
}
}
</style>
+9
View File
@@ -0,0 +1,9 @@
import { createApp } from 'vue'
import router from './router'
import store from './store'
import App from './App.vue'
const app = createApp(App)
app.use(router).use(store).mount('#app')
+110
View File
@@ -0,0 +1,110 @@
import { createRouter, createWebHistory } from 'vue-router'
import register from "../views/register.vue";
const routerHistory = createWebHistory()
const routes = [
{
path: '/login',
name:'login',
// redirect: '/login',
component: () => import('../views/login.vue')
},
{
path: '/visitorApply',
name:'visitorApply',
// redirect: '/login',
component: () => import('../views/visitorApply.vue'),
meta: { title: '我要预约' }
},
{
path: '/applyInquire',
name:'applyInquire',
component: () => import('../views/applyInquire.vue'),
meta: { title: '预约查询' }
},
{
path: '/applyRecord',
name:'applyRecord',
// redirect: '/login',
component: () => import('../views/applyRecord.vue'),
meta: { title: '预约记录' }
},
{
path: '/apply',
name:'apply',
// redirect: '/login',
component: () => import('../views/apply.vue'),
meta: { title: '我要预约' }
},
{
path: '/adminHome',
name:'adminHome',
// redirect: '/login',
component: () => import('../views/adminHome.vue'),
meta: { title: '智能访客' }
},
{
path: '/visitorRecord',
name:'visitorRecord',
// redirect: '/login',
component: () => import('../views/visitorRecord.vue'),
meta: { title: '访客记录' }
},
{
path: '/Invitation',
name:'Invitation',
// redirect: '/login',
component: () => import('../views/Invitation.vue'),
meta: { title: '我要邀约' }
},
{
path: '/userHome',
name:'userHome',
// redirect: '/login',
component: () => import('../views/userHome.vue'),
meta: { title: '智能访客' }
},
{
path: '/register',
name:'register',
component: register
},
{
path: '/home',
name:'home',
// redirect: '/login',
component: () => import('../views/home.vue'),
meta: { title: '员工信息' }
},
{
path: '/specialCrowd',
name:'specialCrowd',
// redirect: '/login',
component: () => import('../views/specialCrowd.vue'),
meta: { title: '特殊人员录入' }
},
{
path:'/',
name:'',
component:()=>import('../views/userHome.vue'),
meta: { title: '智能访客' }
},
{ path: '/:pathMatch(.*)',
redirect: '/userHome',
}
]
const router = createRouter({
history: routerHistory,
routes
})
const title = document.querySelector('title')
router.beforeEach((to, from, next) => {
title.innerText = to.meta.title
next()
})
export default router
+42
View File
@@ -0,0 +1,42 @@
import { createStore } from 'vuex'
const store = createStore({
state: {
name: '访客预约系统',
stateEnum: {
0: {
statusName:'待审核',
className: 'status',
statusText: '您的来访预约还未审核,请耐心等待',
status: 0
},
1: {
statusName: '已通过',
className: 'pass',
statusText: '预约审核通过',
status: 1
},
2: {
statusName:'未通过',
className: 'refuse',
statusText: '预约审核已拒绝',
status: 2
}
}
},
getters: {
},
mutations: {
},
actions: {
},
modules: {
}
})
export default store
+310
View File
@@ -0,0 +1,310 @@
<template>
<div class='invitation-model-fixed' id="canvas">
<div class="invitation-model">
<img class="english-title" src="../assets/yinwen.png" alt="">
<div class="ewm-box">
<!-- <img :src=" 'data:image/png;base64,'+imgEncode" alt=""> -->
<img src="https://www.baidu.com/s?wd=%E7%99%BE%E5%BA%A6%E7%83%AD%E6%90%9C&sa=ire_dl_gh_logo_texing&rsv_dl=igh_logo_pcs" alt="">
<b class="ewm-box-one"></b>
<b class="ewm-box-two"></b>
<b class="ewm-box-three"></b>
<b class="ewm-box-four"></b>
</div>
<div class="tips">快来保存您的专属邀约二维码吧</div>
<button @click="onClickDown" class="save-button">保存图片</button>
<span @click="onClickA" :href="canvasUrl" download="下载">下载</span>
</div>
<!-- {{ raw }} -->
</div>
<Savesuccessmodel v-if="false" />
</template>
<script setup>
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import { useStore } from 'vuex'
import { rulesForm, resetForm, rulesPhone } from '@/com/index'
import dd from 'dingtalk-jsapi'
import html2canvas from 'html2canvas'
import Savesuccessmodel from '@/components/save-success.vue'
import { getToken, getVisitorRecord, getWhiteCode } from '@/api/admin.js'
/* @data */
const router = useRouter()
const store = useStore()
const imgEncode = ref('')
const raw = ref('')
const canvasUrl = ref('')
/* @setup */
getWhiteCode().then(res =>{
console.log(res)
imgEncode.value = res.imgEncode
// raw.value = window.atob(res.imgEncode);
})
/* @method */
const onClickDown1 = () =>{
// const byteCharacters = atob(imgEncode.value);
// const byteNumbers = new Array(byteCharacters.length);
// for (let i = 0; i < byteCharacters.length; i++) {
// byteNumbers[i] = byteCharacters.charCodeAt(i);
// }
// const byteArray = new Uint8Array(byteNumbers);
// const blob = new Blob([byteArray], { type: 'image/jpeg' });
// raw.value = byteArray
// // 创建可下载链接
// const url = URL.createObjectURL(blob);
// // 创建隐藏的<a>标签
// const link = document.createElement('a');
// link.href = url;
// link.download = 'fileName';
// // 模拟点击隐藏的<a>标签来触发下载
// link.click();
// // 清理URL对象
// URL.revokeObjectURL(url);
}
const onClickA =() =>{
const canvas = document.getElementById('canvas')
console.log(canvas.offsetHeight)
console.log(canvas.scrollHeight)
// alert('asjfjafj1')
// // window.location.href = canvasUrl.value
// dd.previewImage({
// urls: [
// canvasUrl.value,
// ],
// current: 1,
// success: () => {},
// fail: () => {},
// complete: () => {},
// });
// dd.openLink({
// url: 'https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png',
// success: () => {},
// fail: () => {},
// complete: () => {},
// });
// dd.saveImage({
// url: 'https://img.alicdn.com/tps/TB1sXGYIFXXXXc5XpXXXXXXXXXX.jpg',
// success: () => {},
// fail: () => {},
// complete: () => {},
// });
}
const onClickDown = () =>{
let options = { scale: 1, allowTaint: false, useCORS: true, width: '375', height: '649', backgroundColor: null }
autoPicture('canvas')
}
const autoPicture = async (el, options) => {
// let { scale = 1, allowTaint = false, useCORS = true, width = '375', height = '649', backgroundColor = null } = options
const id = document.getElementById(el)
const canvas = await html2canvas(id, {
scale: 1, //缩放比例,默认为1
allowTaint: false, //是否允许跨域图像污染画布
useCORS: true, //是否尝试使用CORS从服务器加载图像
width: id.offsetWidth, //画布的宽度
height: id.offsetHeight, //画布的高度
backgroundColor: null //画布的背景色,默认为透明
})
// canvas.toDataURL('image/png')
console.log(canvas.toDataURL('image/png'))
canvasUrl.value = canvas.toDataURL('image/png')
}
</script>
<style lang='scss' scoped>
.invitation-model-fixed{
position: fixed;
width: 100%;
height: 100%;
background: rgba(60,60,67,0.6);
.invitation-model{
position: absolute;
top: 20%;
left: 50%;
transform: translateX(-50%);
width: 7.2267rem /* 542/75 */;
height: 9.3067rem /* 698/75 */;
background: #FFFFFF;
border-radius: .5333rem /* 40/75 */.5333rem /* 40/75 */ .5333rem /* 40/75 */ .5333rem /* 40/75 */;
// overflow: hidden;
opacity: 1;
background: url('../assets/yaoqingbeijing.png') left top/100% 100% no-repeat;
.english-title{
margin: 1.1733rem /* 88/75 */ auto 0;
width: 4.9867rem /* 374/75 */;
height: .6133rem /* 46/75 */;
}
.ewm-box{
position: relative;
border-radius: .1333rem /* 10/75 */;
// border: .1333rem /* 10/75 */ solid #30BF78;
width: 4.64rem /* 348/75 */;
height: 4.64rem /* 348/75 */;
margin: .5067rem /* 38/75 */ auto 0;
display: flex;
justify-content: center;
align-items: center;
img{
width: 4rem /* 300/75 */;
height: 4rem;
}
}
@mixin border-basics {
position: absolute;
width: 1.1733rem /* 88/75 */;
height: 1.1733rem /* 88/75 */;
// border-radius: .1333rem /* 10/75 */;
opacity: 1;
border: .1333rem /* 10/75 */ solid #30BF78;
}
.ewm-box-one::after{
content: '';
position: absolute;
left: 0;
top: 0;
// border: .1333rem /* 10/75 */ solid #30BF78;
width: .1333rem /* 10/75 */;
height: 1.3067rem /* 98/75 */;
background-color: #30BF78;
border-radius: .2667rem /* 20/75 */ 0 .1333rem /* 10/75 */ .1333rem /* 10/75 */;
}
.ewm-box-one::before{
content: '';
position: absolute;
left: 0;
top: 0;
// border: .1333rem /* 10/75 */ solid #30BF78;
width: 1.3067rem /* 98/75 */;
height: .1333rem /* 10/75 */;
background-color: #30BF78;
border-radius: .2667rem /* 20/75 */ .1333rem /* 10/75 */ .1333rem /* 10/75 */ 0 ;
}
.ewm-box-two::after{
content: '';
position: absolute;
right: 0;
top: 0;
// border: .1333rem /* 10/75 */ solid #30BF78;
width: .1333rem /* 10/75 */;
height: 1.3067rem /* 98/75 */;
background-color: #30BF78;
border-radius: 0 .2667rem /* 20/75 */ .1333rem /* 10/75 */ .1333rem /* 10/75 */;
}
.ewm-box-two::before{
content: '';
position: absolute;
right: 0;
top: 0;
// border: .1333rem /* 10/75 */ solid #30BF78;
width: 1.3067rem /* 98/75 */;
height: .1333rem /* 10/75 */;
background-color: #30BF78;
border-radius: .1333rem /* 10/75 */ .2667rem /* 20/75 */ 0 .1333rem /* 10/75 */ ;
}
// .ewm-box-three{
// @include border-basics;
// left: 0;
// bottom: 0;
// border-top: none;
// border-right: none;
// border-radius: 0 0 0 .1333rem /* 10/75 */ ;
// }
.ewm-box-three::after{
content: '';
position: absolute;
left: 0;
bottom: 0;
// border: .1333rem /* 10/75 */ solid #30BF78;
width: .1333rem /* 10/75 */;
height: 1.3067rem /* 98/75 */;
background-color: #30BF78;
border-radius: .1333rem /* 10/75 */ .1333rem /* 10/75 */ 0 .2667rem /* 20/75 */ ;
}
.ewm-box-three::before{
content: '';
position: absolute;
left: 0;
bottom: 0;
// border: .1333rem /* 10/75 */ solid #30BF78;
width: 1.3067rem /* 98/75 */;
height: .1333rem /* 10/75 */;
background-color: #30BF78;
border-radius: 0 .1333rem /* 10/75 */ .1333rem /* 10/75 */ .2667rem /* 20/75 */ ;
}
// .ewm-box-four{
// @include border-basics;
// bottom: 0;
// right: 0;
// border-top: none;
// border-left: none;
// border-radius: 0 0 .1333rem /* 10/75 */ 0;
// }
.ewm-box-four::after{
content: '';
position: absolute;
right: 0;
bottom: 0;
// border: .1333rem /* 10/75 */ solid #30BF78;
width: .1333rem /* 10/75 */;
height: 1.3067rem /* 98/75 */;
background-color: #30BF78;
border-radius: .1333rem /* 10/75 */ .1333rem /* 10/75 */ .2667rem /* 20/75 */ 0 ;
}
.ewm-box-four::before{
content: '';
position: absolute;
right: 0;
bottom: 0;
// border: .1333rem /* 10/75 */ solid #30BF78;
width: 1.3067rem /* 98/75 */;
height: .1333rem /* 10/75 */;
background-color: #30BF78;
border-radius: .1333rem /* 10/75 */ 0 .2667rem /* 20/75 */ .1333rem /* 10/75 */ ;
}
.tips{
font-size: $size30;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #999999;
margin-top: .72rem /* 54/75 */;
text-align: center;
line-height: .6133rem /* 46/75 */;
}
.save-button{
position: absolute;
bottom: 0;
left: 50%;
z-index: 10;
transform: translateY(50% ) translateX(-50% );
width: 3.76rem /* 282/75 */;
height: 1.1467rem /* 86/75 */;
line-height: 1.1467rem /* 86/75 */;
background: #30BF78;
border-radius: 1.3333rem /* 100/75 */;
opacity: 1;
font-size: $lg-size;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #FFFFFF;
text-align: center;
border: none;
outline: none;
}
}
}
</style>
+143
View File
@@ -0,0 +1,143 @@
<template>
<div v-if="!canvasUrl" class='invitation-model-fixed' id="canvas">
<img src="../assets/yaoqingbeijingtu.png" alt="" class="big-img">
<img class="ewm" :src="'data:image/png;base64,'+imgEncode" alt="">
<!-- <img src="" alt=""> -->
</div>
<div v-if="canvasUrl" class="img-fixed">
<img :src="canvasUrl" alt="">
</div>
<!-- <img src="" alt=""> -->
<Savesuccessmodel v-if="false" />
</template>
<script setup>
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import { useStore } from 'vuex'
import { rulesForm, resetForm, rulesPhone } from '@/com/index'
import dd from 'dingtalk-jsapi'
import html2canvas from 'html2canvas'
import Savesuccessmodel from '@/components/save-success.vue'
import { getToken, getVisitorRecord, getWhiteCode } from '@/api/admin.js'
/* @data */
const router = useRouter()
const store = useStore()
const imgEncode = ref('')
const raw = ref('')
const canvasUrl = ref('')
/* @setup */
/* @method */
const onClickDown1 = () =>{
// const byteCharacters = atob(imgEncode.value);
// const byteNumbers = new Array(byteCharacters.length);
// for (let i = 0; i < byteCharacters.length; i++) {
// byteNumbers[i] = byteCharacters.charCodeAt(i);
// }
// const byteArray = new Uint8Array(byteNumbers);
// const blob = new Blob([byteArray], { type: 'image/jpeg' });
// raw.value = byteArray
// // 创建可下载链接
// const url = URL.createObjectURL(blob);
// // 创建隐藏的<a>标签
// const link = document.createElement('a');
// link.href = url;
// link.download = 'fileName';
// // 模拟点击隐藏的<a>标签来触发下载
// link.click();
// // 清理URL对象
// URL.revokeObjectURL(url);
}
const onClickA =() =>{
const canvas = document.getElementById('canvas')
console.log(canvas.offsetHeight)
console.log(canvas.scrollHeight)
dd.previewImage({
urls: [
canvasUrl.value,
],
current: 1,
success: () => {},
fail: () => {},
complete: () => {},
});
}
const onClickDown = () =>{
// let options = { scale: 1, allowTaint: false, useCORS: true, width: '375', height: '649', backgroundColor: null }
autoPicture('canvas')
}
const autoPicture = async (el, options) => {
// let { scale = 1, allowTaint = false, useCORS = true, width = '375', height = '649', backgroundColor = null } = options
const id = document.getElementById(el)
// alert(document.getElementById('canvas').offsetWidth
const canvas = await html2canvas(id, {
scale: 2, //缩放比例,默认为1
allowTaint: false, //是否允许跨域图像污染画布
useCORS: true, //是否尝试使用CORS从服务器加载图像
// width: document.getElementById('canvas').offsetWidth, //画布的宽度
// height: document.getElementById('canvas').offsetHeight, //画布的高度
width: document.getElementById('canvas').offsetWidth, //画布的宽度
height: document.getElementById('canvas').offsetHeight, //画布的高度
backgroundColor: null //画布的背景色,默认为透明
})
// canvas.toDataURL('image/png')
// console.log(canvas.toDataURL('image/png'))
canvasUrl.value = canvas.toDataURL('image/png')
}
// localStorage.setItem('token', 123456)
getWhiteCode().then(res =>{
console.log(res)
imgEncode.value = res.imgEncode
// autoPicture(canvas)
// raw.value = window.atob(res.imgEncode);
setTimeout(()=>{
onClickDown()
}, 100)
})
</script>
<style lang='scss' scoped>
.invitation-model-fixed{
position: fixed;
width: 100%;
height: 100%;
// background: url('../assets/yaoqingbeijingtu.png') left top/100% auto no-repeat;
background: #fff;
.big-img{
width: 100%;
}
.ewm{
position: absolute;
left: 50%;
top: 4rem /* 300/75 */;
transform: translateX(-50%);
width:4.5333rem /* 340/75 */;
height: 4.5333rem /* 340/75 */;
}
}
.img-fixed{
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
img{
width: 100%;
}
}
</style>
+321
View File
@@ -0,0 +1,321 @@
<template>
<div v-if="true" class='box'>
<header>
<h3>Hello~{{userName}}</h3>
<h4> 你又来审批预约了啊ㄟ( , )</h4>
<div class="plate-box">
<div @click="onClickGo('/Invitation')" class="plate-button">
<img src="../assets/ren.png" alt="">
<div class="right">
<div>我要邀约</div>
<p>保存专属二维码</p>
</div>
</div>
<div @click="onClickGo('/visitorRecord')" class="plate-button">
<img src="../assets/shuben.png" alt="">
<div class="right">
<div>访客记录</div>
<p>查看历史来访记录</p>
</div>
</div>
</div>
<div class="position-img">
<img src="../assets/fangzi.png" alt="">
</div>
</header>
<div class="content">
<h2>
<div>待审批列表</div>
<span @click="onClickGo('/visitorRecord')">查看全部</span>
</h2>
<div class="content-ul">
<div v-for="item in recordArr" class="wait-list" :key="item.id">
<h5>被拜访人{{ item.visitingName }}</h5>
<h5>拜访时间{{ item.hours }}</h5>
<div class="apply-box">
<p><span>访客姓名</span><span>{{ item.name }}</span></p>
<p><span>联系方式</span><span>{{ item.phone }}</span></p>
<p><span>随行人数</span><span>{{ item.visitingNum }} </span></p>
<p><span>来访事由</span><span>{{ item.reason }}</span></p>
</div>
<div class="status" :class="stateEnum[item.status].className">{{ stateEnum[item.status].statusName }}</div>
</div>
</div>
<div v-if="isLoading && recordArr.length == 0" class="no-have">
<img class="" src="../assets/nodata.png" alt="">
<p>暂无待审批数据</p>
</div>
</div>
</div>
</template>
<script setup>
import { reactive, ref } from 'vue'
import { useRouter } from 'vue-router'
import { useStore } from 'vuex'
import { Toast, ImagePreview } from 'vant';
import dd from 'dingtalk-jsapi'
import { rulesForm, resetForm, rulesPhone } from '@/com/index'
import { getToken, getVisitorRecord, getWhiteCode } from '@/api/admin.js'
/* @data */
const router = useRouter()
const store = useStore()
const isLoading = ref(false)
const token = ref('')
const userName = ref('')
const query = reactive({
status: 0,
size: 2,
current: 1,
})
const recordArr = ref([])
const stateEnum = store.state.stateEnum
token.value = localStorage.getItem('token')
localStorage.setItem('redirect', '/adminHome')
/* @setup */
const login = () =>{
Toast.loading({ duration: 0, forbidClick: true, message: '登录中', });
dd.getAuthCode({
corpId: 'ding03f8e88dde9b426835c2f4657eb6378f',
success: (res) => {
const { code } = res;
localStorage.removeItem('token');
localStorage.removeItem('user');
getToken(code).then(data =>{
localStorage.setItem('user', JSON.stringify(data.data) )
localStorage.setItem('token', data.data.authorization)
userName.value = data.data.name
Toast.clear()
Toast.success('登录成功')
visitorList()
})
},
fail: (err) => {
},
complete: () => {},
});
}
// login()
const visitorList = () =>{
Toast.loading({ duration: 0, forbidClick: true, message: '加载中', });
getVisitorRecord(query).then(res =>{
isLoading.value = true
recordArr.value = res.data
}).catch(err=>{
isLoading.value = true
})
}
// visitorList()
if(token.value){
visitorList()
console.log((JSON.parse(localStorage.getItem('user'))).name )
userName.value = JSON.parse(localStorage.getItem('user')).name
}else{
login()
}
/* @method */
const onClickGo = (url = '/adminHome') =>{
router.push(url)
}
</script>
<style lang='scss' scoped>
.box{
background-color: #F9FBFC;
display: flex;
flex-direction: column;
header{
position: relative;
width: 100%;
height: 10.4rem /* 780/75 */;
background: url('../assets/guanlibeijing.png') left top/100% 100% no-repeat;
box-sizing: border-box;
h3{
padding: 1.0933rem /* 82/75 */ .6933rem /* 52/75 */ .1067rem /* 8/75 */;
box-sizing: border-box;
line-height: .72rem /* 54/75 */;
font-size: .48rem /* 36/75 */;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 500;
color: #222222;
}
h4{
line-height: .48rem /* 36/75 */;
font-size: $sm-size;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #FFFFFF;
padding: 0 .6933rem /* 52/75 */ 1.12rem /* 84/75 */;
box-sizing: border-box;
}
.position-img{
position: absolute;
right: .4267rem /* 32/75 */;
top: .2933rem /* 22/75 */;
width: 3.6267rem /* 272/75 */;
height: 2.96rem /* 222/75 */;
img{
width: 100%;
height: 100%;
}
}
.plate-box{
width: 100%;
padding: 0 .4rem /* 30/75 */;
box-sizing: border-box;
display: flex;
justify-content: space-between;
.plate-button{
width: 4.4rem /* 330/75 */;
height: 1.92rem /* 144/75 */;
background: linear-gradient(180deg, rgba(255,255,255,0.3) 0%, rgba(255,255,255,0.6) 48%);
border-radius: .2667rem /* 20/75 */;
border: .0267rem /* 2/75 */ solid #FFFFFF;
display: flex;
align-items: center;
img{
width: .9867rem /* 74/75 */;
height: .9867rem /* 74/75 */;
margin-left: .32rem /* 24/75 */;
}
.right{
margin-left: .3467rem /* 26/75 */;
div{
line-height: .64rem /* 48/75 */;
font-size: $lg-size;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 500;
color: #222222;
}
P{
line-height: .4rem /* 30/75 */;
font-size: .2667rem /* 20/75 */;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #5F6583;
margin-top: .1067rem /* 8/75 */;
}
}
}
}
}
.content{
flex: 1;
overflow: auto;
position: relative;
// top: 5.8667rem /* 440/75 */;
margin-top: -4.5333rem /* 340/75 */;
width: 100%;
padding: 0 .4rem /* 30/75 */;
box-sizing: border-box;
h2{
width: 100%;
padding: .2667rem /* 20/75 */ .32rem /* 24/75 */;
box-sizing: border-box;
display: flex;
justify-content: space-between;
line-height: .72rem /* 54/75 */;
font-size: .48rem /* 36/75 */;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 500;
color: #222222;
span{
// line-height: 36px;
font-size: $sm-size;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #999999;
}
}
.content-ul{
.wait-list{
position: relative;
width: 9.2rem /* 690/75 */;
// height: 470px;
background: #FFFFFF;
border-radius: .2667rem /* 20/75 */;
padding: .48rem /* 36/75 */;
box-sizing: border-box;
margin-bottom: .2667rem /* 20/75 */;
h5{
height: .6667rem /* 50/75 */;
font-size: $md-size;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #222222;
}
.apply-box{
width: 100%;
padding: .4rem /* 30/75 */ .3733rem /* 28/75 */ .5067rem /* 38/75 */;
box-sizing: border-box;
border-radius: .1333rem /* 10/75 */;
font-size: $md-size;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #999999;
line-height: .72rem /* 54/75 */;
background: #F5F7F9;
span:last-child{
color: #5F6583;
}
}
.status{
position: absolute;
top: 0;
right: 0;
width: 1.52rem /* 114/75 */;
height: .6667rem /* 50/75 */;
line-height: .6667rem /* 50/75 */;
background: #FFE9E4;
border-radius: 0px .2667rem /* 20/75 */ 0px .1333rem /* 10/75 */;
color: #FF6643;
font-size: $sm-size;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #FF6643;
text-align: center;
}
.refuse{
color: #5F6583;
background: #EAEEF6;
}
.pass{
color: #30BF78;
background: #E7F8F5;
}
}
}
.no-have{
// display: block;
width: 7.4667rem /* 560/75 */;
height: 5.3867rem /* 404/75 */;
margin: .8rem /* 60/75 */ auto;
img{
width: 100%;
height: 100%;
}
// font-size: 15px;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #999999;
text-align: center;
}
}
}
</style>
+720
View File
@@ -0,0 +1,720 @@
<template>
<div v-if="true" class='box'>
<div class="apply-box">
<img class="text" src="../assets/zhinengfangke.png" alt="">
<p>来访提前约 拜访开门无需按铃</p>
<div class="apply-content">
<h3>
<img src="../assets/icon-shenfenzheng.png" alt="">
<span>拜访信息<span>(我找谁)</span></span>
</h3>
<div class="apply-content-form">
<!-- <Selectli label="被拜访对象" v-model="form.visitingTel" :columns="visitDict" :isMust="true" :isline="true" name="name" nameId="userId"/> -->
<!-- <div class="search-select-box">
<label class="label"><span>*&nbsp;</span>被拜访人</label>
<div class="search-select-input">
<input @click.stop="onClickVisitor" @input="onInputVisior" :value="visitorName" type="text" placeholder="可填写选择">
<div v-if="isSelect && visitDict.length" class="select-box">
<b></b>
<div class="select-sroll-box">
<div @click.self="onClickSelect(item)" v-for="item in visitDict" :class="item.userId == form.visitingTel ? 'select-active' : ''" class="select-li" :key="item.userId">{{ item.name }}</div>
</div>
</div>
<div v-if="isSelect && !visitDict.length" class="select-box">
<b></b>
<div class="select-li" >查无此人</div>
</div>
</div>
</div> -->
<Inputli v-if="telName" v-model="telName" @input="onInputName" label="被访人" :isMust="true" :isline="true" isMustPosition="left"/>
<Inputli v-else v-model="form.visitingTel" @blur="onBlurTel" @input="onInputTel" label="被访人电话" :isMust="true" :isline="true" isMustPosition="left"/>
<Selectdate v-model="form.hours" label="来访时间" :isMust="true" :isline="false" :minDate="minDate"/>
</div>
</div>
<img class="absolute-img" src="../assets/woyaoyuyue.png" alt="">
</div>
<p class="tips">
说明拜访时间前后一小时都可刷脸通过门禁
</p>
<div class="visitor-content">
<h3>
<img src="../assets/icon-shenfenzheng.png" alt="">
<span>访客信息<span>(我是谁)</span></span>
</h3>
<div class="visitor-content-form">
<Inputli v-model="form.name" label="访客姓名" :isMust="true" :isline="true" isMustPosition="left"/>
<Inputli v-model="form.phone" label="联系方式" :isMust="true" :isline="true" isMustPosition="left"/>
<Inputli v-model="form.visitingNum" label="随行人数" :isMust="true" :isline="true" isMustPosition="left"/>
<Inputli v-model="form.reason" label="来访事由" :isMust="true" :isline="true" isMustPosition="left"/>
<div class="upload-box">
<label for=""><span>*&nbsp;</span>人脸照片</label>
<div v-if="!form.img" class="upload-button">
<img @click="onClickUpload" src="../assets/xiangji.png" alt="">
<input v-if="isDisabled" @change="onChangeFile" type="file" accept="image/*">
</div>
<div v-else class="face-img">
<img :src="form.img" alt="">
<input v-if="true" @change="onChangeFile" type="file" accept="image/*">
</div>
</div>
</div>
</div>
<p class="tips">
说明人脸照片用于通过门禁系统请上传正脸无遮挡照片
</p>
<p class="tel-tips">
如果您在使用中遇到问题请联系<span @click="onClickCall">400040-1573</span>
</p>
<!-- <div class="disclaimer-box">
<input type="checkbox">
<p>勾选表示同意<span>用户协议</span></p>
</div> -->
<button @click="onClcikSubmit" class="submit-button " :class="isSubmit ? 'active-button' : ''">提交</button>
<Successmodel v-if="isSuccess" @click="onClickAffirm" />
<div v-if="false" class="disclaimer-model-fixed">
<h3>用户协议</h3>
<div class="disclaimer-model">
阿哈撒发哈师范哈撒发哈很舒服哈哈
阿哈撒发哈师范哈撒发哈很舒服哈哈
阿哈撒发哈师范哈撒发哈很舒服哈哈
阿哈撒发哈师范哈撒发哈很舒服哈哈
阿哈撒发哈师范哈撒发哈很舒服哈哈
阿哈撒发哈师范哈撒发哈很舒服哈哈
阿哈撒发哈师范哈撒发哈很舒服哈哈
阿哈撒发哈师范哈撒发哈很舒服哈哈
阿哈撒发哈师范哈撒发哈很舒服哈哈
阿哈撒发哈师范哈撒发哈很舒服哈哈
阿哈撒发哈师范哈撒发哈很舒服哈哈
</div>
</div>
</div>
</template>
<script setup>
import { reactive, ref, watch } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { useStore } from 'vuex'
import { Toast, ImagePreview , Uploader, Dialog } from 'vant';
import { rulesForm, resetForm, rulesPhone, rulesFormFBoolean, formatDate } from '@/com/index'
import Inputli from '@/components/input-li.vue'
import Selectli from '@/components/select-li.vue'
import Selectdate from '@/components/select-li-date.vue'
import Successmodel from '../components/success.vue'
import { postApply, getVisitor, getUserNameByTel } from '@/api/user.js'
import { uploadApi } from '@/api/upload'
/* @data */
const router = useRouter()
const route = useRoute()
const store = useStore()
const phoneReg = /(^1\d{10}$)|(^[0-9]\d{7}$)/;
let form = reactive({
name: "",
phone: "",
hours: "",
reason: "",
img: "",
visitingTel: "",
visitingNum: '',
})
const isSubmit = ref(false)
const visitDict = ref([]) // 被访客数据
const visitAllDict = ref([]) // 被访客数据
const isDisabled = ref(false) // 是否能提交
const isSuccess = ref(false) // 是否提交成功
const isSelect = ref( false) // 是否显示下拉选择
const visitorName = ref( '') // 是否显示下拉选择的名字
const telName = ref( '') // 电话查询姓名
/* watch */
watch(() => form, (v1, v2) => {
isSubmit.value = rulesFormFBoolean(v2)
if(v2.name && v2.phone){
if(!phoneReg.test(v2.phone)){
isDisabled.value = false
}else{
isDisabled.value = true
}
}else{
isDisabled.value = false
}
},{ deep: true }
) // 深度监听 ) // 监听单个
/* @method */
const onInputTel = (e) =>{
}
const onInputName = (e) =>{
form.visitingTel = ''
telName.value = ''
}
const onBlurTel = (phone) =>{
console.log(phone)
if(rulesPhone(phone)){
getUserNameByTel(phone).then(res =>{
console.log(res)
if(res.data){
telName.value = res.data
}
}).catch(err =>{
console.log(err)
Dialog.alert({
message: err.msg,
}).then(() => {
// on close
});
})
}
}
// const onBlurVisitor = () =>{
// isSelect.value = false
// let item = visitAllDict.value.find(item =>{ return item.userId == form.visitingTel })
// if(item){
// visitorName.value = item.name
// visitDict.value = visitAllDict.value
// }else{
// visitorName.value = '' // 是否显示下拉选择的名字
// visitDict.value = visitAllDict.value
// }
// }
// 输入框
// const onInputVisior = (e) =>{
// visitorName.value = e.target.value
// let arr = []
// visitAllDict.value.map( item =>{
// if(item.name.search(e.target.value) != -1){
// arr.push(item)
// }
// })
// visitDict.value = [ ...arr ]
// }
// 点击选择了拜访对象
// const onClickSelect = (item) =>{
// form.visitingTel = item.userId
// visitorName.value = item.name
// isSelect.value = false
// }
// const onClickVisitor = () =>{
// isSelect.value = true
// }
// 点击确认模态框
const onClickAffirm = () =>{
router.go(-1)
}
const onClickUpload = () =>{
if(isDisabled.value){
}else{
Toast.fail('请填写访客姓名和正确的联系方式!')
}
}
const onClcikSubmit = () =>{
if(isSubmit.value){
if(rulesPhone(form.phone) && rulesPhone(form.visitingTel)){
Toast.loading({ duration: 0, forbidClick: true, message: '提交中', });
postApply(form).then(res =>{
Toast.clear();
isSuccess.value = true
})
}
}else{
Toast.fail('请先填写完整信息后提交!')
}
}
// 上传照片
const onChangeFile = (e) =>{
console.log(e.target.files[0].size)
if(e.target.files[0]){
newcompressImg(e.target.files[0], 0.5).then(res =>{
let formData = new FormData()
// formData.append('file', e.target.files[0])
formData.append('file', res.file)
formData.append('userId', form.phone)
formData.append('name', form.name)
Toast.loading({ duration: 0, forbidClick: true, message: '上传中', });
uploadApi(formData).then(res =>{
Toast.success(res.msg);
form.img = res.data
})
}).catch(err =>{
Toast.fail('图片上传失败,请重新上传')
})
}
}
const onClickCall = () =>{
window.location.href = 'tel:400-040-1573'
}
const newcompressImg = (file, quality) =>{
var qualitys = 0.52
if (parseInt((file.size / 1024).toFixed(2)) < 1024) {
qualitys = 0.85
}
if (5 * 1024 < parseInt((file.size / 1024).toFixed(2))) {
qualitys = 0.92
}
if (quality) {
qualitys = quality
}
if (file[0]) {
return Promise.all(Array.from(file).map(e => this.compressImg(e,
qualitys))) // 如果是 file 数组返回 Promise 数组
} else {
return new Promise((resolve) => {
if ((file.size / 1024).toFixed(2) < 300) {
resolve({
file: file
})
} else {
const reader = new FileReader() // 创建 FileReader
reader.onload = ({
target: {
result: src
}
}) => {
const image = new Image() // 创建 img 元素
image.onload = async() => {
const canvas = document.createElement('canvas') // 创建 canvas 元素
const context = canvas.getContext('2d')
var targetWidth = image.width
var targetHeight = image.height
var originWidth = image.width
var originHeight = image.height
if (1 * 1024 <= parseInt((file.size / 1024).toFixed(2)) && parseInt((file.size / 1024).toFixed(2)) <= 10 * 1024) {
var maxWidth = 1600
var maxHeight = 1600
targetWidth = originWidth
targetHeight = originHeight
// 图片尺寸超过的限制
if (originWidth > maxWidth || originHeight > maxHeight) {
if (originWidth / originHeight > maxWidth / maxHeight) {
// 更宽,按照宽度限定尺寸
targetWidth = maxWidth
targetHeight = Math.round(maxWidth * (originHeight / originWidth))
} else {
targetHeight = maxHeight
targetWidth = Math.round(maxHeight * (originWidth / originHeight))
}
}
}
if (10 * 1024 <= parseInt((file.size / 1024).toFixed(2)) && parseInt((file.size / 1024).toFixed(2)) <= 20 * 1024) {
maxWidth = 1400
maxHeight = 1400
targetWidth = originWidth
targetHeight = originHeight
// 图片尺寸超过的限制
if (originWidth > maxWidth || originHeight > maxHeight) {
if (originWidth / originHeight > maxWidth / maxHeight) {
// 更宽,按照宽度限定尺寸
targetWidth = maxWidth
targetHeight = Math.round(maxWidth * (originHeight / originWidth))
} else {
targetHeight = maxHeight
targetWidth = Math.round(maxHeight * (originWidth / originHeight))
}
}
}
canvas.width = targetWidth
canvas.height = targetHeight
context.clearRect(0, 0, targetWidth, targetHeight)
context.drawImage(image, 0, 0, targetWidth, targetHeight) // 绘制 canvas
const canvasURL = canvas.toDataURL('image/jpeg', qualitys)
const buffer = atob(canvasURL.split(',')[1])
let length = buffer.length
const bufferArray = new Uint8Array(new ArrayBuffer(length))
while (length--) {
bufferArray[length] = buffer.charCodeAt(length)
}
const miniFile = new File([bufferArray], file.name, {
type: 'image/jpeg'
})
resolve({
file: miniFile,
origin: file,
beforeSrc: src,
afterSrc: canvasURL,
beforeKB: Number((file.size / 1024).toFixed(2)),
afterKB: Number((miniFile.size / 1024).toFixed(2))
})
}
image.src = src
}
reader.readAsDataURL(file)
}
})
}
}
/* @setup */
const minDate = ref( formatDate(new Date()))
console.log(route.query.tel)
if(route.query.tel){
form.visitingTel = route.query.tel
onBlurTel(route.query.tel)
}
getVisitor().then( res=>{
visitDict.value = res.data
visitAllDict.value = res.data
})
</script>
<style lang='scss' scoped>
.box{
width: 100%;
height: 100%;
background: #F5F5F5;
padding: 0 0 1.08rem /* 81/75 */;
box-sizing: border-box;
overflow: auto;
.apply-box{
position: relative;
width: 100%;
height: 8.0267rem /* 602/75 */;
background: url('../assets/yuyuebeijing.png') left top/ 100% 100% no-repeat;
padding: 1.04rem /* 78/75 */ 0 0;
box-sizing: border-box;
.text{
width: 3.5467rem /* 266/75 */;
height: 1.1467rem /* 86/75 */;
margin-left: .6133rem /* 46/75 */;
}
p{
line-height: .48rem /* 36/75 */;
font-size: $sm-size;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #999999;
padding: .1067rem /* 8/75 */ 0 0 .7467rem /* 56/75 */;
}
.apply-content{
width: 9.2rem /* 690/75 */;
height: 4.32rem /* 324/75 */;
background: #FFFFFF;
border-radius: .2667rem /* 20/75 */;
margin: 1.0133rem /* 76/75 */ auto 0;
h3{
padding: .5067rem /* 38/75 */ .3467rem /* 26/75 */ 0 .4533rem /* 34/75 */;
box-sizing: border-box;
display: flex;
align-items: center;
color: #222222;
font-size: $size30;
img{
width: .5867rem /* 44/75 */;
height: .5333rem /* 40/75 */;
margin-right: .16rem /* 12/75 */;
}
span{
span{
font-size: .32rem /* 24/75 */;
color: #666;
}
}
}
.apply-content-form{
width: 100%;
padding: 0 .3467rem /* 26/75 */ 0 .4533rem /* 34/75 */;
box-sizing: border-box;
.search-select-box{
position: relative;
width: 100%;
height: 1.5733rem /* 118/75 */;
// background: #F9F9F9;
// border-radius: .2667rem /* 20/75 */;
padding: 0 .0267rem /* 2/75 */ 0;
box-sizing: border-box;
display: flex;
// margin-bottom: .2667rem /* 20/75 */;
border-bottom: 0.0267rem dashed #D9D9D9;
.label{
flex: 1;
font-size: $size26;
color: #333333;
line-height: 1.5733rem /* 118/75 */;
span{
color: #FF0000;
margin-right: .0267rem /* 2/75 */;
}
}
.search-select-input{
position: relative;
height: 100%;
box-sizing: border-box;
input{
flex: 1;
border: none;
outline: none;
text-align: right;
font-size: $size26;
color: #333333;
// background: #F9F9F9;
padding-right: 0.3733rem;
// line-height: 1.5733rem /* 118/75 */;
box-sizing: border-box;
height: 100%;
}
input::-webkit-input-placeholder {
font-size: $size26;
color: #999999;
}
.select-box{
position: absolute;
right: 0;
top: 1.8667rem /* 140/75 */;
z-index: 5;
width: 3.7333rem /* 280/75 */;
// max-height: 5.0667rem /* 380/75 */;
background: #FFFFFF;
box-shadow: 0px 0px .2667rem /* 20/75 */ .1067rem /* 8/75 */ rgba(0,0,0,0.1);
border-radius: .0533rem /* 4/75 */ ;
// overflow: auto;
.select-sroll-box{
// position: absolute;
// right: 0;
// top: 1.8667rem /* 140/75 */;
// z-index: 5;
width: 100%;
max-height: 5.0667rem /* 380/75 */;
background: #FFFFFF;
// box-shadow: 0px 0px .2667rem /* 20/75 */ .1067rem /* 8/75 */ rgba(0,0,0,0.1);
// border-radius: .0533rem /* 4/75 */ ;
overflow: auto;
}
.select-li{
height: 1.0133rem /* 76/75 */;
background: #fff;
border-radius: .0533rem /* 4/75 */ .0533rem /* 4/75 */ 0px 0px;
font-size: $size26;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #999999;
line-height: 1.0133rem /* 76/75 */;
text-align: center;
}
.select-active{
color: #30BF78;
background: #F3FEFB;
}
}
b{
position: absolute;
left: 50%;
// top: -100%;
// width: .2rem /* 15/75 */;
// height: 20px;
// background: red;
// display: block;
width: 0;
height: 0;
border: .16rem /* 12/75 */ solid transparent;
border-bottom: .16rem /* 12/75 */ solid #fff;
transform: translateY(-100%) translateX(-50%);
// margin-top: -.6667rem /* 50/75 */;
}
}
}
}
}
.absolute-img{
position: absolute;
top: .5333rem /* 40/75 */;
right: .4rem /* 30/75 */;
width: 4.5067rem /* 338/75 */;
height: 3.7867rem /* 284/75 */;
}
}
.tips{
line-height: .4533rem /* 34/75 */;
font-size: .2933rem /* 22/75 */;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #999999;
padding: .2133rem /* 16/75 */ 0 .4267rem /* 32/75 */ .8rem /* 60/75 */;
box-sizing: border-box;
}
.visitor-content{
width: 9.2rem /* 690/75 */;
// height: 4.32rem /* 324/75 */;
background: #FFFFFF;
border-radius: .2667rem /* 20/75 */;
margin: 0 auto 0;
h3{
padding: .5067rem /* 38/75 */ .3467rem /* 26/75 */ 0 .4533rem /* 34/75 */;
box-sizing: border-box;
display: flex;
align-items: center;
color: #222222;
font-size: $size30;
img{
width: .5867rem /* 44/75 */;
height: .5333rem /* 40/75 */;
margin-right: .16rem /* 12/75 */;
}
span{
span{
font-size: .32rem /* 24/75 */;
color: #666;
}
}
}
.visitor-content-form{
width: 100%;
padding: 0 .3467rem /* 26/75 */ .16rem /* 12/75 */ .4533rem /* 34/75 */;
box-sizing: border-box;
.upload-box{
width: 100%;
height: 1.9733rem /* 148/75 */;
// padding: 0 .6933rem /* 52/75 */ 0 0;
padding-right: .3733rem /* 28/75 */;
box-sizing: border-box;
display: flex;
justify-content: space-between;
align-items: center;
color: #333333;
font-size: $size26;
label{
line-height: 1.9733rem /* 148/75 */;
span{
color: #FF0000;
}
}
.upload-button{
position: relative;
width: 1.3333rem /* 100/75 */;
height: 1.3333rem /* 100/75 */;
background: #FFFFFF;
border-radius: .1333rem /* 10/75 */;
border: .0267rem /* 2/75 */ dashed #D9D9D9;
display: flex;
align-items: center;
justify-content: center;
// overflow: hidden;
input{
position: absolute;
left: 0;
top: 0;
width: 1.3333rem /* 100/75 */;
height: 1.3333rem /* 100/75 */;
opacity: 0;
}
}
.face-img{
position: relative;
width: 1.3333rem /* 100/75 */;
height: 1.3333rem /* 100/75 */;
background: #FFFFFF;
border-radius: .1333rem /* 10/75 */;
img{
width: 100%;
height: 100%;
border-radius: .1333rem /* 10/75 */;
}
input{
position: absolute;
left: 0;
top: 0;
width: 1.3333rem /* 100/75 */;
height: 1.3333rem /* 100/75 */;
opacity: 0;
}
}
}
}
}
.disclaimer-box{
display: flex;
justify-content: center;
input{
margin-right: .1333rem /* 10/75 */;
}
span{
color: #30BF78;
}
}
.tel-tips{
line-height: .4533rem /* 34/75 */;
font-size: .2933rem /* 22/75 */;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #999999;
padding: 0 0 .4267rem /* 32/75 */ .8rem /* 60/75 */;
box-sizing: border-box;
margin-top: -.24rem /* 18/75 */;
span{
color: #409eff;
}
}
.submit-button{
display: block;
width: 6.72rem /* 504/75 */;
height: 1.12rem /* 84/75 */;
background: #D9D9D9;
border-radius: 1.3333rem /* 100/75 */ ;
opacity: 1;
font-size: $size30;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 500;
color: #222222;
border: none;
outline: none;
margin: 1.0133rem /* 76/75 */ auto 0;
}
.active-button{
background-color: #30BF78;
color: #fff;
}
}
.disclaimer-model-fixed{
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: #fff;
display: flex;
flex-direction: column;
padding: .4rem /* 30/75 */;
box-sizing: border-box;
h3{
font-size: .4267rem /* 32/75 */;
line-height: .8rem /* 60/75 */;
text-align: center;
}
.disclaimer-model{
flex: 1;
line-height: .5333rem /* 40/75 */;
text-align: justify;
}
}
</style>
+720
View File
@@ -0,0 +1,720 @@
<template>
<div v-if="true" class='box'>
<div class="apply-box">
<img class="text" src="../assets/zhinengfangke.png" alt="">
<p>来访提前约 拜访开门无需按铃</p>
<div class="apply-content">
<h3>
<img src="../assets/icon-shenfenzheng.png" alt="">
<span>拜访信息<span>(我找谁)</span></span>
</h3>
<div class="apply-content-form">
<!-- <Selectli label="被拜访对象" v-model="form.visitingTel" :columns="visitDict" :isMust="true" :isline="true" name="name" nameId="userId"/> -->
<!-- <div class="search-select-box">
<label class="label"><span>*&nbsp;</span>被拜访人</label>
<div class="search-select-input">
<input @click.stop="onClickVisitor" @input="onInputVisior" :value="visitorName" type="text" placeholder="可填写选择">
<div v-if="isSelect && visitDict.length" class="select-box">
<b></b>
<div class="select-sroll-box">
<div @click.self="onClickSelect(item)" v-for="item in visitDict" :class="item.userId == form.visitingTel ? 'select-active' : ''" class="select-li" :key="item.userId">{{ item.name }}</div>
</div>
</div>
<div v-if="isSelect && !visitDict.length" class="select-box">
<b></b>
<div class="select-li" >查无此人</div>
</div>
</div>
</div> -->
<Inputli v-if="telName" v-model="telName" @input="onInputName" label="被访人" :isMust="true" :isline="true" isMustPosition="left"/>
<Inputli v-else v-model="form.visitingTel" @blur="onBlurTel" @input="onInputTel" label="被访人电话" :isMust="true" :isline="true" isMustPosition="left"/>
<Selectdate v-model="form.hours" label="来访时间" :isMust="true" :isline="false" :minDate="minDate"/>
</div>
</div>
<img class="absolute-img" src="../assets/woyaoyuyue.png" alt="">
</div>
<p class="tips">
说明拜访时间前后一小时都可刷脸通过门禁
</p>
<div class="visitor-content">
<h3>
<img src="../assets/icon-shenfenzheng.png" alt="">
<span>访客信息<span>(我是谁)</span></span>
</h3>
<div class="visitor-content-form">
<Inputli v-model="form.name" label="访客姓名" :isMust="true" :isline="true" isMustPosition="left"/>
<Inputli v-model="form.phone" label="联系方式" :isMust="true" :isline="true" isMustPosition="left"/>
<Inputli v-model="form.visitingNum" label="随行人数" :isMust="true" :isline="true" isMustPosition="left"/>
<Inputli v-model="form.reason" label="来访事由" :isMust="true" :isline="true" isMustPosition="left"/>
<div class="upload-box">
<label for=""><span>*&nbsp;</span>人脸照片</label>
<div v-if="!form.img" class="upload-button">
<img @click="onClickUpload" src="../assets/xiangji.png" alt="">
<input v-if="isDisabled" @change="onChangeFile" type="file" accept="image/*">
</div>
<div v-else class="face-img">
<img :src="form.img" alt="">
<input v-if="true" @change="onChangeFile" type="file" accept="image/*">
</div>
</div>
</div>
</div>
<p class="tips">
说明人脸照片用于通过门禁系统请上传正脸无遮挡照片
</p>
<p class="tel-tips">
如果您在使用中遇到问题请联系<span @click="onClickCall">400040-1573</span>
</p>
<!-- <div class="disclaimer-box">
<input type="checkbox">
<p>勾选表示同意<span>用户协议</span></p>
</div> -->
<button @click="onClcikSubmit" class="submit-button " :class="isSubmit ? 'active-button' : ''">提交</button>
<Successmodel v-if="isSuccess" @click="onClickAffirm" />
<div v-if="false" class="disclaimer-model-fixed">
<h3>用户协议</h3>
<div class="disclaimer-model">
阿哈撒发哈师范哈撒发哈很舒服哈哈
阿哈撒发哈师范哈撒发哈很舒服哈哈
阿哈撒发哈师范哈撒发哈很舒服哈哈
阿哈撒发哈师范哈撒发哈很舒服哈哈
阿哈撒发哈师范哈撒发哈很舒服哈哈
阿哈撒发哈师范哈撒发哈很舒服哈哈
阿哈撒发哈师范哈撒发哈很舒服哈哈
阿哈撒发哈师范哈撒发哈很舒服哈哈
阿哈撒发哈师范哈撒发哈很舒服哈哈
阿哈撒发哈师范哈撒发哈很舒服哈哈
阿哈撒发哈师范哈撒发哈很舒服哈哈
</div>
</div>
</div>
</template>
<script setup>
import { reactive, ref, watch } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { useStore } from 'vuex'
import { Toast, ImagePreview , Uploader, Dialog } from 'vant';
import { rulesForm, resetForm, rulesPhone, rulesFormFBoolean, formatDate } from '@/com/index'
import Inputli from '@/components/input-li.vue'
import Selectli from '@/components/select-li.vue'
import Selectdate from '@/components/select-li-date.vue'
import Successmodel from '../components/success.vue'
import { postApply, getVisitor, getUserNameByTel } from '@/api/user.js'
import { uploadApi } from '@/api/upload'
/* @data */
const router = useRouter()
const route = useRoute()
const store = useStore()
const phoneReg = /(^1\d{10}$)|(^[0-9]\d{7}$)/;
let form = reactive({
name: "",
phone: "",
hours: "",
reason: "",
img: "",
visitingTel: "",
visitingNum: '',
})
const isSubmit = ref(false)
const visitDict = ref([]) // 被访客数据
const visitAllDict = ref([]) // 被访客数据
const isDisabled = ref(false) // 是否能提交
const isSuccess = ref(false) // 是否提交成功
const isSelect = ref( false) // 是否显示下拉选择
const visitorName = ref( '') // 是否显示下拉选择的名字
const telName = ref( '') // 电话查询姓名
/* watch */
watch(() => form, (v1, v2) => {
isSubmit.value = rulesFormFBoolean(v2)
if(v2.name && v2.phone){
if(!phoneReg.test(v2.phone)){
isDisabled.value = false
}else{
isDisabled.value = true
}
}else{
isDisabled.value = false
}
},{ deep: true }
) // 深度监听 ) // 监听单个
/* @method */
const onInputTel = (e) =>{
}
const onInputName = (e) =>{
form.visitingTel = ''
telName.value = ''
}
const onBlurTel = (phone) =>{
console.log(phone)
if(rulesPhone(phone)){
getUserNameByTel(phone).then(res =>{
console.log(res)
if(res.data){
telName.value = res.data
}
}).catch(err =>{
console.log(err)
Dialog.alert({
message: err.msg,
}).then(() => {
// on close
});
})
}
}
// const onBlurVisitor = () =>{
// isSelect.value = false
// let item = visitAllDict.value.find(item =>{ return item.userId == form.visitingTel })
// if(item){
// visitorName.value = item.name
// visitDict.value = visitAllDict.value
// }else{
// visitorName.value = '' // 是否显示下拉选择的名字
// visitDict.value = visitAllDict.value
// }
// }
// 输入框
// const onInputVisior = (e) =>{
// visitorName.value = e.target.value
// let arr = []
// visitAllDict.value.map( item =>{
// if(item.name.search(e.target.value) != -1){
// arr.push(item)
// }
// })
// visitDict.value = [ ...arr ]
// }
// 点击选择了拜访对象
// const onClickSelect = (item) =>{
// form.visitingTel = item.userId
// visitorName.value = item.name
// isSelect.value = false
// }
// const onClickVisitor = () =>{
// isSelect.value = true
// }
// 点击确认模态框
const onClickAffirm = () =>{
router.go(-1)
}
const onClickUpload = () =>{
if(isDisabled.value){
}else{
Toast.fail('请填写访客姓名和正确的联系方式!')
}
}
const onClcikSubmit = () =>{
if(isSubmit.value){
if(rulesPhone(form.phone) && rulesPhone(form.visitingTel)){
Toast.loading({ duration: 0, forbidClick: true, message: '提交中', });
postApply(form).then(res =>{
Toast.clear();
isSuccess.value = true
})
}
}else{
Toast.fail('请先填写完整信息后提交!')
}
}
// 上传照片
const onChangeFile = (e) =>{
console.log(e.target.files[0].size)
if(e.target.files[0]){
newcompressImg(e.target.files[0], 0.5).then(res =>{
let formData = new FormData()
// formData.append('file', e.target.files[0])
formData.append('file', res.file)
formData.append('userId', form.phone)
formData.append('name', form.name)
Toast.loading({ duration: 0, forbidClick: true, message: '上传中', });
uploadApi(formData).then(res =>{
Toast.success(res.msg);
form.img = res.data
})
}).catch(err =>{
Toast.fail('图片上传失败,请重新上传')
})
}
}
const onClickCall = () =>{
window.location.href = 'tel:400-040-1573'
}
const newcompressImg = (file, quality) =>{
var qualitys = 0.52
if (parseInt((file.size / 1024).toFixed(2)) < 1024) {
qualitys = 0.85
}
if (5 * 1024 < parseInt((file.size / 1024).toFixed(2))) {
qualitys = 0.92
}
if (quality) {
qualitys = quality
}
if (file[0]) {
return Promise.all(Array.from(file).map(e => this.compressImg(e,
qualitys))) // 如果是 file 数组返回 Promise 数组
} else {
return new Promise((resolve) => {
if ((file.size / 1024).toFixed(2) < 300) {
resolve({
file: file
})
} else {
const reader = new FileReader() // 创建 FileReader
reader.onload = ({
target: {
result: src
}
}) => {
const image = new Image() // 创建 img 元素
image.onload = async() => {
const canvas = document.createElement('canvas') // 创建 canvas 元素
const context = canvas.getContext('2d')
var targetWidth = image.width
var targetHeight = image.height
var originWidth = image.width
var originHeight = image.height
if (1 * 1024 <= parseInt((file.size / 1024).toFixed(2)) && parseInt((file.size / 1024).toFixed(2)) <= 10 * 1024) {
var maxWidth = 1600
var maxHeight = 1600
targetWidth = originWidth
targetHeight = originHeight
// 图片尺寸超过的限制
if (originWidth > maxWidth || originHeight > maxHeight) {
if (originWidth / originHeight > maxWidth / maxHeight) {
// 更宽,按照宽度限定尺寸
targetWidth = maxWidth
targetHeight = Math.round(maxWidth * (originHeight / originWidth))
} else {
targetHeight = maxHeight
targetWidth = Math.round(maxHeight * (originWidth / originHeight))
}
}
}
if (10 * 1024 <= parseInt((file.size / 1024).toFixed(2)) && parseInt((file.size / 1024).toFixed(2)) <= 20 * 1024) {
maxWidth = 1400
maxHeight = 1400
targetWidth = originWidth
targetHeight = originHeight
// 图片尺寸超过的限制
if (originWidth > maxWidth || originHeight > maxHeight) {
if (originWidth / originHeight > maxWidth / maxHeight) {
// 更宽,按照宽度限定尺寸
targetWidth = maxWidth
targetHeight = Math.round(maxWidth * (originHeight / originWidth))
} else {
targetHeight = maxHeight
targetWidth = Math.round(maxHeight * (originWidth / originHeight))
}
}
}
canvas.width = targetWidth
canvas.height = targetHeight
context.clearRect(0, 0, targetWidth, targetHeight)
context.drawImage(image, 0, 0, targetWidth, targetHeight) // 绘制 canvas
const canvasURL = canvas.toDataURL('image/jpeg', qualitys)
const buffer = atob(canvasURL.split(',')[1])
let length = buffer.length
const bufferArray = new Uint8Array(new ArrayBuffer(length))
while (length--) {
bufferArray[length] = buffer.charCodeAt(length)
}
const miniFile = new File([bufferArray], file.name, {
type: 'image/jpeg'
})
resolve({
file: miniFile,
origin: file,
beforeSrc: src,
afterSrc: canvasURL,
beforeKB: Number((file.size / 1024).toFixed(2)),
afterKB: Number((miniFile.size / 1024).toFixed(2))
})
}
image.src = src
}
reader.readAsDataURL(file)
}
})
}
}
/* @setup */
const minDate = ref( formatDate(new Date()))
console.log(route.query.tel)
if(route.query.tel){
form.visitingTel = route.query.tel
onBlurTel(route.query.tel)
}
getVisitor().then( res=>{
visitDict.value = res.data
visitAllDict.value = res.data
})
</script>
<style lang='scss' scoped>
.box{
width: 100%;
height: 100%;
background: #F5F5F5;
padding: 0 0 1.08rem /* 81/75 */;
box-sizing: border-box;
overflow: auto;
.apply-box{
position: relative;
width: 100%;
height: 8.0267rem /* 602/75 */;
background: url('../assets/yuyuebeijing.png') left top/ 100% 100% no-repeat;
padding: 1.04rem /* 78/75 */ 0 0;
box-sizing: border-box;
.text{
width: 3.5467rem /* 266/75 */;
height: 1.1467rem /* 86/75 */;
margin-left: .6133rem /* 46/75 */;
}
p{
line-height: .48rem /* 36/75 */;
font-size: $sm-size;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #999999;
padding: .1067rem /* 8/75 */ 0 0 .7467rem /* 56/75 */;
}
.apply-content{
width: 9.2rem /* 690/75 */;
height: 4.32rem /* 324/75 */;
background: #FFFFFF;
border-radius: .2667rem /* 20/75 */;
margin: 1.0133rem /* 76/75 */ auto 0;
h3{
padding: .5067rem /* 38/75 */ .3467rem /* 26/75 */ 0 .4533rem /* 34/75 */;
box-sizing: border-box;
display: flex;
align-items: center;
color: #222222;
font-size: $size30;
img{
width: .5867rem /* 44/75 */;
height: .5333rem /* 40/75 */;
margin-right: .16rem /* 12/75 */;
}
span{
span{
font-size: .32rem /* 24/75 */;
color: #666;
}
}
}
.apply-content-form{
width: 100%;
padding: 0 .3467rem /* 26/75 */ 0 .4533rem /* 34/75 */;
box-sizing: border-box;
.search-select-box{
position: relative;
width: 100%;
height: 1.5733rem /* 118/75 */;
// background: #F9F9F9;
// border-radius: .2667rem /* 20/75 */;
padding: 0 .0267rem /* 2/75 */ 0;
box-sizing: border-box;
display: flex;
// margin-bottom: .2667rem /* 20/75 */;
border-bottom: 0.0267rem dashed #D9D9D9;
.label{
flex: 1;
font-size: $size26;
color: #333333;
line-height: 1.5733rem /* 118/75 */;
span{
color: #FF0000;
margin-right: .0267rem /* 2/75 */;
}
}
.search-select-input{
position: relative;
height: 100%;
box-sizing: border-box;
input{
flex: 1;
border: none;
outline: none;
text-align: right;
font-size: $size26;
color: #333333;
// background: #F9F9F9;
padding-right: 0.3733rem;
// line-height: 1.5733rem /* 118/75 */;
box-sizing: border-box;
height: 100%;
}
input::-webkit-input-placeholder {
font-size: $size26;
color: #999999;
}
.select-box{
position: absolute;
right: 0;
top: 1.8667rem /* 140/75 */;
z-index: 5;
width: 3.7333rem /* 280/75 */;
// max-height: 5.0667rem /* 380/75 */;
background: #FFFFFF;
box-shadow: 0px 0px .2667rem /* 20/75 */ .1067rem /* 8/75 */ rgba(0,0,0,0.1);
border-radius: .0533rem /* 4/75 */ ;
// overflow: auto;
.select-sroll-box{
// position: absolute;
// right: 0;
// top: 1.8667rem /* 140/75 */;
// z-index: 5;
width: 100%;
max-height: 5.0667rem /* 380/75 */;
background: #FFFFFF;
// box-shadow: 0px 0px .2667rem /* 20/75 */ .1067rem /* 8/75 */ rgba(0,0,0,0.1);
// border-radius: .0533rem /* 4/75 */ ;
overflow: auto;
}
.select-li{
height: 1.0133rem /* 76/75 */;
background: #fff;
border-radius: .0533rem /* 4/75 */ .0533rem /* 4/75 */ 0px 0px;
font-size: $size26;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #999999;
line-height: 1.0133rem /* 76/75 */;
text-align: center;
}
.select-active{
color: #30BF78;
background: #F3FEFB;
}
}
b{
position: absolute;
left: 50%;
// top: -100%;
// width: .2rem /* 15/75 */;
// height: 20px;
// background: red;
// display: block;
width: 0;
height: 0;
border: .16rem /* 12/75 */ solid transparent;
border-bottom: .16rem /* 12/75 */ solid #fff;
transform: translateY(-100%) translateX(-50%);
// margin-top: -.6667rem /* 50/75 */;
}
}
}
}
}
.absolute-img{
position: absolute;
top: .5333rem /* 40/75 */;
right: .4rem /* 30/75 */;
width: 4.5067rem /* 338/75 */;
height: 3.7867rem /* 284/75 */;
}
}
.tips{
line-height: .4533rem /* 34/75 */;
font-size: .2933rem /* 22/75 */;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #999999;
padding: .2133rem /* 16/75 */ 0 .4267rem /* 32/75 */ .8rem /* 60/75 */;
box-sizing: border-box;
}
.visitor-content{
width: 9.2rem /* 690/75 */;
// height: 4.32rem /* 324/75 */;
background: #FFFFFF;
border-radius: .2667rem /* 20/75 */;
margin: 0 auto 0;
h3{
padding: .5067rem /* 38/75 */ .3467rem /* 26/75 */ 0 .4533rem /* 34/75 */;
box-sizing: border-box;
display: flex;
align-items: center;
color: #222222;
font-size: $size30;
img{
width: .5867rem /* 44/75 */;
height: .5333rem /* 40/75 */;
margin-right: .16rem /* 12/75 */;
}
span{
span{
font-size: .32rem /* 24/75 */;
color: #666;
}
}
}
.visitor-content-form{
width: 100%;
padding: 0 .3467rem /* 26/75 */ .16rem /* 12/75 */ .4533rem /* 34/75 */;
box-sizing: border-box;
.upload-box{
width: 100%;
height: 1.9733rem /* 148/75 */;
// padding: 0 .6933rem /* 52/75 */ 0 0;
padding-right: .3733rem /* 28/75 */;
box-sizing: border-box;
display: flex;
justify-content: space-between;
align-items: center;
color: #333333;
font-size: $size26;
label{
line-height: 1.9733rem /* 148/75 */;
span{
color: #FF0000;
}
}
.upload-button{
position: relative;
width: 1.3333rem /* 100/75 */;
height: 1.3333rem /* 100/75 */;
background: #FFFFFF;
border-radius: .1333rem /* 10/75 */;
border: .0267rem /* 2/75 */ dashed #D9D9D9;
display: flex;
align-items: center;
justify-content: center;
// overflow: hidden;
input{
position: absolute;
left: 0;
top: 0;
width: 1.3333rem /* 100/75 */;
height: 1.3333rem /* 100/75 */;
opacity: 0;
}
}
.face-img{
position: relative;
width: 1.3333rem /* 100/75 */;
height: 1.3333rem /* 100/75 */;
background: #FFFFFF;
border-radius: .1333rem /* 10/75 */;
img{
width: 100%;
height: 100%;
border-radius: .1333rem /* 10/75 */;
}
input{
position: absolute;
left: 0;
top: 0;
width: 1.3333rem /* 100/75 */;
height: 1.3333rem /* 100/75 */;
opacity: 0;
}
}
}
}
}
.disclaimer-box{
display: flex;
justify-content: center;
input{
margin-right: .1333rem /* 10/75 */;
}
span{
color: #30BF78;
}
}
.tel-tips{
line-height: .4533rem /* 34/75 */;
font-size: .2933rem /* 22/75 */;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #999999;
padding: 0 0 .4267rem /* 32/75 */ .8rem /* 60/75 */;
box-sizing: border-box;
margin-top: -.24rem /* 18/75 */;
span{
color: #409eff;
}
}
.submit-button{
display: block;
width: 6.72rem /* 504/75 */;
height: 1.12rem /* 84/75 */;
background: #D9D9D9;
border-radius: 1.3333rem /* 100/75 */ ;
opacity: 1;
font-size: $size30;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 500;
color: #222222;
border: none;
outline: none;
margin: 1.0133rem /* 76/75 */ auto 0;
}
.active-button{
background-color: #30BF78;
color: #fff;
}
}
.disclaimer-model-fixed{
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: #fff;
display: flex;
flex-direction: column;
padding: .4rem /* 30/75 */;
box-sizing: border-box;
h3{
font-size: .4267rem /* 32/75 */;
line-height: .8rem /* 60/75 */;
text-align: center;
}
.disclaimer-model{
flex: 1;
line-height: .5333rem /* 40/75 */;
text-align: justify;
}
}
</style>
+721
View File
@@ -0,0 +1,721 @@
<template>
<div v-if="true" class='box'>
<div class="apply-box">
<img class="text" src="../assets/zhinengfangke.png" alt="">
<p>来访提前约 拜访开门无需按铃</p>
<div class="apply-content">
<h3>
<img src="../assets/icon-shenfenzheng.png" alt="">
<span>拜访信息<span>(我找谁)</span></span>
</h3>
<div class="apply-content-form">
<!-- <Selectli label="被拜访对象" v-model="form.visitingTel" :columns="visitDict" :isMust="true" :isline="true" name="name" nameId="userId"/> -->
<!-- <div class="search-select-box">
<label class="label"><span>*&nbsp;</span>被拜访人</label>
<div class="search-select-input">
<input @click.stop="onClickVisitor" @input="onInputVisior" :value="visitorName" type="text" placeholder="可填写选择">
<div v-if="isSelect && visitDict.length" class="select-box">
<b></b>
<div class="select-sroll-box">
<div @click.self="onClickSelect(item)" v-for="item in visitDict" :class="item.userId == form.visitingTel ? 'select-active' : ''" class="select-li" :key="item.userId">{{ item.name }}</div>
</div>
</div>
<div v-if="isSelect && !visitDict.length" class="select-box">
<b></b>
<div class="select-li" >查无此人</div>
</div>
</div>
</div> -->
<Inputli v-if="telName" v-model="telName" @input="onInputName" label="被访人" :isMust="true" :isline="true" isMustPosition="left"/>
<Inputli v-else v-model="form.visitingTel" @blur="onBlurTel" @input="onInputTel" label="被访人电话" :isMust="true" :isline="true" isMustPosition="left"/>
<Selectdate v-model="form.hours" label="来访时间" :isMust="true" :isline="false" :minDate="minDate"/>
</div>
</div>
<img class="absolute-img" src="../assets/woyaoyuyue.png" alt="">
</div>
<p class="tips">
说明拜访时间前后一小时都可刷脸通过门禁
</p>
<div class="visitor-content">
<h3>
<img src="../assets/icon-shenfenzheng.png" alt="">
<span>访客信息<span>(我是谁)</span></span>
</h3>
<div class="visitor-content-form">
<Inputli v-model="form.name" label="访客姓名" :isMust="true" :isline="true" isMustPosition="left"/>
<Inputli v-model="form.phone" label="联系方式" :isMust="true" :isline="true" isMustPosition="left"/>
<Inputli v-model="form.visitingNum" label="随行人数" :isMust="true" :isline="true" isMustPosition="left"/>
<Inputli v-model="form.reason" label="来访事由" :isMust="true" :isline="true" isMustPosition="left"/>
<div class="upload-box">
<label for=""><span>*&nbsp;</span>人脸照片</label>
<div v-if="!form.img" class="upload-button">
<img @click="onClickUpload" src="../assets/xiangji.png" alt="">
<input v-if="isDisabled" @change="onChangeFile" type="file" accept="image/*">
</div>
<div v-else class="face-img">
<img :src="form.img" alt="">
<input v-if="true" @change="onChangeFile" type="file" accept="image/*">
</div>
</div>
</div>
</div>
<p class="tips">
说明人脸照片用于通过门禁系统请上传正脸无遮挡照片
</p>
<p class="tel-tips">
如果您在使用中遇到问题请联系<span @click="onClickCall">400040-1573</span>
</p>
<!-- <div class="disclaimer-box">
<input type="checkbox">
<p>勾选表示同意<span>用户协议</span></p>
</div> -->
<button @click="onClcikSubmit" class="submit-button " :class="isSubmit ? 'active-button' : ''">提交</button>
<Successmodel v-if="isSuccess" @click="onClickAffirm" />
<div v-if="false" class="disclaimer-model-fixed">
<h3>用户协议</h3>
<div class="disclaimer-model">
阿哈撒发哈师范哈撒发哈很舒服哈哈
阿哈撒发哈师范哈撒发哈很舒服哈哈
阿哈撒发哈师范哈撒发哈很舒服哈哈
阿哈撒发哈师范哈撒发哈很舒服哈哈
阿哈撒发哈师范哈撒发哈很舒服哈哈
阿哈撒发哈师范哈撒发哈很舒服哈哈
阿哈撒发哈师范哈撒发哈很舒服哈哈
阿哈撒发哈师范哈撒发哈很舒服哈哈
阿哈撒发哈师范哈撒发哈很舒服哈哈
阿哈撒发哈师范哈撒发哈很舒服哈哈
阿哈撒发哈师范哈撒发哈很舒服哈哈
</div>
</div>
</div>
</template>
<script setup>
import { reactive, ref, watch } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { useStore } from 'vuex'
import { Toast, ImagePreview , Uploader, Dialog } from 'vant';
import { rulesForm, resetForm, rulesPhone, rulesFormFBoolean, formatDate } from '@/com/index'
import Inputli from '@/components/input-li.vue'
import Selectli from '@/components/select-li.vue'
import Selectdate from '@/components/select-li-date.vue'
import Successmodel from '../components/success.vue'
import { postApply, getVisitor, getUserNameByTel } from '@/api/user.js'
import { uploadApi } from '@/api/upload'
/* @data */
const router = useRouter()
const route = useRoute()
const store = useStore()
const phoneReg = /(^1\d{10}$)|(^[0-9]\d{7}$)/;
let form = reactive({
name: "",
phone: "",
hours: "",
reason: "",
img: "",
visitingTel: "",
visitingNum: '',
})
const isSubmit = ref(false)
const visitDict = ref([]) // 被访客数据
const visitAllDict = ref([]) // 被访客数据
const isDisabled = ref(false) // 是否能提交
const isSuccess = ref(false) // 是否提交成功
const isSelect = ref( false) // 是否显示下拉选择
const visitorName = ref( '') // 是否显示下拉选择的名字
const telName = ref( '') // 电话查询姓名
/* watch */
watch(() => form, (v1, v2) => {
isSubmit.value = rulesFormFBoolean(v2)
if(v2.name && v2.phone){
if(!phoneReg.test(v2.phone)){
isDisabled.value = false
}else{
isDisabled.value = true
}
}else{
isDisabled.value = false
}
},{ deep: true }
) // 深度监听 ) // 监听单个
/* @method */
const onInputTel = (e) =>{
}
const onInputName = (e) =>{
form.visitingTel = ''
telName.value = ''
}
const onBlurTel = (phone) =>{
console.log(phone)
if(rulesPhone(phone)){
getUserNameByTel(phone).then(res =>{
console.log(res)
if(res.data){
telName.value = res.data
}
}).catch(err =>{
Toast.clear();
console.log(err)
Dialog.alert({
message: err.msg,
}).then(() => {
// on close
});
})
}
}
// const onBlurVisitor = () =>{
// isSelect.value = false
// let item = visitAllDict.value.find(item =>{ return item.userId == form.visitingTel })
// if(item){
// visitorName.value = item.name
// visitDict.value = visitAllDict.value
// }else{
// visitorName.value = '' // 是否显示下拉选择的名字
// visitDict.value = visitAllDict.value
// }
// }
// 输入框
// const onInputVisior = (e) =>{
// visitorName.value = e.target.value
// let arr = []
// visitAllDict.value.map( item =>{
// if(item.name.search(e.target.value) != -1){
// arr.push(item)
// }
// })
// visitDict.value = [ ...arr ]
// }
// 点击选择了拜访对象
// const onClickSelect = (item) =>{
// form.visitingTel = item.userId
// visitorName.value = item.name
// isSelect.value = false
// }
// const onClickVisitor = () =>{
// isSelect.value = true
// }
// 点击确认模态框
const onClickAffirm = () =>{
router.go(-1)
}
const onClickUpload = () =>{
if(isDisabled.value){
}else{
Toast.fail('请填写访客姓名和正确的联系方式!')
}
}
const onClcikSubmit = () =>{
if(isSubmit.value){
if(rulesPhone(form.phone) && rulesPhone(form.visitingTel)){
Toast.loading({ duration: 0, forbidClick: true, message: '提交中', });
postApply(form).then(res =>{
Toast.clear();
isSuccess.value = true
})
}
}else{
Toast.fail('请先填写完整信息后提交!')
}
}
// 上传照片
const onChangeFile = (e) =>{
console.log(e.target.files[0].size)
if(e.target.files[0]){
newcompressImg(e.target.files[0], 0.5).then(res =>{
let formData = new FormData()
// formData.append('file', e.target.files[0])
formData.append('file', res.file)
formData.append('userId', form.phone)
formData.append('name', form.name)
Toast.loading({ duration: 0, forbidClick: true, message: '上传中', });
uploadApi(formData).then(res =>{
Toast.success(res.msg);
form.img = res.data
})
}).catch(err =>{
Toast.fail('图片上传失败,请重新上传')
})
}
}
const onClickCall = () =>{
window.location.href = 'tel:400-040-1573'
}
const newcompressImg = (file, quality) =>{
var qualitys = 0.52
if (parseInt((file.size / 1024).toFixed(2)) < 1024) {
qualitys = 0.85
}
if (5 * 1024 < parseInt((file.size / 1024).toFixed(2))) {
qualitys = 0.92
}
if (quality) {
qualitys = quality
}
if (file[0]) {
return Promise.all(Array.from(file).map(e => this.compressImg(e,
qualitys))) // 如果是 file 数组返回 Promise 数组
} else {
return new Promise((resolve) => {
if ((file.size / 1024).toFixed(2) < 300) {
resolve({
file: file
})
} else {
const reader = new FileReader() // 创建 FileReader
reader.onload = ({
target: {
result: src
}
}) => {
const image = new Image() // 创建 img 元素
image.onload = async() => {
const canvas = document.createElement('canvas') // 创建 canvas 元素
const context = canvas.getContext('2d')
var targetWidth = image.width
var targetHeight = image.height
var originWidth = image.width
var originHeight = image.height
if (1 * 1024 <= parseInt((file.size / 1024).toFixed(2)) && parseInt((file.size / 1024).toFixed(2)) <= 10 * 1024) {
var maxWidth = 1600
var maxHeight = 1600
targetWidth = originWidth
targetHeight = originHeight
// 图片尺寸超过的限制
if (originWidth > maxWidth || originHeight > maxHeight) {
if (originWidth / originHeight > maxWidth / maxHeight) {
// 更宽,按照宽度限定尺寸
targetWidth = maxWidth
targetHeight = Math.round(maxWidth * (originHeight / originWidth))
} else {
targetHeight = maxHeight
targetWidth = Math.round(maxHeight * (originWidth / originHeight))
}
}
}
if (10 * 1024 <= parseInt((file.size / 1024).toFixed(2)) && parseInt((file.size / 1024).toFixed(2)) <= 20 * 1024) {
maxWidth = 1400
maxHeight = 1400
targetWidth = originWidth
targetHeight = originHeight
// 图片尺寸超过的限制
if (originWidth > maxWidth || originHeight > maxHeight) {
if (originWidth / originHeight > maxWidth / maxHeight) {
// 更宽,按照宽度限定尺寸
targetWidth = maxWidth
targetHeight = Math.round(maxWidth * (originHeight / originWidth))
} else {
targetHeight = maxHeight
targetWidth = Math.round(maxHeight * (originWidth / originHeight))
}
}
}
canvas.width = targetWidth
canvas.height = targetHeight
context.clearRect(0, 0, targetWidth, targetHeight)
context.drawImage(image, 0, 0, targetWidth, targetHeight) // 绘制 canvas
const canvasURL = canvas.toDataURL('image/jpeg', qualitys)
const buffer = atob(canvasURL.split(',')[1])
let length = buffer.length
const bufferArray = new Uint8Array(new ArrayBuffer(length))
while (length--) {
bufferArray[length] = buffer.charCodeAt(length)
}
const miniFile = new File([bufferArray], file.name, {
type: 'image/jpeg'
})
resolve({
file: miniFile,
origin: file,
beforeSrc: src,
afterSrc: canvasURL,
beforeKB: Number((file.size / 1024).toFixed(2)),
afterKB: Number((miniFile.size / 1024).toFixed(2))
})
}
image.src = src
}
reader.readAsDataURL(file)
}
})
}
}
/* @setup */
const minDate = ref( formatDate(new Date()))
console.log(route.query.tel)
if(route.query.tel){
form.visitingTel = route.query.tel
onBlurTel(route.query.tel)
}
getVisitor().then( res=>{
visitDict.value = res.data
visitAllDict.value = res.data
})
</script>
<style lang='scss' scoped>
.box{
width: 100%;
height: 100%;
background: #F5F5F5;
padding: 0 0 1.08rem /* 81/75 */;
box-sizing: border-box;
overflow: auto;
.apply-box{
position: relative;
width: 100%;
height: 8.0267rem /* 602/75 */;
background: url('../assets/yuyuebeijing.png') left top/ 100% 100% no-repeat;
padding: 1.04rem /* 78/75 */ 0 0;
box-sizing: border-box;
.text{
width: 3.5467rem /* 266/75 */;
height: 1.1467rem /* 86/75 */;
margin-left: .6133rem /* 46/75 */;
}
p{
line-height: .48rem /* 36/75 */;
font-size: $sm-size;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #999999;
padding: .1067rem /* 8/75 */ 0 0 .7467rem /* 56/75 */;
}
.apply-content{
width: 9.2rem /* 690/75 */;
height: 4.32rem /* 324/75 */;
background: #FFFFFF;
border-radius: .2667rem /* 20/75 */;
margin: 1.0133rem /* 76/75 */ auto 0;
h3{
padding: .5067rem /* 38/75 */ .3467rem /* 26/75 */ 0 .4533rem /* 34/75 */;
box-sizing: border-box;
display: flex;
align-items: center;
color: #222222;
font-size: $size30;
img{
width: .5867rem /* 44/75 */;
height: .5333rem /* 40/75 */;
margin-right: .16rem /* 12/75 */;
}
span{
span{
font-size: .32rem /* 24/75 */;
color: #666;
}
}
}
.apply-content-form{
width: 100%;
padding: 0 .3467rem /* 26/75 */ 0 .4533rem /* 34/75 */;
box-sizing: border-box;
.search-select-box{
position: relative;
width: 100%;
height: 1.5733rem /* 118/75 */;
// background: #F9F9F9;
// border-radius: .2667rem /* 20/75 */;
padding: 0 .0267rem /* 2/75 */ 0;
box-sizing: border-box;
display: flex;
// margin-bottom: .2667rem /* 20/75 */;
border-bottom: 0.0267rem dashed #D9D9D9;
.label{
flex: 1;
font-size: $size26;
color: #333333;
line-height: 1.5733rem /* 118/75 */;
span{
color: #FF0000;
margin-right: .0267rem /* 2/75 */;
}
}
.search-select-input{
position: relative;
height: 100%;
box-sizing: border-box;
input{
flex: 1;
border: none;
outline: none;
text-align: right;
font-size: $size26;
color: #333333;
// background: #F9F9F9;
padding-right: 0.3733rem;
// line-height: 1.5733rem /* 118/75 */;
box-sizing: border-box;
height: 100%;
}
input::-webkit-input-placeholder {
font-size: $size26;
color: #999999;
}
.select-box{
position: absolute;
right: 0;
top: 1.8667rem /* 140/75 */;
z-index: 5;
width: 3.7333rem /* 280/75 */;
// max-height: 5.0667rem /* 380/75 */;
background: #FFFFFF;
box-shadow: 0px 0px .2667rem /* 20/75 */ .1067rem /* 8/75 */ rgba(0,0,0,0.1);
border-radius: .0533rem /* 4/75 */ ;
// overflow: auto;
.select-sroll-box{
// position: absolute;
// right: 0;
// top: 1.8667rem /* 140/75 */;
// z-index: 5;
width: 100%;
max-height: 5.0667rem /* 380/75 */;
background: #FFFFFF;
// box-shadow: 0px 0px .2667rem /* 20/75 */ .1067rem /* 8/75 */ rgba(0,0,0,0.1);
// border-radius: .0533rem /* 4/75 */ ;
overflow: auto;
}
.select-li{
height: 1.0133rem /* 76/75 */;
background: #fff;
border-radius: .0533rem /* 4/75 */ .0533rem /* 4/75 */ 0px 0px;
font-size: $size26;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #999999;
line-height: 1.0133rem /* 76/75 */;
text-align: center;
}
.select-active{
color: #30BF78;
background: #F3FEFB;
}
}
b{
position: absolute;
left: 50%;
// top: -100%;
// width: .2rem /* 15/75 */;
// height: 20px;
// background: red;
// display: block;
width: 0;
height: 0;
border: .16rem /* 12/75 */ solid transparent;
border-bottom: .16rem /* 12/75 */ solid #fff;
transform: translateY(-100%) translateX(-50%);
// margin-top: -.6667rem /* 50/75 */;
}
}
}
}
}
.absolute-img{
position: absolute;
top: .5333rem /* 40/75 */;
right: .4rem /* 30/75 */;
width: 4.5067rem /* 338/75 */;
height: 3.7867rem /* 284/75 */;
}
}
.tips{
line-height: .4533rem /* 34/75 */;
font-size: .2933rem /* 22/75 */;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #999999;
padding: .2133rem /* 16/75 */ 0 .4267rem /* 32/75 */ .8rem /* 60/75 */;
box-sizing: border-box;
}
.visitor-content{
width: 9.2rem /* 690/75 */;
// height: 4.32rem /* 324/75 */;
background: #FFFFFF;
border-radius: .2667rem /* 20/75 */;
margin: 0 auto 0;
h3{
padding: .5067rem /* 38/75 */ .3467rem /* 26/75 */ 0 .4533rem /* 34/75 */;
box-sizing: border-box;
display: flex;
align-items: center;
color: #222222;
font-size: $size30;
img{
width: .5867rem /* 44/75 */;
height: .5333rem /* 40/75 */;
margin-right: .16rem /* 12/75 */;
}
span{
span{
font-size: .32rem /* 24/75 */;
color: #666;
}
}
}
.visitor-content-form{
width: 100%;
padding: 0 .3467rem /* 26/75 */ .16rem /* 12/75 */ .4533rem /* 34/75 */;
box-sizing: border-box;
.upload-box{
width: 100%;
height: 1.9733rem /* 148/75 */;
// padding: 0 .6933rem /* 52/75 */ 0 0;
padding-right: .3733rem /* 28/75 */;
box-sizing: border-box;
display: flex;
justify-content: space-between;
align-items: center;
color: #333333;
font-size: $size26;
label{
line-height: 1.9733rem /* 148/75 */;
span{
color: #FF0000;
}
}
.upload-button{
position: relative;
width: 1.3333rem /* 100/75 */;
height: 1.3333rem /* 100/75 */;
background: #FFFFFF;
border-radius: .1333rem /* 10/75 */;
border: .0267rem /* 2/75 */ dashed #D9D9D9;
display: flex;
align-items: center;
justify-content: center;
// overflow: hidden;
input{
position: absolute;
left: 0;
top: 0;
width: 1.3333rem /* 100/75 */;
height: 1.3333rem /* 100/75 */;
opacity: 0;
}
}
.face-img{
position: relative;
width: 1.3333rem /* 100/75 */;
height: 1.3333rem /* 100/75 */;
background: #FFFFFF;
border-radius: .1333rem /* 10/75 */;
img{
width: 100%;
height: 100%;
border-radius: .1333rem /* 10/75 */;
}
input{
position: absolute;
left: 0;
top: 0;
width: 1.3333rem /* 100/75 */;
height: 1.3333rem /* 100/75 */;
opacity: 0;
}
}
}
}
}
.disclaimer-box{
display: flex;
justify-content: center;
input{
margin-right: .1333rem /* 10/75 */;
}
span{
color: #30BF78;
}
}
.tel-tips{
line-height: .4533rem /* 34/75 */;
font-size: .2933rem /* 22/75 */;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #999999;
padding: 0 0 .4267rem /* 32/75 */ .8rem /* 60/75 */;
box-sizing: border-box;
margin-top: -.24rem /* 18/75 */;
span{
color: #409eff;
}
}
.submit-button{
display: block;
width: 6.72rem /* 504/75 */;
height: 1.12rem /* 84/75 */;
background: #D9D9D9;
border-radius: 1.3333rem /* 100/75 */ ;
opacity: 1;
font-size: $size30;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 500;
color: #222222;
border: none;
outline: none;
margin: 1.0133rem /* 76/75 */ auto 0;
}
.active-button{
background-color: #30BF78;
color: #fff;
}
}
.disclaimer-model-fixed{
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: #fff;
display: flex;
flex-direction: column;
padding: .4rem /* 30/75 */;
box-sizing: border-box;
h3{
font-size: .4267rem /* 32/75 */;
line-height: .8rem /* 60/75 */;
text-align: center;
}
.disclaimer-model{
flex: 1;
line-height: .5333rem /* 40/75 */;
text-align: justify;
}
}
</style>
+124
View File
@@ -0,0 +1,124 @@
<template>
<div class='box'>
<div class="content">
<img class="icon-h1" src="../assets/yuyuejilu.png" alt="">
<div class="title">请输入预约时填写的手机号</div>
<p>手机号用于查询所提交的预约记录</p>
<input @input="onInputPhone" v-model="phone" placeholder="输入手机号" :maxlength="11" />
<button @click="onClickQuery" :class=" phone.length >= 11 ? 'active-button' : '' " class="query-button">查询</button>
<!-- <p v-for="item in 90">54444444444444</p> -->
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import { useStore } from 'vuex'
import { rulesForm, resetForm, rulesPhone } from '@/com/index'
import { postApply, getVisitor, getByPhone } from '@/api/user.js'
import { Toast, ImagePreview } from 'vant';
/* @data */
const router = useRouter()
const store = useStore()
let phone = ref('')
/* @setup */
const getStorage = localStorage.getItem('iphone')
if(getStorage){
phone.value = getStorage
}
/* @method */
const onInputPhone = (e) =>{
// console.log(e.target.value.length)
}
const onClickQuery = () =>{
if(phone.value.length >= 11){
if( rulesPhone(phone.value)){
console.log('可以提交')
localStorage.setItem('iphone', phone.value)
let obj = { phone: phone.value }
Toast.loading({ duration: 0, forbidClick: true, message: '查询中', });
getByPhone(obj).then(res =>{
console.log(res)
Toast.clear()
router.replace('/applyRecord/'+phone.value)
}).catch(err =>{
console.log(err)
})
}
}
}
</script>
<style lang='scss' scoped>
.box{
// position: relative;
// display: flex;
// flex-direction: column;
overflow: auto;
align-items: center;
padding: 2rem /* 150/75 */ 0 0;
box-sizing: border-box;
.icon-h1{
width: 5.3333rem /* 400/75 */;
height: 4rem /* 300/75 */;
margin: 0 auto;
}
.title{
line-height: .72rem /* 54/75 */;
font-size: .48rem /* 36/75 */;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #222222;
margin: .4533rem /* 34/75 */ auto .16rem /* 12/75 */;
// margin: 0 auto;
text-align: center;
}
p{
line-height: .48rem /* 36/75 */;
font-size: $sm-size;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #999999;
text-align: center;
}
input{
display: block;
width: 6.72rem /* 504/75 */;
height: 1.12rem /* 84/75 */;
background: #F5F5F5;
border-radius: 1.3333rem /* 100/75 */;
margin: .9067rem /* 68/75 */ auto 0;
font-size: $md-size;
border: none;
outline: none;
padding: 0 .56rem /* 42/75 */;
box-sizing: border-box;
}
input::-webkit-input-placeholder{
color: #999999;
}
.query-button{
display: block;
width: 6.72rem /* 504/75 */;
height: 1.12rem /* 84/75 */;
background: #D9D9D9;
border-radius: 1.3333rem /* 100/75 */ ;
color: #222222;
border: none;
font-size: $size30;
margin: .64rem /* 48/75 */ auto 0;
}
.active-button{
background-color: #30BF78;
color: #fff;
}
}
</style>
+196
View File
@@ -0,0 +1,196 @@
<template>
<div class='box' @scroll="onScroll">
<div v-for="item in recordArr" class="apply-list" :key="item.id">
<h3 class="h3">
<span>提交时间{{ item.createTime }}</span>
<!-- refuse拒绝 pass通过 状态 -->
<div class="status" :class="stateEnum[item.status].className">{{ stateEnum[item.status].statusName }}</div>
</h3>
<div class="apply-content">
<div class="user-info">
<img src="../assets/lanseyuanquan.png" alt="">
<span>拜访人{{ item.name }}</span>
</div>
<div class="user-info">
<img src="../assets/huangseyuanquan.png" alt="">
<span>拜访时间{{ item.hours }}</span>
</div>
<p>
{{ item.status == 2 && item.remark ? item.remark : stateEnum[item.status].statusText }}
<span class="triangle"></span>
</p>
</div>
</div>
</div>
</template>
<script setup>
import { ref, reactive } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { useStore } from 'vuex'
import { rulesForm, resetForm, rulesPhone } from '@/com/index'
import { postApply, getVisitor, getByPhone } from '@/api/user.js'
import { Toast, ImagePreview } from 'vant';
/* @data */
const router = useRouter()
const route = useRoute()
const store = useStore()
// console.log(route.params.iphone)
const query = reactive({
// phone: route.params.iphone,
current: 1,
size: 10
})
const total = ref()
const recordArr = ref([])
const stateEnum = store.state.stateEnum
/* @setup */
console.log(localStorage.getItem('userToken'))
let windowHeight = document.documentElement.clientHeight || document.body.clientHeight;
console.log(windowHeight)
getByPhone(query).then(res =>{
// console.log(res)
recordArr.value = res.data
total.value = res.total
// alert(windowHeight)
}).catch(err =>{
console.log(err)
})
/* @method */
let old = 0
const onScroll = (e) =>{
if(e.target.scrollTop > old){ // 说明是下拉
if(e.target.scrollHeight <= windowHeight + Math.ceil(e.target.scrollTop) ){ // 说明到底了
console.log('到底了')
if(recordArr.value.length < total.value ){
query.current++
getList()
}else{
Toast.success('数据已全部加载完成!');
}
}
}else{ // 说明是上拉
}
old = e.target.scrollTop
// setTimeout(()=>{
// alert(e.target.scrollHeight - e.target.scrollTop)
// },2000)
}
const getList = () =>{
Toast.loading({ duration: 0, forbidClick: true, message: '加载中', });
getByPhone(query).then(res =>{
// console.log(res)
recordArr.value = [ ...recordArr.value , ...res.data]
total.value = res.total
Toast.clear();
}).catch(err =>{
query.current--
console.log(err)
})
}
</script>
<style lang='scss' scoped>
.box{
width: 100%;
height: 100vh;
overflow: auto;
background: #EDEDED;
padding: .4rem /* 30/75 */;
box-sizing: border-box;
.apply-list{
width: 100%;
// height: 5.0667rem /* 380/75 */;
background: #FFFFFF;
border-radius: .2667rem /* 20/75 */;
padding: 0 .4rem /* 30/75 */ .5867rem /* 44/75 */;
box-sizing: border-box;
margin-bottom: .4rem /* 30/75 */;
.h3{
width: 100%;
height: 1.2267rem /* 92/75 */;
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: .0267rem /* 2/75 */ dashed #D9D9D9;
padding: 0 .1067rem /* 8/75 */;
box-sizing: border-box;
span{
font-size: $sm-size;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #999999;
}
.status{
font-size: $size26;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 500;
color: #448CF7;
}
.pass{
color: #18C297;
}
.refuse{
color: #FF6643;
}
}
.apply-content{
width: 100%;
padding: .5067rem /* 38/75 */ 0 0;
.user-info{
padding: 0 .24rem /* 18/75 */;
box-sizing: border-box;
font-size: $md-size;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #000000;
display: flex;
align-items: center;
height: .56rem /* 42/75 */;
margin-bottom: .2133rem /* 16/75 */;
img{
width: .2667rem /* 20/75 */;
height: .2667rem /* 20/75 */;
margin-right: .2133rem /* 16/75 */;
}
}
p{
position: relative;
width: 8.2133rem /* 616/75 */;
padding: .2133rem /* 16/75 */ .4533rem /* 34/75 */;
box-sizing: border-box;
line-height: .48rem /* 36/75 */;
// height: .9067rem /* 68/75 */;
background: #EAEEF6;
// background: #fff;
border-radius: .1333rem /* 10/75 */;
font-size: $sm-size;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #5F6583;
margin-top: .5067rem /* 38/75 */;
.triangle{
position: absolute;
top: -.08rem /* 6/75 */;
left: .5333rem /* 40/75 */;
width: .2rem /* 15/75 */;
height: .2rem /* 15/75 */;
transform: rotateZ(-45deg);
background: #EAEEF6;
}
}
}
}
}
</style>
+786
View File
@@ -0,0 +1,786 @@
<template>
<div v-if="true" class='box'>
<div class="apply-box">
<img class="text" src="../assets/zhinengfangke.png" alt="">
<p>来访提前约 拜访开门无需按铃</p>
<img class="absolute-img" src="../assets/woyaoyuyue.png" alt="">
<div class="apply-content">
<h3>
<img src="../assets/icon-shenfenzheng.png" alt="">
<span>拜访信息<span>(我找谁)</span></span>
</h3>
<div class="apply-content-form">
<Inputli v-model="form.name" label="访客姓名" :isMust="true" :isline="true" isMustPosition="left"/>
<Inputli v-model="form.phone" label="联系方式" :isMust="true" :isline="true" isMustPosition="left"/>
<Inputli v-model="form.visitingNum" label="随行人数" :isMust="true" :isline="true" isMustPosition="left"/>
<Inputli v-model="form.reason" label="来访事由" :isMust="true" :isline="true" isMustPosition="left"/>
<div class="upload-box">
<label for=""><span>*&nbsp;</span>人脸照片</label>
<div v-if="!form.img" class="upload-button">
<img @click="onClickUpload" src="../assets/xiangji.png" alt="">
<input v-if="isDisabled" @change="onChangeFile" type="file" accept="image/*">
</div>
<div v-else class="face-img">
<img :src="form.img" alt="">
<input v-if="true" @change="onChangeFile" type="file" accept="image/*">
</div>
</div>
</div>
</div>
</div>
<p class="tips">
说明人脸照片用于通过门禁系统请上传正脸无遮挡照片
</p>
<p class="tel-tips">
如果您在使用中遇到问题请联系<span @click="onClickCall">400040-1573</span>
</p>
<div class="visitor-content">
<h3>
<img src="../assets/icon-shenfenzheng.png" alt="">
<span>访客信息<span>(我是谁)</span></span>
</h3>
<div class="visitor-content-form">
<Inputli v-if="telName" v-model="telName" @input="onInputName" label="被访人" :isMust="true" :isline="true" isMustPosition="left"/>
<Inputli v-else v-model="form.visitingTel" @blur="onBlurTel" @input="onInputTel" label="被访人电话" :isMust="true" :isline="true" isMustPosition="left"/>
<Selectdate v-model="form.hours" label="来访时间" :isMust="true" :isline="false" :minDate="minDate"/>
</div>
</div>
<p class="tips" style="margin-top: .2rem /* 15/75 */">
说明拜访时间前后一小时都可刷脸通过门禁
</p>
<!-- <p class="tips">
说明拜访时间前后一小时都可刷脸通过门禁
</p>
<div v-if="false" class="visitor-content">
<h3>
<img src="../assets/icon-shenfenzheng.png" alt="">
<span>访客信息<span>(我是谁)</span></span>
</h3>
<div class="visitor-content-form">
<Inputli v-if="telName" v-model="telName" @input="onInputName" label="被访人" :isMust="true" :isline="true" isMustPosition="left"/>
<Inputli v-else v-model="form.visitingTel" @blur="onBlurTel" @input="onInputTel" label="被访人电话" :isMust="true" :isline="true" isMustPosition="left"/>
<Selectdate v-model="form.hours" label="来访时间" :isMust="true" :isline="false" :minDate="minDate"/>
</div>
</div>
<p class="tips">
说明人脸照片用于通过门禁系统请上传正脸无遮挡照片
</p>
<p class="tel-tips">
如果您在使用中遇到问题请联系<span @click="onClickCall">400040-1573</span>
</p> -->
<!-- <div class="disclaimer-box">
<input type="checkbox">
<p>勾选表示同意<span>用户协议</span></p>
</div> -->
<button @click="onClcikSubmit" class="submit-button " :class="isSubmit ? 'active-button' : ''">提交</button>
<Successmodel v-if="isSuccess" @click="onClickAffirm" />
<div v-if="false" class="disclaimer-model-fixed">
<h3>用户协议</h3>
<div class="disclaimer-model">
阿哈撒发哈师范哈撒发哈很舒服哈哈
阿哈撒发哈师范哈撒发哈很舒服哈哈
阿哈撒发哈师范哈撒发哈很舒服哈哈
阿哈撒发哈师范哈撒发哈很舒服哈哈
阿哈撒发哈师范哈撒发哈很舒服哈哈
阿哈撒发哈师范哈撒发哈很舒服哈哈
阿哈撒发哈师范哈撒发哈很舒服哈哈
阿哈撒发哈师范哈撒发哈很舒服哈哈
阿哈撒发哈师范哈撒发哈很舒服哈哈
阿哈撒发哈师范哈撒发哈很舒服哈哈
阿哈撒发哈师范哈撒发哈很舒服哈哈
</div>
</div>
</div>
</template>
<script setup>
import { reactive, ref, watch } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { useStore } from 'vuex'
import { Toast, ImagePreview , Uploader, Dialog } from 'vant';
import { rulesForm, resetForm, rulesPhone, rulesFormFBoolean, formatDate } from '@/com/index'
import Inputli from '@/components/input-li.vue'
import Selectli from '@/components/select-li.vue'
import Selectdate from '@/components/select-li-date.vue'
import Successmodel from '../components/success.vue'
import { postApply, getVisitor, getUserNameByTel } from '@/api/user.js'
import { uploadApi } from '@/api/upload'
/* @data */
const router = useRouter()
const route = useRoute()
const store = useStore()
const phoneReg = /(^1\d{10}$)|(^[0-9]\d{7}$)/;
let form = reactive({
name: "",
phone: "",
hours: "",
reason: "",
img: "",
visitingTel: "",
visitingNum: '',
})
const isSubmit = ref(false)
const visitDict = ref([]) // 被访客数据
const visitAllDict = ref([]) // 被访客数据
const isDisabled = ref(false) // 是否能提交
const isSuccess = ref(false) // 是否提交成功
const isSelect = ref( false) // 是否显示下拉选择
const visitorName = ref( '') // 是否显示下拉选择的名字
const telName = ref( '') // 电话查询姓名
/* watch */
watch(() => form, (v1, v2) => {
isSubmit.value = rulesFormFBoolean(v2)
if(v2.name && v2.phone){
if(!phoneReg.test(v2.phone)){
isDisabled.value = false
}else{
isDisabled.value = true
}
}else{
isDisabled.value = false
}
},{ deep: true }
) // 深度监听 ) // 监听单个
/* @method */
const onInputTel = (e) =>{
}
const onInputName = (e) =>{
form.visitingTel = ''
telName.value = ''
}
const onBlurTel = (phone) =>{
console.log(phone)
if(rulesPhone(phone)){
getUserNameByTel(phone).then(res =>{
console.log(res)
if(res.data){
telName.value = res.data
}
}).catch(err =>{
console.log(err)
Dialog.alert({
message: err.msg,
}).then(() => {
// on close
});
})
}
}
// const onBlurVisitor = () =>{
// isSelect.value = false
// let item = visitAllDict.value.find(item =>{ return item.userId == form.visitingTel })
// if(item){
// visitorName.value = item.name
// visitDict.value = visitAllDict.value
// }else{
// visitorName.value = '' // 是否显示下拉选择的名字
// visitDict.value = visitAllDict.value
// }
// }
// 输入框
// const onInputVisior = (e) =>{
// visitorName.value = e.target.value
// let arr = []
// visitAllDict.value.map( item =>{
// if(item.name.search(e.target.value) != -1){
// arr.push(item)
// }
// })
// visitDict.value = [ ...arr ]
// }
// 点击选择了拜访对象
// const onClickSelect = (item) =>{
// form.visitingTel = item.userId
// visitorName.value = item.name
// isSelect.value = false
// }
// const onClickVisitor = () =>{
// isSelect.value = true
// }
// 点击确认模态框
const onClickAffirm = () =>{
router.go(-1)
}
const onClickUpload = () =>{
if(isDisabled.value){
}else{
Toast.fail('请填写访客姓名和正确的联系方式!')
}
}
const onClcikSubmit = () =>{
if(isSubmit.value){
if(rulesPhone(form.phone) && rulesPhone(form.visitingTel)){
Toast.loading({ duration: 0, forbidClick: true, message: '提交中', });
postApply(form).then(res =>{
Toast.clear();
isSuccess.value = true
})
}
}else{
Toast.fail('请先填写完整信息后提交!')
}
}
// 上传照片
const onChangeFile = (e) =>{
console.log(e.target.files[0].size)
if(e.target.files[0]){
newcompressImg(e.target.files[0], 0.5).then(res =>{
let formData = new FormData()
// formData.append('file', e.target.files[0])
formData.append('file', res.file)
formData.append('userId', form.phone)
formData.append('name', form.name)
Toast.loading({ duration: 0, forbidClick: true, message: '上传中', });
uploadApi(formData).then(res =>{
Toast.success(res.msg);
form.img = res.data
})
}).catch(err =>{
Toast.fail('图片上传失败,请重新上传')
})
}
}
const onClickCall = () =>{
window.location.href = 'tel:400-040-1573'
}
const newcompressImg = (file, quality) =>{
var qualitys = 0.52
if (parseInt((file.size / 1024).toFixed(2)) < 1024) {
qualitys = 0.85
}
if (5 * 1024 < parseInt((file.size / 1024).toFixed(2))) {
qualitys = 0.92
}
if (quality) {
qualitys = quality
}
if (file[0]) {
return Promise.all(Array.from(file).map(e => this.compressImg(e,
qualitys))) // 如果是 file 数组返回 Promise 数组
} else {
return new Promise((resolve) => {
if ((file.size / 1024).toFixed(2) < 300) {
resolve({
file: file
})
} else {
const reader = new FileReader() // 创建 FileReader
reader.onload = ({
target: {
result: src
}
}) => {
const image = new Image() // 创建 img 元素
image.onload = async() => {
const canvas = document.createElement('canvas') // 创建 canvas 元素
const context = canvas.getContext('2d')
var targetWidth = image.width
var targetHeight = image.height
var originWidth = image.width
var originHeight = image.height
if (1 * 1024 <= parseInt((file.size / 1024).toFixed(2)) && parseInt((file.size / 1024).toFixed(2)) <= 10 * 1024) {
var maxWidth = 1600
var maxHeight = 1600
targetWidth = originWidth
targetHeight = originHeight
// 图片尺寸超过的限制
if (originWidth > maxWidth || originHeight > maxHeight) {
if (originWidth / originHeight > maxWidth / maxHeight) {
// 更宽,按照宽度限定尺寸
targetWidth = maxWidth
targetHeight = Math.round(maxWidth * (originHeight / originWidth))
} else {
targetHeight = maxHeight
targetWidth = Math.round(maxHeight * (originWidth / originHeight))
}
}
}
if (10 * 1024 <= parseInt((file.size / 1024).toFixed(2)) && parseInt((file.size / 1024).toFixed(2)) <= 20 * 1024) {
maxWidth = 1400
maxHeight = 1400
targetWidth = originWidth
targetHeight = originHeight
// 图片尺寸超过的限制
if (originWidth > maxWidth || originHeight > maxHeight) {
if (originWidth / originHeight > maxWidth / maxHeight) {
// 更宽,按照宽度限定尺寸
targetWidth = maxWidth
targetHeight = Math.round(maxWidth * (originHeight / originWidth))
} else {
targetHeight = maxHeight
targetWidth = Math.round(maxHeight * (originWidth / originHeight))
}
}
}
canvas.width = targetWidth
canvas.height = targetHeight
context.clearRect(0, 0, targetWidth, targetHeight)
context.drawImage(image, 0, 0, targetWidth, targetHeight) // 绘制 canvas
const canvasURL = canvas.toDataURL('image/jpeg', qualitys)
const buffer = atob(canvasURL.split(',')[1])
let length = buffer.length
const bufferArray = new Uint8Array(new ArrayBuffer(length))
while (length--) {
bufferArray[length] = buffer.charCodeAt(length)
}
const miniFile = new File([bufferArray], file.name, {
type: 'image/jpeg'
})
resolve({
file: miniFile,
origin: file,
beforeSrc: src,
afterSrc: canvasURL,
beforeKB: Number((file.size / 1024).toFixed(2)),
afterKB: Number((miniFile.size / 1024).toFixed(2))
})
}
image.src = src
}
reader.readAsDataURL(file)
}
})
}
}
/* @setup */
const minDate = ref( formatDate(new Date()))
console.log(route.query.tel)
if(route.query.tel){
form.visitingTel = route.query.tel
onBlurTel(route.query.tel)
}
getVisitor().then( res=>{
visitDict.value = res.data
visitAllDict.value = res.data
})
</script>
<style lang='scss' scoped>
.box{
width: 100%;
height: 100%;
background: #F5F5F5;
padding: 0 0 1.08rem /* 81/75 */;
box-sizing: border-box;
overflow: auto;
.apply-box{
position: relative;
width: 100%;
height: 8.0267rem /* 602/75 */;
background: url('../assets/yuyuebeijing.png') left top/ 100% 100% no-repeat;
padding: 1.04rem /* 78/75 */ 0 0;
box-sizing: border-box;
.text{
width: 3.5467rem /* 266/75 */;
height: 1.1467rem /* 86/75 */;
margin-left: .6133rem /* 46/75 */;
}
p{
line-height: .48rem /* 36/75 */;
font-size: $sm-size;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #999999;
padding: .1067rem /* 8/75 */ 0 0 .7467rem /* 56/75 */;
}
.apply-content{
width: 9.2rem /* 690/75 */;
// height: 4.32rem /* 324/75 */;
background: #FFFFFF;
border-radius: .2667rem /* 20/75 */;
margin: 1.0133rem /* 76/75 */ auto 0;
h3{
padding: .5067rem /* 38/75 */ .3467rem /* 26/75 */ 0 .4533rem /* 34/75 */;
box-sizing: border-box;
display: flex;
align-items: center;
color: #222222;
font-size: $size30;
img{
width: .5867rem /* 44/75 */;
height: .5333rem /* 40/75 */;
margin-right: .16rem /* 12/75 */;
}
span{
span{
font-size: .32rem /* 24/75 */;
color: #666;
}
}
}
.apply-content-form{
width: 100%;
padding: 0 .3467rem /* 26/75 */ 0 .4533rem /* 34/75 */;
box-sizing: border-box;
.search-select-box{
position: relative;
width: 100%;
height: 1.5733rem /* 118/75 */;
// background: #F9F9F9;
// border-radius: .2667rem /* 20/75 */;
padding: 0 .0267rem /* 2/75 */ 0;
box-sizing: border-box;
display: flex;
// margin-bottom: .2667rem /* 20/75 */;
border-bottom: 0.0267rem dashed #D9D9D9;
.label{
flex: 1;
font-size: $size26;
color: #333333;
line-height: 1.5733rem /* 118/75 */;
span{
color: #FF0000;
margin-right: .0267rem /* 2/75 */;
}
}
.search-select-input{
position: relative;
height: 100%;
box-sizing: border-box;
input{
flex: 1;
border: none;
outline: none;
text-align: right;
font-size: $size26;
color: #333333;
// background: #F9F9F9;
padding-right: 0.3733rem;
// line-height: 1.5733rem /* 118/75 */;
box-sizing: border-box;
height: 100%;
}
input::-webkit-input-placeholder {
font-size: $size26;
color: #999999;
}
.select-box{
position: absolute;
right: 0;
top: 1.8667rem /* 140/75 */;
z-index: 5;
width: 3.7333rem /* 280/75 */;
// max-height: 5.0667rem /* 380/75 */;
background: #FFFFFF;
box-shadow: 0px 0px .2667rem /* 20/75 */ .1067rem /* 8/75 */ rgba(0,0,0,0.1);
border-radius: .0533rem /* 4/75 */ ;
// overflow: auto;
.select-sroll-box{
// position: absolute;
// right: 0;
// top: 1.8667rem /* 140/75 */;
// z-index: 5;
width: 100%;
max-height: 5.0667rem /* 380/75 */;
background: #FFFFFF;
// box-shadow: 0px 0px .2667rem /* 20/75 */ .1067rem /* 8/75 */ rgba(0,0,0,0.1);
// border-radius: .0533rem /* 4/75 */ ;
overflow: auto;
}
.select-li{
height: 1.0133rem /* 76/75 */;
background: #fff;
border-radius: .0533rem /* 4/75 */ .0533rem /* 4/75 */ 0px 0px;
font-size: $size26;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #999999;
line-height: 1.0133rem /* 76/75 */;
text-align: center;
}
.select-active{
color: #30BF78;
background: #F3FEFB;
}
}
b{
position: absolute;
left: 50%;
// top: -100%;
// width: .2rem /* 15/75 */;
// height: 20px;
// background: red;
// display: block;
width: 0;
height: 0;
border: .16rem /* 12/75 */ solid transparent;
border-bottom: .16rem /* 12/75 */ solid #fff;
transform: translateY(-100%) translateX(-50%);
// margin-top: -.6667rem /* 50/75 */;
}
}
}
.upload-box{
width: 100%;
height: 1.9733rem /* 148/75 */;
// padding: 0 .6933rem /* 52/75 */ 0 0;
padding-right: .3733rem /* 28/75 */;
box-sizing: border-box;
display: flex;
justify-content: space-between;
align-items: center;
color: #333333;
font-size: $size26;
label{
line-height: 1.9733rem /* 148/75 */;
span{
color: #FF0000;
}
}
.upload-button{
position: relative;
width: 1.3333rem /* 100/75 */;
height: 1.3333rem /* 100/75 */;
background: #FFFFFF;
border-radius: .1333rem /* 10/75 */;
border: .0267rem /* 2/75 */ dashed #D9D9D9;
display: flex;
align-items: center;
justify-content: center;
// overflow: hidden;
input{
position: absolute;
left: 0;
top: 0;
width: 1.3333rem /* 100/75 */;
height: 1.3333rem /* 100/75 */;
opacity: 0;
}
}
.face-img{
position: relative;
width: 1.3333rem /* 100/75 */;
height: 1.3333rem /* 100/75 */;
background: #FFFFFF;
border-radius: .1333rem /* 10/75 */;
img{
width: 100%;
height: 100%;
border-radius: .1333rem /* 10/75 */;
}
input{
position: absolute;
left: 0;
top: 0;
width: 1.3333rem /* 100/75 */;
height: 1.3333rem /* 100/75 */;
opacity: 0;
}
}
}
}
}
.absolute-img{
position: absolute;
top: .5333rem /* 40/75 */;
right: .4rem /* 30/75 */;
width: 4.5067rem /* 338/75 */;
height: 3.7867rem /* 284/75 */;
}
}
.tips{
line-height: .4533rem /* 34/75 */;
font-size: .2933rem /* 22/75 */;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #999999;
padding: .2133rem /* 16/75 */ 0 .4267rem /* 32/75 */ .8rem /* 60/75 */;
box-sizing: border-box;
margin-top: 5.3333rem /* 400/75 */;
}
.visitor-content{
width: 9.2rem /* 690/75 */;
// height: 4.32rem /* 324/75 */;
background: #FFFFFF;
border-radius: .2667rem /* 20/75 */;
margin: 0 auto 0;
// margin-top: 5.3333rem /* 400/75 */;
h3{
padding: .5067rem /* 38/75 */ .3467rem /* 26/75 */ 0 .4533rem /* 34/75 */;
box-sizing: border-box;
display: flex;
align-items: center;
color: #222222;
font-size: $size30;
img{
width: .5867rem /* 44/75 */;
height: .5333rem /* 40/75 */;
margin-right: .16rem /* 12/75 */;
}
span{
span{
font-size: .32rem /* 24/75 */;
color: #666;
}
}
}
.visitor-content-form{
width: 100%;
padding: 0 .3467rem /* 26/75 */ .16rem /* 12/75 */ .4533rem /* 34/75 */;
box-sizing: border-box;
.upload-box{
width: 100%;
height: 1.9733rem /* 148/75 */;
// padding: 0 .6933rem /* 52/75 */ 0 0;
padding-right: .3733rem /* 28/75 */;
box-sizing: border-box;
display: flex;
justify-content: space-between;
align-items: center;
color: #333333;
font-size: $size26;
label{
line-height: 1.9733rem /* 148/75 */;
span{
color: #FF0000;
}
}
.upload-button{
position: relative;
width: 1.3333rem /* 100/75 */;
height: 1.3333rem /* 100/75 */;
background: #FFFFFF;
border-radius: .1333rem /* 10/75 */;
border: .0267rem /* 2/75 */ dashed #D9D9D9;
display: flex;
align-items: center;
justify-content: center;
// overflow: hidden;
input{
position: absolute;
left: 0;
top: 0;
width: 1.3333rem /* 100/75 */;
height: 1.3333rem /* 100/75 */;
opacity: 0;
}
}
.face-img{
position: relative;
width: 1.3333rem /* 100/75 */;
height: 1.3333rem /* 100/75 */;
background: #FFFFFF;
border-radius: .1333rem /* 10/75 */;
img{
width: 100%;
height: 100%;
border-radius: .1333rem /* 10/75 */;
}
input{
position: absolute;
left: 0;
top: 0;
width: 1.3333rem /* 100/75 */;
height: 1.3333rem /* 100/75 */;
opacity: 0;
}
}
}
}
}
.disclaimer-box{
display: flex;
justify-content: center;
input{
margin-right: .1333rem /* 10/75 */;
}
span{
color: #30BF78;
}
}
.tel-tips{
line-height: .4533rem /* 34/75 */;
font-size: .2933rem /* 22/75 */;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #999999;
padding: 0 0 .4267rem /* 32/75 */ .8rem /* 60/75 */;
box-sizing: border-box;
margin-top: -.24rem /* 18/75 */;
span{
color: #409eff;
}
}
.submit-button{
display: block;
width: 6.72rem /* 504/75 */;
height: 1.12rem /* 84/75 */;
background: #D9D9D9;
border-radius: 1.3333rem /* 100/75 */ ;
opacity: 1;
font-size: $size30;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 500;
color: #222222;
border: none;
outline: none;
margin: 1.0133rem /* 76/75 */ auto 0;
}
.active-button{
background-color: #30BF78;
color: #fff;
}
}
.disclaimer-model-fixed{
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: #fff;
display: flex;
flex-direction: column;
padding: .4rem /* 30/75 */;
box-sizing: border-box;
h3{
font-size: .4267rem /* 32/75 */;
line-height: .8rem /* 60/75 */;
text-align: center;
}
.disclaimer-model{
flex: 1;
line-height: .5333rem /* 40/75 */;
text-align: justify;
}
}
</style>
+404
View File
@@ -0,0 +1,404 @@
<template>
<div class="box">
<header>
<h2>门禁人脸信息</h2>
<h3> <img src="../assets/img/dunpai.png" alt=""> 公司门禁系统人脸上传和查看 </h3>
<div class="entering-box">
<img src="../assets/img/shenfenzheng.png" alt="">
<div class="entering-center">
<p>特殊人员录入</p>
<p>暂未进入公司钉钉组织人员</p>
</div>
<button @click="onClickGoSpecialCrowd" class="entering-button">录入</button>
</div>
</header>
<div class="content">
<h2>通讯录</h2>
<div class="content-box" >
<div class="nav">
<div @click="onClickDept(item.deptId)" v-for="item in deptArr" class="li" :class="item.deptId == deptId ? 'active' : ''" :style=" item.name.length >= 6 ? 'padding-right: 0.28rem;' : '' " :key="item.userId">
{{ item.name }}
</div>
</div>
<div class="user-content">
<div class="user-scroll">
<div v-for="item in userArr" class="user-list">
<div @click="onClickPreviewImg(item.img)" class="img-box">
<img v-if="item.img" :src="item.img" alt="">
</div>
<div class="name">{{ item.name }}</div>
<div @click="onClcikUser(item)" v-if="item.img" class="button">查看</div>
<div @click="onClcikUser(item)" v-else class="button active-button">上传</div>
</div>
</div>
</div>
</div>
</div>
</div>
<Facemodel @change="onChangeFile" @cancel="isFace = false; form.face =''" @save="onClciksave" v-if="isFace" :faceImg="form.face" />
</template>
<script setup>
import { rulesPhone, compareTime, timeDiff,formatDate } from '../com/index.js'
// import { getShop, getKind } from '../api/user.js'
import { reactive, ref } from 'vue'
import { Toast, ImagePreview, Skeleton } from 'vant';
import dd from 'dingtalk-jsapi'
import { getToken, getDept, getUserListByDept, putUserId } from '@/api/doorAdmin.js'
import Facemodel from '@/components/face-model.vue'
import { uploadApi } from '@/api/upload'
import { useRouter } from 'vue-router';
const router = useRouter()
const deptArr = ref([]) // 部门数据
const userArr = ref([]) // 用户
const deptId = ref('') // 选中的部门Id
const isFace = ref(false)
const form = reactive({ //
face: '',
userId: '',
name: ''
})
const token = ref('')
const isLoading = ref(false)
localStorage.setItem('redirect', '/home')
token.value = localStorage.getItem('token')
// setTimeout( ()=>{
// form.face = 'https://img0.baidu.com/it/u=3229206321,1418550973&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=500'
// }, 2000)
// method
// 点击预栏图片
const onClickPreviewImg = (img) =>{
dd.previewImage({
urls: [img
],
current: 1,
success: () => {},
fail: () => {},
complete: () => {},
});
}
// 点击切换部门
const onClickDept = (Id) =>{
if(deptId.value != Id){
deptId.value = Id
userList()
}
}
// 点击上传了文件
const onChangeFile = (file) =>{
console.log(file)
if(file){
let reader = new FileReader()
reader.readAsDataURL(file) // 转base64
reader.onload = () =>{ // 简化代码 - 转换完成
form.face = reader.result
}
}
}
// 点击保存
const onClciksave = (file) =>{
if(form.face && file){
let formData = new FormData()
formData.append('file', file)
formData.append('userId', form.userId)
formData.append('name', form.name)
Toast.loading({ duration: 0, forbidClick: true, message: '上传中', });
uploadApi(formData).then(res =>{
form.face = res.data
let obj = { userId: form.userId, img: form.face }
putUserId(obj).then(response =>{
userList()
isFace.value = false
Toast.success(response.msg);
})
})
}else if(form.face && !file ){ // 说明之前有数据现在没传
isFace.value = false
}else if(!form.face && !file){
Toast.fail('请先上传照片!');
}
// if(file){ // 文件是否上传了
// let formData = new FormData()
// formData.append('face', file)
// formData.append('userId', form.userId)
// formData.append('name', form.name)
// Toast.loading({ duration: 0, forbidClick: true, message: '保存中', });
// uploadApi(formData).then(res =>{
// Toast.success(res.msg);
// form.img = res.data
// })
// }else{
// Toast.fail('请先上传照片!');
// }
}
// 点击用户上传照片或查看
const onClcikUser = (item) =>{
console.log(item)
isFace.value = true
form.face = item.img
form.userId = item.userId
form.name = item.name
}
const login = () =>{
Toast.loading({ duration: 0, forbidClick: true, message: '登录中', });
dd.getAuthCode({
corpId: 'ding03f8e88dde9b426835c2f4657eb6378f',
success: (res) => {
const { code } = res;
localStorage.removeItem('token');
localStorage.removeItem('user');
getToken(code).then(data =>{
localStorage.setItem('user', data.data)
localStorage.setItem('token', data.data.authorization)
Toast.clear()
Toast.success('登录成功')
deptList()
})
}
});
}
// 获取部门用户
const userList = () =>{
isLoading.value = false
getUserListByDept(deptId.value).then(res =>{
isLoading.value = true
userArr.value = res.data
})
}
// 获取部门列表
const deptList = () =>{
getDept().then(res =>{
deptArr.value = res.data
deptId.value = res.data[0].deptId
userList()
})
}
// 跳转特殊人员
const onClickGoSpecialCrowd = () =>{
router.push('/specialCrowd')
}
if(token.value){
// localStorage.setItem('token', 123)
deptList()
}else{
login()
}
</script>
<style lang="scss" scoped>
.box{
display: flex;
flex-direction:column ;
header{
width: 100%;
height: 5.8933rem /* 442/75 */;
background: url('../assets/img/shouyebeijing.png') left top/100% 100% no-repeat;
h2{
line-height: 1.12rem /* 84/75 */;
font-size: .7467rem /* 56/75 */;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 500;
color: #FFFFFF;
padding: .8533rem /* 64/75 */ 0 0 .64rem /* 48/75 */;
box-sizing: border-box;
}
h3{
display: flex;
padding: .32rem /* 24/75 */ 0 0 .7467rem /* 56/75 */;
box-sizing: border-box;
line-height: .5333rem /* 40/75 */;
font-size: $sm-size;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #FFFFFF;
img{
width: .5333rem /* 40/75 */;
height: .5333rem /* 40/75 */;
margin-right: .1067rem /* 8/75 */;
}
}
.entering-box{
width: 8rem /* 600/75 */;
height: 1.6rem /* 120/75 */;
margin: .6667rem /* 50/75 */ auto 0;
background: #202F44;
border-radius: .5333rem /* 40/75 */ .5333rem /* 40/75 */ 0px 0px;
padding: 0 .4rem /* 30/75 */ .8rem /* 60/75 */ .4533rem /* 34/75 */;
display: flex;
align-items: center;
img{
width: .6133rem /* 46/75 */;
height: .48rem /* 36/75 */;
margin-right: .2933rem /* 22/75 */;
}
.entering-center{
flex: 1;
font-size: $md-size;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 500;
color: #FFFFFF;
p:nth-child(1){
line-height: .56rem /* 42/75 */;
padding: .2933rem /* 22/75 */ 0 0;
}
p:nth-child(2){
line-height: .4533rem /* 34/75 */;
font-size: $sm-size;
font-weight: 400;
color: #B6B7B8;
}
}
.entering-button{
width: 1.4667rem /* 110/75 */;
height: .6933rem /* 52/75 */;
background: #F6C976;
border-radius: .5333rem /* 40/75 */;
outline: none;
font-size: $size26;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 500;
color: #202F44;
border: none;
}
}
}
.content{
width: 100%;
background: #FFFFFF;
border-radius: .5333rem /* 40/75 */ .5333rem /* 40/75 */ 0px 0px;
margin-top: -20px;
overflow: auto;
flex: 1;
display: flex;
flex-direction: column ;
h2{
padding: .4267rem /* 32/75 */ .48rem /* 36/75 */;
box-sizing: border-box;
line-height: .8rem /* 60/75 */;
font-size: .5333rem /* 40/75 */;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 500;
color: #222222;
}
.content-box{
overflow: auto;
display: flex;
flex: 1;
.nav{
width: 3.6533rem /* 274/75 */;
height: 100%;
// width: 360px;
padding: 0 0 0 .4rem /* 30/75 */;
box-sizing: border-box;
display: flex;
align-content: flex-start;
// flex-direction: column;
flex-wrap: wrap;
border-right: .0267rem /* 2/75 */ dashed #D9D9D9;
.li{
display:block;
box-sizing: border-box;
height: .9067rem /* 68/75 */;
line-height: .9067rem /* 68/75 */;
border-radius: 0px .8rem /* 60/75 */ .8rem /* 60/75 */ 0px ;
padding: 0 .48rem /* 36/75 */ 0 .24rem /* 18/75 */;
box-sizing: border-box;
box-sizing: border-box;
font-size: $md-size;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #5F6583;
margin-bottom: .28rem /* 21/75 */;
}
.active{
color: #FFFFFF;
background: #448CF7;
}
}
.user-content{
flex: 1;
height: 100%;
overflow: auto;
.user-scroll{
width: 100%;
// height: 100%;
}
.user-list{
display: flex;
align-items: center;
padding: 0 .4rem /* 30/75 */;
box-sizing: border-box;
margin-bottom: .5333rem /* 40/75 */;
.img-box{
width: .9867rem /* 74/75 */;
height: .9867rem /* 74/75 */;
border-radius:.1333rem /* 10/75 */;
background: #D9D9D9;
img{
display: block;
width: 100%;
height: 100%;
border-radius:.1333rem /* 10/75 */;
}
}
.name{
padding: 0 0 0 .2933rem /* 22/75 */;
box-sizing: border-box;
flex: 1;
line-height: .9867rem /* 74/75 */;
font-size: $size30;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #4B4C4E;
}
.button{
width: 1.2267rem /* 92/75 */;
height: .6667rem /* 50/75 */;
background: #EDEDED;
border-radius:.1333rem /* 10/75 */;
border: none;
outline: none;
font-size: $sm-size;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #999999;
box-sizing: border-box;
text-align: center;
line-height: .6667rem /* 50/75 */;
}
.active-button{
background: #EBFCF4;
border: .0267rem /* 2/75 */ solid #30BF78;
color: #30BF78;
}
}
}
}
}
}
</style>
+14
View File
@@ -0,0 +1,14 @@
<template>
<div>
登录
<button @click="onClcikGo">注册</button>
</div>
</template>
<script setup>
import { useRouter } from 'vue-router';
const router = useRouter()
console.log(router)
const onClcikGo = () =>{
router.push('/register')
}
</script>
+5
View File
@@ -0,0 +1,5 @@
<template>
<div>
注册
</div>
</template>
+370
View File
@@ -0,0 +1,370 @@
<template>
<div class='box'>
<header @click="onClickAdd">
<img src="../assets/img/baiseshenfenzheng.png" alt="">
<span>新增特殊人员</span>
</header>
<h2>
<div class="title">已录入人员 <img class="subscript" src="../assets/img/xiabiao.png" alt=""></div>
<div class="tips">人员信息删除后不可在刷脸通过门禁</div>
</h2>
<div class="content">
<div class="search-box">
<input type="text" placeholder="输入员工姓名进行搜索">
<img src="../assets/img/sousuo.png" alt="">
</div>
<div v-if="specialArr.length" class="scroll-content">
<div v-for="item in specialArr" class="user-list" :key="item.id">
<div class="img-box">
<img @click="onClickPreviewImg(item.img)" v-if="item.img" :src="item.img" alt="">
</div>
<div class="name">
<span>{{ item.name }}</span>
<p>联系方式{{ item.tel }}</p>
</div>
<div @click="onClickLook(item)" class="button">查看</div>
<div @click="onClickDelete(item)" class="remove-button">删除</div>
</div>
</div>
<div v-else class="scroll-content">
<div class="no-have">
暂无特殊人员
</div>
</div>
</div>
</div>
<Addmodel @cancel="isAddmodel = false" @save="onClciksave" v-if="isAddmodel" />
<Removemodel @delete="onClickDeleteAffirm" @cancel="isRemovemodel = false" v-if="isRemovemodel" />
<Facemodel @save="onClickSaveFace" @change="onChangeFace" @cancel="isFacemodel = false" v-if="isFacemodel" :faceImg="updateFaceImg" />
</template>
<script setup>
import { ref, reactive } from 'vue'
import { useRouter } from 'vue-router'
import { useStore } from 'vuex'
import { rulesForm, resetForm, rulesPhone } from '@/com/index'
import Addmodel from '@/components/add-model.vue'
import Removemodel from '@/components/remove-model.vue'
import Facemodel from '@/components/face-model.vue'
import dd from 'dingtalk-jsapi'
import { getToken, getDept, getUserListByDept, putUserId, getSpecial, putSpecial, postSpecial, putSpecialFace } from '@/api/doorAdmin.js'
import { uploadApi } from '@/api/upload'
import { Toast } from 'vant'
/* @data */
const router = useRouter()
const store = useStore()
const isAddmodel = ref(false) // 新增特殊人员模态框
const isRemovemodel = ref(false) // 删除特殊人员模态框
const isFacemodel = ref(false) // 人脸模态框
const specialArr = ref([]) // 特殊人员数据
const update = ref() // 特殊人员修改数据
const deleteId = ref('') // 需要删除的id
const updateFaceImg = ref() // 修改人脸照片base64或之前的数据
const query = reactive({
current: -1,
size: 10,
})
/* @setup */
/* @method */
// 点击预栏图片
const onClickPreviewImg = (img) =>{
dd.previewImage({
urls: [img
],
current: 1,
success: () => {},
fail: () => {},
complete: () => {},
});
}
const onChangeFace = (file) =>{
if(file){
let reader = new FileReader()
reader.readAsDataURL(file) // 转base64
reader.onload = () =>{ // 简化代码 - 转换完成
updateFaceImg.value = reader.result
}
}
}
// 点击确认保存修改图片
const onClickSaveFace = (file) =>{
console.log(update.value)
if(file){
let formData = new FormData()
formData.append('file', file)
formData.append('userId', update.value.tel)
formData.append('name', update.value.name)
Toast.loading({ duration: 0, forbidClick: true, message: '上传中', });
uploadApi(formData).then(res =>{
// form.face = res.data
let obj = { id: update.value.id, img: res.data }
putSpecialFace(obj).then(response =>{
specialList()
isFacemodel.value = false
Toast.success(response.msg);
})
})
}
}
// 点击查看按钮
const onClickLook = (item) =>{
update.value = item
isFacemodel.value = true
updateFaceImg.value = item.img
console.log(item)
}
// 点击新增特殊人员按钮
const onClickAdd = () =>{
isAddmodel.value = true
}
// 确认删除
const onClickDeleteAffirm = () =>{
console.log(deleteId.value)
putSpecial(deleteId.value).then(res =>{
Toast.success(res.msg)
isRemovemodel.value = false
specialList()
})
}
// 点击删除
const onClickDelete = (item) =>{
console.log(item)
deleteId.value = item.id
isRemovemodel.value = true
}
// 点击保存
const onClciksave = (specialForm) =>{
console.log(specialForm)
if( !specialForm.face ){
Toast.fail('请上传人脸照片!')
}else if( !specialForm.userId ){
Toast.fail('请输入人员联系方式!')
}else if( !specialForm.name){
Toast.fail('请输入人员姓名!')
}else {
if(rulesPhone(specialForm.userId)){
let formData = new FormData()
formData.append('file', specialForm.face)
formData.append('userId', specialForm.userId)
formData.append('name', specialForm.name)
Toast.loading({ duration: 0, forbidClick: true, message: '保存中', });
uploadApi(formData).then(res =>{
let obj = { tel: specialForm.userId, img: res.data, name: specialForm.name }
postSpecial(obj).then(response =>{
specialList()
isAddmodel.value = false
Toast.success(response.msg);
})
})
}
}
}
// 特殊人员列表
const specialList = () =>{
getSpecial(query).then(res =>{
specialArr.value = res.data
})
}
/* @setup */
specialList()
</script>
<style lang='scss' scoped>
.box{
padding: .6667rem /* 50/75 */ .4rem /* 30/75 */ .4rem /* 30/75 */;
box-sizing: border-box;
background: #F4F6F7;
display: flex;
flex-direction: column;
header{
width: 8.4267rem /* 632/75 */;
height: 1.6rem /* 120/75 */;
background: linear-gradient(108deg, #7CB5FF 0%, #448CF7 84%);
box-shadow: 0px .1067rem /* 8/75 */ .2667rem /* 20/75 */ 0px #C2DBFF;
border-radius: 1.3333rem /* 100/75 */;
display: flex;
// justify-content: center;
align-items: center;
padding: 0 0 0 2.4533rem /* 184/75 */;
box-sizing: border-box;
margin: 0 auto;
img{
width: .72rem /* 54/75 */;
height: .6933rem /* 52/75 */;
margin-right: .2133rem /* 16/75 */;
}
span{
font-size: $lg-size;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 500;
color: #FFFFFF;
line-height: 1.6rem /* 120/75 */;
}
}
h2{
margin-top: .64rem /* 48/75 */;
height: 1.2533rem /* 94/75 */;
display: flex;
.title{
position: relative;
width: 3.0933rem /* 232/75 */;
line-height: 1.2533rem /* 94/75 */;
font-size: $lg-size;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 500;
color: #222222;
background: url('../assets/img/tixing.png') left top/100% 100% no-repeat;
border-radius: .2667rem /* 20/75 */ .2667rem /* 20/75 */ 0 0 ;
text-align: center;
.subscript{
position: absolute;
left: 50%;
bottom: 0;
transform: translateX(-50%) translateY(50%);
width: .6133rem /* 46/75 */;
height: .2667rem /* 20/75 */;
}
}
.tips{
font-size: $sm-size;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #999999;
margin-left: .5333rem /* 40/75 */;
line-height: 1.2533rem /* 94/75 */;
}
}
.content{
flex: 1;
overflow: auto;
background-color: #FFFFFF;
border-radius:0 .2667rem /* 20/75 */ .2667rem /* 20/75 */ .2667rem /* 20/75 */;
padding:.5867rem /* 44/75 */ .3733rem /* 28/75 */;
box-sizing: border-box;
display: flex;
flex-direction: column;
.search-box{
width: 100%;
height: .9333rem /* 70/75 */;
background: #EEEFF3;
border-radius: .1333rem /* 10/75 */;
padding: 0 .4533rem /* 34/75 */ 0 .3467rem /* 26/75 */ ;
box-sizing: border-box;
display: flex;
align-items: center;
margin-bottom: .6667rem /* 50/75 */;
input{
flex: 1;
background: #EEEFF3;
padding: 0 .6667rem /* 50/75 */ 0 0;
box-sizing: border-box;
line-height: .9333rem /* 70/75 */;
outline: none;
border: none;
font-size: $sm-size;
}
input::-webkit-input-placeholder{
color: #CCCCCC;
font-size: $sm-size;
}
img{
width: .48rem /* 36/75 */;
height: .48rem /* 36/75 */;
}
}
.scroll-content{
flex: 1;
overflow: auto;
.user-list{
display: flex;
align-items: center;
// padding: 0 .4rem /* 30/75 */;
box-sizing: border-box;
margin-bottom: .5333rem /* 40/75 */;
.img-box{
width: .9867rem /* 74/75 */;
height: .9867rem /* 74/75 */;
border-radius:.1333rem /* 10/75 */;
background: #D9D9D9;
img{
display: block;
width: 100%;
height: 100%;
border-radius:.1333rem /* 10/75 */;
}
}
.name{
padding: 0 0 0 .2933rem /* 22/75 */;
box-sizing: border-box;
flex: 1;
// line-height: .9867rem /* 74/75 */;
font-size: $size30;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #4B4C4E;
span{
line-height: .6133rem /* 46/75 */;
color: #4B4C4E;
}
p{
font-size: $sm-size;
line-height: .4533rem /* 34/75 */;
color: #B6B7B8;
}
}
.button{
width: 1.2267rem /* 92/75 */;
height: .6667rem /* 50/75 */;
background: #EDEDED;
border-radius:.1333rem /* 10/75 */;
border: none;
outline: none;
font-size: $sm-size;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #999999;
box-sizing: border-box;
text-align: center;
line-height: .6667rem /* 50/75 */;
margin-right: .2133rem /* 16/75 */;
}
.remove-button{
width: 1.2267rem /* 92/75 */;
height: .6667rem /* 50/75 */;
background: #FFE9E4;
border-radius:.1333rem /* 10/75 */;
border: none;
outline: none;
font-size: $sm-size;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #FF6643;
box-sizing: border-box;
text-align: center;
line-height: .6667rem /* 50/75 */;
}
}
}
.scroll-content::-webkit-scrollbar {
display: none; /* Chrome Safari */
} // 超出部分隐藏滚动条
}
}
</style>
+340
View File
@@ -0,0 +1,340 @@
<template>
<div class='box'>
<header>
<div class="header-top">
<h1><img src="../assets/userhome/dingsheng.png" alt=""></h1>
<img class="h3" src="../assets/userhome/zhiengfangke.png" alt="">
<p>欢迎进入鼎盛无际智能访客系统</p>
</div>
<div class="plate-box">
<div @click="onClickGo('/apply')" class="list">
<h4>来访预约</h4>
<p>登记来访</p>
<img class="icon-arrowhead" src="../assets/userhome/youjiantou.png" alt="">
<img class="img" src="../assets/userhome/ren.png" alt="">
</div>
<div @click="onClickGo('/applyRecord')" class="list">
<h4>预约记录</h4>
<p>记录查询</p>
<img class="icon-arrowhead" src="../assets/userhome/youjiantou.png" alt="">
<img class="img" src="../assets/userhome/fang.png" alt="">
</div>
</div>
</header>
<div class="content">
<div class="contact-box">
<h3><span>联系我们 </span><p><b class="square"></b></p> </h3>
<div class="time">法定工作日 09:00-12:00 14:00-18:00</div>
<p @click="onClickCall" class="text-list">
<img src="../assets/userhome/dianhua.png" alt="">
<span>服务热线400040-1573</span>
</p>
<p class="text-list">
<img src="../assets/userhome/dingwei.png" alt="">
<span>地址四川省泸州市高新区众创中心12楼</span>
</p>
</div>
<div class="controls-box">
<img class="icon-img" src="../assets/caozuo.png" >
<p>操作手册</p>
<img class="icon-right" src="../assets/youjiantou.png" alt="">
<a href="https://www.yuque.com/zhoupangpang-m5aid/qp94nz/hhvxk757iolg8gah?singleDoc#"></a>
</div>
<!-- <div><button @click="onClickUpdate">修改token</button></div> -->
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import { useStore } from 'vuex'
import { rulesForm, resetForm, rulesPhone } from '@/com/index'
import { getWeChatCode, login } from '../api/user'
import { Toast, ImagePreview } from 'vant';
/* @data */
const router = useRouter()
const store = useStore()
const url = 'http://visitor.scdswj.cn/?code=071ucW200YtsQR1AUj3007Tabj2ucW26&state=STATE'
/* @setup */
// url.split('?')[1].split('&').map(item =>{
// if(item.split('=')[0] === 'code'){
// // code = item.split('=')[1]
// console.log(item.split('=')[1])
// alert(item.split('=')[1])
// }
// })
localStorage.setItem('redirect', '/userhome')
/* @method */
// const onClickUpdate = () =>{
// localStorage.setItem("userToken", 'res.data.authorization');
// }
const onClickGo = (url = '/userHome') =>{
router.push(url)
}
const onClickCall = () =>{
window.location.href = 'tel:400-040-1573'
}
const onClickMap = () =>{
window.location.href = ''
}
//判断是否是微信内置的浏览器
const isWXBrowserFun = ()=> {
return (
String(navigator.userAgent.toLowerCase().match(/MicroMessenger/i)) ===
"micromessenger"
);
}
//截取地址栏code
const getUrlCode = (name) =>{
if(window.location.href.indexOf(name) === -1) return
let code = ''
window.location.href.split('?')[1].split('&').map(item =>{
if(item.split('=')[0] === name){
code = item.split('=')[1]
}
})
return code
}
//访问微信地址,用来获取code
const weChatCode = () =>{
getWeChatCode().then(res =>{
window.location.href = res.data
})
}
//把code传递给后台接口,静默登录
const wxLogin = (code) =>{
login(code).then((res) => {
console.log(res)
if (res.code===200) {
localStorage.setItem("userToken", res.data.authorization);
Toast.success('登录成功!')
} else {
Toast.fail('登录失败!')
}
});
}
const isWXBrowser = isWXBrowserFun();
if (isWXBrowser) {
const userToken = localStorage.getItem('userToken')
if(!userToken){
let code = getUrlCode("code");
console.log(code)
if (code) {
wxLogin(code); //后台登录
} else {
weChatCode(); //微信授权
}
}
}else {
alert('请在微信中打开!')
}
</script>
<style lang='scss' scoped>
.box{
background-color: #F4F5F8;
header{
width: 100%;
height: 6.9333rem /* 520/75 */;
background: url('../assets/userhome/beijign.png') left top/100% 100% no-repeat;
.header-top{
width: 100%;
height: 4.1867rem /* 314/75 */;
background: url('../assets/userhome/topbeijing.png') left top/100% 100% no-repeat;
h1{
padding: .7467rem /* 56/75 */ .64rem /* 48/75 */;
box-sizing: border-box;
img{
width: 2.1333rem /* 160/75 */;
height: .5867rem /* 44/75 */;
}
}
.h3{
width: 3.12rem /* 234/75 */;
height: .7733rem /* 58/75 */;
margin: 0 0 .2133rem /* 16/75 */ .6667rem /* 50/75 */;
}
p{
line-height: .48rem /* 36/75 */;
font-size: .32rem /* 24/75 */;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #514E4B;
padding: 0 0 0 .7467rem /* 56/75 */;
box-sizing: border-box;
}
}
.plate-box{
width: 100%;
height: 3.3333rem /* 250/75 */;
padding: 0 .4267rem /* 32/75 */;
box-sizing: border-box;
display: flex;
justify-content: space-between;
.list{
position: relative;
flex: 1;
height: 3.3333rem /* 250/75 */;
background: linear-gradient(180deg, rgba(255,255,255,0) 0%, #FFFFFF 100%);
border-radius: .2667rem /* 20/75 */;
opacity: 1;
border: .0267rem /* 2/75 */ solid #FFFFFF;
h4{
padding: .4533rem /* 34/75 */ 0 .1333rem /* 10/75 */ .5067rem /* 38/75 */;
box-sizing: border-box;
line-height: .6133rem /* 46/75 */;
font-size: $size30;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 500;
color: #222222;
}
p{
line-height: .4533rem /* 34/75 */;
font-size: .2933rem /* 22/75 */;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #5F6583;
padding: 0 0 0 .5333rem /* 40/75 */;
box-sizing: border-box;
}
.icon-arrowhead{
width: .5333rem /* 40/75 */;
height: .5333rem /* 40/75 */;
margin: .7733rem /* 58/75 */ 0 0 .4533rem /* 34/75 */;
}
.img{
position: absolute;
bottom: 0;
right: .3733rem /* 28/75 */;
width: 1.5733rem /* 118/75 */;
height: 1.8133rem /* 136/75 */;
}
}
.list:first-child{
margin-right: .3467rem /* 26/75 */;
}
}
}
.content{
width: 100%;
padding: 1.12rem /* 84/75 */ .4rem /* 30/75 */ 0;
box-sizing: border-box;
.contact-box{
width: 100%;
height: 4.24rem /* 318/75 */;
// background: linear-gradient(261deg, #FFFFFF 0%, rgba(255,255,255,0) 100%);
background: url('../assets/userhome/ditu.png') left top/100% 100% no-repeat;
background-color: #FFFFFF;
border-radius: .2667rem /* 20/75 */ 1.0667rem /* 80/75 */ .2667rem /* 20/75 */ .2667rem /* 20/75 */;
h3{
position: relative;
// padding: .48rem /* 36/75 */ .5333rem /* 40/75 */ .2667rem /* 20/75 */;
box-sizing: border-box;
display: flex;
span{
display: block;
padding: .48rem /* 36/75 */ 0 0 .5333rem /* 40/75 */ ;
line-height: .6133rem /* 46/75 */;
font-size: $size30;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 500;
color: #000000;
position: relative;
z-index: 2;
}
p{
position: relative;
flex: 1;
.square{
position: absolute;
bottom: .0;
left: 0;
z-index: 1;
transform: translateX(-70%) translateY(-20%);
width: .24rem /* 18/75 */;
height: .24rem /* 18/75 */;
background: #30BF78;
border-radius: 0px 0px 0px 0px;
opacity: 1;
}
}
}
.time{
padding: .2667rem /* 20/75 */ 0 0 .5333rem /* 40/75 */;
box-sizing: border-box;
line-height: .4533rem /* 34/75 */;
font-size: .2933rem /* 22/75 */;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #5F6583;
margin-bottom: .56rem /* 42/75 */;
}
.text-list{
padding: 0 .64rem /* 48/75 */;
box-sizing: border-box;
line-height: .5333rem /* 40/75 */;
font-size: $size26;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 500;
color: #5F6583;
display: flex;
margin-bottom: .3733rem /* 28/75 */;
img{
width: .5333rem /* 40/75 */;
height: .5333rem /* 40/75 */;
margin-right: .2133rem /* 16/75 */;
}
}
}
.controls-box{
position: relative;
height: 1.3333rem /* 100/75 */;
background: rgba(255,255,255,0.8);
border-radius:.2667rem /* 20/75 */;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 .24rem /* 18/75 */ 0 .4533rem /* 34/75 */;
box-sizing: border-box;
margin-top: .32rem /* 24/75 */ ;
.icon-img{
width: .8533rem /* 64/75 */;
height: .8533rem /* 64/75 */;
}
p{
flex: 1;
font-size: $lg-size;
font-family: Inter, Inter;
font-weight: 500;
color: #5F6583;
line-height: .5067rem /* 38/75 */;
padding: 0 0 0 .24rem /* 18/75 */;
box-sizing: border-box;
}
.icon-right{
width: .64rem /* 48/75 */;
height: .64rem /* 48/75 */;
}
a{
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
}
}
}
</style>
+25
View File
@@ -0,0 +1,25 @@
<template>
<div class='box'>
<Successmodel />
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import { useStore } from 'vuex'
import { rulesForm } from '@/com/index'
import Successmodel from '@/components/success.vue'
/* @data */
const router = useRouter()
const store = useStore()
/* @setup */
/* @method */
</script>
<style lang='scss' scoped>
</style>
+192
View File
@@ -0,0 +1,192 @@
<template>
<div class='box'>
<header class="tabbar">
<div @click="onClickTabbar(item)" v-for="item in stateEnum" class="tabbar-li" :key="item.status" :class="query.status == item.status ? 'active' : ''">
{{ item.statusName }}
<img v-if="query.status == item.status" src="../assets/maomaochong.png" alt="">
</div>
</header>
<div v-if="recordArr.length" class="content">
<div @click="onClcikItem(item)" v-for="item in recordArr" class="wait-list" :key="item.id">
<h5>被拜访人{{ item.visitingName }}</h5>
<h5>拜访时间{{ item.hours }}</h5>
<div class="apply-box">
<p><span>访客姓名</span><span>{{ item.name }}</span></p>
<p><span>联系方式</span><span>{{ item.phone }}</span></p>
<p><span>随行人数</span><span>{{ item.visitingNum }} </span></p>
<p><span>来访事由</span><span>{{ item.reason }}</span></p>
</div>
<!-- refuse 拒绝 pass 通过 状态 -->
<div class="status" :class="stateEnum[item.status].className">{{ stateEnum[item.status].statusName }}</div>
</div>
</div>
<div v-if="isLoading && recordArr.length == 0" class="content">
<div class="no-have">
暂无数据
</div>
</div>
</div>
</template>
<script setup>
import { ref, reactive } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { useStore } from 'vuex'
import dd from 'dingtalk-jsapi'
import { Toast, ImagePreview } from 'vant';
import { rulesForm, resetForm, rulesPhone } from '@/com/index'
import { getToken, getVisitorRecord } from '@/api/admin.js'
/* @data */
const router = useRouter()
const route = useRoute()
const store = useStore()
const stateEnum = store.state.stateEnum
const recordArr = ref([])
const isLoading = ref(false)
const query = reactive({
status: 0,
size: -1,
current: 1,
})
console.log(route)
/* @setup */
/* @method */
const onClcikItem = (item) =>{
// window.location.href = item.instanceUrl
// window.open(item.instanceUrl, '审批详情');
dd.openLink({
url: item.instanceUrl,
success: () => {},
fail: () => {},
complete: () => {},
});
}
const onClickTabbar = (item) =>{
if(query.status != item.status){
query.status = item.status
recordArr.value = []
visitorList()
}
}
const visitorList = () =>{
Toast.loading({ duration: 0, forbidClick: true, message: '加载中', });
getVisitorRecord(query).then(res =>{
isLoading.value = true
recordArr.value = res.data
}).catch(err =>{
isLoading.value = true
})
}
visitorList()
</script>
<style lang='scss' scoped>
.box{
background: #F9FBFC;;
display: flex;
flex-direction: column;
.tabbar{
height: 1.8933rem /* 142/75 */;
line-height: .6133rem /* 46/75 */;
display: flex;
font-size: $md-size;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #999999;
padding: .56rem /* 42/75 */ .4rem /* 30/75 */ 0;
box-sizing: border-box;
.tabbar-li{
position: relative;
flex: 1;
text-align: center;
img{
position: absolute;
left: 50%;
transform: translateX(-50%);
bottom: .48rem /* 36/75 */;
width: .6133rem /* 46/75 */;
height: .2667rem /* 20/75 */;
}
}
.active{
font-size: $size30;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 500;
color: #222222;
}
}
.content{
width: 100%;
flex: 1;
padding: 0 .4rem /* 30/75 */;
box-sizing: border-box;
overflow: auto;
.wait-list{
position: relative;
width: 100%;
// height: 470px;
background: #FFFFFF;
border-radius: .2667rem /* 20/75 */;
padding: .48rem /* 36/75 */;
box-sizing: border-box;
margin-bottom: .2667rem /* 20/75 */;
h5{
height: .6667rem /* 50/75 */;
font-size: $md-size;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #222222;
}
.apply-box{
width: 100%;
padding: .4rem /* 30/75 */ .3733rem /* 28/75 */ .5067rem /* 38/75 */;
box-sizing: border-box;
border-radius: .1333rem /* 10/75 */;
font-size: $md-size;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #999999;
line-height: .72rem /* 54/75 */;
background: #F5F7F9;
span:last-child{
color: #5F6583;
}
}
.status{
position: absolute;
top: 0;
right: 0;
width: 1.52rem /* 114/75 */;
height: .6667rem /* 50/75 */;
line-height: .6667rem /* 50/75 */;
background: #FFE9E4;
border-radius: 0px .2667rem /* 20/75 */ 0px .1333rem /* 10/75 */;
color: #FF6643;
font-size: $sm-size;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #FF6643;
text-align: center;
}
.refuse{
color: #5F6583;
background: #EAEEF6;
}
.pass{
color: #30BF78;
background: #E7F8F5;
}
}
}
}
</style>
+56
View File
@@ -0,0 +1,56 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from "path"
import styleImport, { VantResolve } from 'vite-plugin-style-import';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
styleImport({
resolves: [VantResolve()],
libs: [
{
libraryName: 'vant',
esModule: true,
resolveStyle: (name) => `../es/${name}/style`, //https://blog.csdn.net/yunchong_zhao/article/details/123552423
},
],
}),
],
server: {
host: '0.0.0.0',//设置地址:http://localhost
port: 4000, // 设置服务启动端口号
// open: true, // 设置服务启动时是否自动打开浏览器
// cors: true, // 允许跨域
// proxy: {
// '/api': {
// target: 'http://dining.lzyyj.com/prod-api',//代理的地址
// changeOrigin: true,
// // 路径重写 pathRewrite无效使用rewrite
// // pathRewrite: { '/api': '' // 用'/api'代替target里面的地址 },
// rewrite: path => path.replace(/^\/api/, '')
// }
// }
},
resolve:{
alias:{
"@": resolve(__dirname, "src")
}
},
css: {
// css预处理器
preprocessorOptions: {
scss: {
charset: false,
additionalData: '@import "./src/assets/style/index.scss";',
},
},
},
})
+823
View File
@@ -0,0 +1,823 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@babel/parser@^7.23.0":
version "7.23.3"
resolved "https://registry.npmmirror.com/@babel/parser/-/parser-7.23.3.tgz#0ce0be31a4ca4f1884b5786057cadcb6c3be58f9"
integrity sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==
"@esbuild/linux-loong64@0.14.54":
version "0.14.54"
resolved "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz#de2a4be678bd4d0d1ffbb86e6de779cde5999028"
integrity sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==
"@jridgewell/sourcemap-codec@^1.4.15":
version "1.4.15"
resolved "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32"
integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
"@rollup/pluginutils@^4.1.2":
version "4.2.1"
resolved "https://registry.npmmirror.com/@rollup/pluginutils/-/pluginutils-4.2.1.tgz#e6c6c3aba0744edce3fb2074922d3776c0af2a6d"
integrity sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==
dependencies:
estree-walker "^2.0.1"
picomatch "^2.2.2"
"@vant/icons@^1.8.0":
version "1.8.0"
resolved "https://registry.npmmirror.com/@vant/icons/-/icons-1.8.0.tgz#36b13f2e628b533f6523a93a168cf02f07056674"
integrity sha512-sKfEUo2/CkQFuERxvkuF6mGQZDKu3IQdj5rV9Fm0weJXtchDSSQ+zt8qPCNUEhh9Y8shy5PzxbvAfOOkCwlCXg==
"@vant/popperjs@^1.2.1":
version "1.3.0"
resolved "https://registry.npmmirror.com/@vant/popperjs/-/popperjs-1.3.0.tgz#e0eff017124b5b2352ef3b36a6df06277f4400f2"
integrity sha512-hB+czUG+aHtjhaEmCJDuXOep0YTZjdlRR+4MSmIFnkCQIxJaXLQdSsR90XWvAI2yvKUI7TCGqR8pQg2RtvkMHw==
"@vant/use@^1.4.2":
version "1.6.0"
resolved "https://registry.npmmirror.com/@vant/use/-/use-1.6.0.tgz#237df3091617255519552ca311ffdfea9de59001"
integrity sha512-PHHxeAASgiOpSmMjceweIrv2AxDZIkWXyaczksMoWvKV2YAYEhoizRuk/xFnKF+emUIi46TsQ+rvlm/t2BBCfA==
"@vitejs/plugin-vue@^2.3.0":
version "2.3.4"
resolved "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-2.3.4.tgz#966a6279060eb2d9d1a02ea1a331af071afdcf9e"
integrity sha512-IfFNbtkbIm36O9KB8QodlwwYvTEsJb4Lll4c2IwB3VHc2gie2mSPtSzL0eYay7X2jd/2WX02FjSGTWR6OPr/zg==
"@vue/compiler-core@3.3.8":
version "3.3.8"
resolved "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.3.8.tgz#301bb60d0245265a88ed5b30e200fbf223acb313"
integrity sha512-hN/NNBUECw8SusQvDSqqcVv6gWq8L6iAktUR0UF3vGu2OhzRqcOiAno0FmBJWwxhYEXRlQJT5XnoKsVq1WZx4g==
dependencies:
"@babel/parser" "^7.23.0"
"@vue/shared" "3.3.8"
estree-walker "^2.0.2"
source-map-js "^1.0.2"
"@vue/compiler-dom@3.3.8":
version "3.3.8"
resolved "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.3.8.tgz#09d832514b9b8d9415a3816b065d69dbefcc7e9b"
integrity sha512-+PPtv+p/nWDd0AvJu3w8HS0RIm/C6VGBIRe24b9hSyNWOAPEUosFZ5diwawwP8ip5sJ8n0Pe87TNNNHnvjs0FQ==
dependencies:
"@vue/compiler-core" "3.3.8"
"@vue/shared" "3.3.8"
"@vue/compiler-sfc@3.3.8":
version "3.3.8"
resolved "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.3.8.tgz#40b18e48aa00260950964d1d72157668521be0e1"
integrity sha512-WMzbUrlTjfYF8joyT84HfwwXo+8WPALuPxhy+BZ6R4Aafls+jDBnSz8PDz60uFhuqFbl3HxRfxvDzrUf3THwpA==
dependencies:
"@babel/parser" "^7.23.0"
"@vue/compiler-core" "3.3.8"
"@vue/compiler-dom" "3.3.8"
"@vue/compiler-ssr" "3.3.8"
"@vue/reactivity-transform" "3.3.8"
"@vue/shared" "3.3.8"
estree-walker "^2.0.2"
magic-string "^0.30.5"
postcss "^8.4.31"
source-map-js "^1.0.2"
"@vue/compiler-ssr@3.3.8":
version "3.3.8"
resolved "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.3.8.tgz#136eed54411e4694815d961048a237191063fbce"
integrity sha512-hXCqQL/15kMVDBuoBYpUnSYT8doDNwsjvm3jTefnXr+ytn294ySnT8NlsFHmTgKNjwpuFy7XVV8yTeLtNl/P6w==
dependencies:
"@vue/compiler-dom" "3.3.8"
"@vue/shared" "3.3.8"
"@vue/devtools-api@^6.0.0-beta.11", "@vue/devtools-api@^6.5.0":
version "6.5.1"
resolved "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.5.1.tgz#7f71f31e40973eeee65b9a64382b13593fdbd697"
integrity sha512-+KpckaAQyfbvshdDW5xQylLni1asvNSGme1JFs8I1+/H5pHEhqUKMEQD/qn3Nx5+/nycBq11qAEi8lk+LXI2dA==
"@vue/reactivity-transform@3.3.8":
version "3.3.8"
resolved "https://registry.npmmirror.com/@vue/reactivity-transform/-/reactivity-transform-3.3.8.tgz#6d07649013b0be5c670f0ab6cc7ddd3150ad03f2"
integrity sha512-49CvBzmZNtcHua0XJ7GdGifM8GOXoUMOX4dD40Y5DxI3R8OUhMlvf2nvgUAcPxaXiV5MQQ1Nwy09ADpnLQUqRw==
dependencies:
"@babel/parser" "^7.23.0"
"@vue/compiler-core" "3.3.8"
"@vue/shared" "3.3.8"
estree-walker "^2.0.2"
magic-string "^0.30.5"
"@vue/reactivity@3.3.8":
version "3.3.8"
resolved "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.3.8.tgz#cce8a03a3fd3539c3eeda53e277ba365d160dd4d"
integrity sha512-ctLWitmFBu6mtddPyOKpHg8+5ahouoTCRtmAHZAXmolDtuZXfjL2T3OJ6DL6ezBPQB1SmMnpzjiWjCiMYmpIuw==
dependencies:
"@vue/shared" "3.3.8"
"@vue/runtime-core@3.3.8":
version "3.3.8"
resolved "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.3.8.tgz#fba5a632cbf2b5d29e171489570149cb6975dcdb"
integrity sha512-qurzOlb6q26KWQ/8IShHkMDOuJkQnQcTIp1sdP4I9MbCf9FJeGVRXJFr2mF+6bXh/3Zjr9TDgURXrsCr9bfjUw==
dependencies:
"@vue/reactivity" "3.3.8"
"@vue/shared" "3.3.8"
"@vue/runtime-dom@3.3.8":
version "3.3.8"
resolved "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.3.8.tgz#e2d7aa795cf50914dda9a951887765a594b38af4"
integrity sha512-Noy5yM5UIf9UeFoowBVgghyGGPIDPy1Qlqt0yVsUdAVbqI8eeMSsTqBtauaEoT2UFXUk5S64aWVNJN4MJ2vRdA==
dependencies:
"@vue/runtime-core" "3.3.8"
"@vue/shared" "3.3.8"
csstype "^3.1.2"
"@vue/server-renderer@3.3.8":
version "3.3.8"
resolved "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.3.8.tgz#9b1779010e75783edeed8fcfb97d9c95fc3ac5d2"
integrity sha512-zVCUw7RFskvPuNlPn/8xISbrf0zTWsTSdYTsUTN1ERGGZGVnRxM2QZ3x1OR32+vwkkCm0IW6HmJ49IsPm7ilLg==
dependencies:
"@vue/compiler-ssr" "3.3.8"
"@vue/shared" "3.3.8"
"@vue/shared@3.3.8":
version "3.3.8"
resolved "https://registry.npmmirror.com/@vue/shared/-/shared-3.3.8.tgz#f044942142e1d3a395f24132e6203a784838542d"
integrity sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==
anymatch@~3.1.2:
version "3.1.3"
resolved "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e"
integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==
dependencies:
normalize-path "^3.0.0"
picomatch "^2.0.4"
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
axios@^1.6.2:
version "1.6.2"
resolved "https://registry.npmmirror.com/axios/-/axios-1.6.2.tgz#de67d42c755b571d3e698df1b6504cde9b0ee9f2"
integrity sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==
dependencies:
follow-redirects "^1.15.0"
form-data "^4.0.0"
proxy-from-env "^1.1.0"
base64-arraybuffer@^1.0.2:
version "1.0.2"
resolved "https://registry.npmmirror.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz#1c37589a7c4b0746e34bd1feb951da2df01c1bdc"
integrity sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==
binary-extensions@^2.0.0:
version "2.2.0"
resolved "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
braces@~3.0.2:
version "3.0.2"
resolved "https://registry.npmmirror.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
dependencies:
fill-range "^7.0.1"
camel-case@^4.1.2:
version "4.1.2"
resolved "https://registry.npmmirror.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a"
integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==
dependencies:
pascal-case "^3.1.2"
tslib "^2.0.3"
capital-case@^1.0.4:
version "1.0.4"
resolved "https://registry.npmmirror.com/capital-case/-/capital-case-1.0.4.tgz#9d130292353c9249f6b00fa5852bee38a717e669"
integrity sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==
dependencies:
no-case "^3.0.4"
tslib "^2.0.3"
upper-case-first "^2.0.2"
change-case@^4.1.2:
version "4.1.2"
resolved "https://registry.npmmirror.com/change-case/-/change-case-4.1.2.tgz#fedfc5f136045e2398c0410ee441f95704641e12"
integrity sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==
dependencies:
camel-case "^4.1.2"
capital-case "^1.0.4"
constant-case "^3.0.4"
dot-case "^3.0.4"
header-case "^2.0.4"
no-case "^3.0.4"
param-case "^3.0.4"
pascal-case "^3.1.2"
path-case "^3.0.4"
sentence-case "^3.0.4"
snake-case "^3.0.4"
tslib "^2.0.3"
"chokidar@>=3.0.0 <4.0.0":
version "3.5.3"
resolved "https://registry.npmmirror.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
dependencies:
anymatch "~3.1.2"
braces "~3.0.2"
glob-parent "~5.1.2"
is-binary-path "~2.1.0"
is-glob "~4.0.1"
normalize-path "~3.0.0"
readdirp "~3.6.0"
optionalDependencies:
fsevents "~2.3.2"
combined-stream@^1.0.8:
version "1.0.8"
resolved "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
dependencies:
delayed-stream "~1.0.0"
constant-case@^3.0.4:
version "3.0.4"
resolved "https://registry.npmmirror.com/constant-case/-/constant-case-3.0.4.tgz#3b84a9aeaf4cf31ec45e6bf5de91bdfb0589faf1"
integrity sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==
dependencies:
no-case "^3.0.4"
tslib "^2.0.3"
upper-case "^2.0.2"
css-line-break@^2.1.0:
version "2.1.0"
resolved "https://registry.npmmirror.com/css-line-break/-/css-line-break-2.1.0.tgz#bfef660dfa6f5397ea54116bb3cb4873edbc4fa0"
integrity sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==
dependencies:
utrie "^1.0.2"
csstype@^3.1.2:
version "3.1.2"
resolved "https://registry.npmmirror.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b"
integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==
debug@^4.3.3:
version "4.3.4"
resolved "https://registry.npmmirror.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
dependencies:
ms "2.1.2"
delayed-stream@~1.0.0:
version "1.0.0"
resolved "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
dingtalk-jsapi@^3.0.29:
version "3.0.29"
resolved "https://registry.npmmirror.com/dingtalk-jsapi/-/dingtalk-jsapi-3.0.29.tgz#ecd56f9a41105c7fb64af553713abed5ac76ae1c"
integrity sha512-EWIvppWaLruMggjP85KRpM5TIDSfDkSgS4XOljD0Wf8A26wRxZw3oZIxKFaXiusSJuZsIJakj5FM178hpYi2SA==
dependencies:
promise-polyfill "^7.1.0"
dot-case@^3.0.4:
version "3.0.4"
resolved "https://registry.npmmirror.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751"
integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==
dependencies:
no-case "^3.0.4"
tslib "^2.0.3"
es-module-lexer@^0.9.3:
version "0.9.3"
resolved "https://registry.npmmirror.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19"
integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==
esbuild-android-64@0.14.54:
version "0.14.54"
resolved "https://registry.npmmirror.com/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz#505f41832884313bbaffb27704b8bcaa2d8616be"
integrity sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==
esbuild-android-arm64@0.14.54:
version "0.14.54"
resolved "https://registry.npmmirror.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.54.tgz#8ce69d7caba49646e009968fe5754a21a9871771"
integrity sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==
esbuild-darwin-64@0.14.54:
version "0.14.54"
resolved "https://registry.npmmirror.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.54.tgz#24ba67b9a8cb890a3c08d9018f887cc221cdda25"
integrity sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==
esbuild-darwin-arm64@0.14.54:
version "0.14.54"
resolved "https://registry.npmmirror.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.54.tgz#3f7cdb78888ee05e488d250a2bdaab1fa671bf73"
integrity sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw==
esbuild-freebsd-64@0.14.54:
version "0.14.54"
resolved "https://registry.npmmirror.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.54.tgz#09250f997a56ed4650f3e1979c905ffc40bbe94d"
integrity sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==
esbuild-freebsd-arm64@0.14.54:
version "0.14.54"
resolved "https://registry.npmmirror.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.54.tgz#bafb46ed04fc5f97cbdb016d86947a79579f8e48"
integrity sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==
esbuild-linux-32@0.14.54:
version "0.14.54"
resolved "https://registry.npmmirror.com/esbuild-linux-32/-/esbuild-linux-32-0.14.54.tgz#e2a8c4a8efdc355405325033fcebeb941f781fe5"
integrity sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==
esbuild-linux-64@0.14.54:
version "0.14.54"
resolved "https://registry.npmmirror.com/esbuild-linux-64/-/esbuild-linux-64-0.14.54.tgz#de5fdba1c95666cf72369f52b40b03be71226652"
integrity sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==
esbuild-linux-arm64@0.14.54:
version "0.14.54"
resolved "https://registry.npmmirror.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.54.tgz#dae4cd42ae9787468b6a5c158da4c84e83b0ce8b"
integrity sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==
esbuild-linux-arm@0.14.54:
version "0.14.54"
resolved "https://registry.npmmirror.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.54.tgz#a2c1dff6d0f21dbe8fc6998a122675533ddfcd59"
integrity sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==
esbuild-linux-mips64le@0.14.54:
version "0.14.54"
resolved "https://registry.npmmirror.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.54.tgz#d9918e9e4cb972f8d6dae8e8655bf9ee131eda34"
integrity sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==
esbuild-linux-ppc64le@0.14.54:
version "0.14.54"
resolved "https://registry.npmmirror.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.54.tgz#3f9a0f6d41073fb1a640680845c7de52995f137e"
integrity sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==
esbuild-linux-riscv64@0.14.54:
version "0.14.54"
resolved "https://registry.npmmirror.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.54.tgz#618853c028178a61837bc799d2013d4695e451c8"
integrity sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==
esbuild-linux-s390x@0.14.54:
version "0.14.54"
resolved "https://registry.npmmirror.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.54.tgz#d1885c4c5a76bbb5a0fe182e2c8c60eb9e29f2a6"
integrity sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==
esbuild-netbsd-64@0.14.54:
version "0.14.54"
resolved "https://registry.npmmirror.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.54.tgz#69ae917a2ff241b7df1dbf22baf04bd330349e81"
integrity sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==
esbuild-openbsd-64@0.14.54:
version "0.14.54"
resolved "https://registry.npmmirror.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.54.tgz#db4c8495287a350a6790de22edea247a57c5d47b"
integrity sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==
esbuild-sunos-64@0.14.54:
version "0.14.54"
resolved "https://registry.npmmirror.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.54.tgz#54287ee3da73d3844b721c21bc80c1dc7e1bf7da"
integrity sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==
esbuild-windows-32@0.14.54:
version "0.14.54"
resolved "https://registry.npmmirror.com/esbuild-windows-32/-/esbuild-windows-32-0.14.54.tgz#f8aaf9a5667630b40f0fb3aa37bf01bbd340ce31"
integrity sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==
esbuild-windows-64@0.14.54:
version "0.14.54"
resolved "https://registry.npmmirror.com/esbuild-windows-64/-/esbuild-windows-64-0.14.54.tgz#bf54b51bd3e9b0f1886ffdb224a4176031ea0af4"
integrity sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ==
esbuild-windows-arm64@0.14.54:
version "0.14.54"
resolved "https://registry.npmmirror.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.54.tgz#937d15675a15e4b0e4fafdbaa3a01a776a2be982"
integrity sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg==
esbuild@^0.14.27:
version "0.14.54"
resolved "https://registry.npmmirror.com/esbuild/-/esbuild-0.14.54.tgz#8b44dcf2b0f1a66fc22459943dccf477535e9aa2"
integrity sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA==
optionalDependencies:
"@esbuild/linux-loong64" "0.14.54"
esbuild-android-64 "0.14.54"
esbuild-android-arm64 "0.14.54"
esbuild-darwin-64 "0.14.54"
esbuild-darwin-arm64 "0.14.54"
esbuild-freebsd-64 "0.14.54"
esbuild-freebsd-arm64 "0.14.54"
esbuild-linux-32 "0.14.54"
esbuild-linux-64 "0.14.54"
esbuild-linux-arm "0.14.54"
esbuild-linux-arm64 "0.14.54"
esbuild-linux-mips64le "0.14.54"
esbuild-linux-ppc64le "0.14.54"
esbuild-linux-riscv64 "0.14.54"
esbuild-linux-s390x "0.14.54"
esbuild-netbsd-64 "0.14.54"
esbuild-openbsd-64 "0.14.54"
esbuild-sunos-64 "0.14.54"
esbuild-windows-32 "0.14.54"
esbuild-windows-64 "0.14.54"
esbuild-windows-arm64 "0.14.54"
estree-walker@^2.0.1, estree-walker@^2.0.2:
version "2.0.2"
resolved "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
fill-range@^7.0.1:
version "7.0.1"
resolved "https://registry.npmmirror.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
dependencies:
to-regex-range "^5.0.1"
follow-redirects@^1.15.0:
version "1.15.3"
resolved "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a"
integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==
form-data@^4.0.0:
version "4.0.0"
resolved "https://registry.npmmirror.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.8"
mime-types "^2.1.12"
fs-extra@^10.0.0:
version "10.1.0"
resolved "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf"
integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==
dependencies:
graceful-fs "^4.2.0"
jsonfile "^6.0.1"
universalify "^2.0.0"
fsevents@~2.3.2:
version "2.3.3"
resolved "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
function-bind@^1.1.2:
version "1.1.2"
resolved "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
glob-parent@~5.1.2:
version "5.1.2"
resolved "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
dependencies:
is-glob "^4.0.1"
graceful-fs@^4.1.6, graceful-fs@^4.2.0:
version "4.2.11"
resolved "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
hasown@^2.0.0:
version "2.0.0"
resolved "https://registry.npmmirror.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c"
integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==
dependencies:
function-bind "^1.1.2"
header-case@^2.0.4:
version "2.0.4"
resolved "https://registry.npmmirror.com/header-case/-/header-case-2.0.4.tgz#5a42e63b55177349cf405beb8d775acabb92c063"
integrity sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==
dependencies:
capital-case "^1.0.4"
tslib "^2.0.3"
html2canvas@^1.4.1:
version "1.4.1"
resolved "https://registry.npmmirror.com/html2canvas/-/html2canvas-1.4.1.tgz#7cef1888311b5011d507794a066041b14669a543"
integrity sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==
dependencies:
css-line-break "^2.1.0"
text-segmentation "^1.0.3"
immutable@^4.0.0:
version "4.3.4"
resolved "https://registry.npmmirror.com/immutable/-/immutable-4.3.4.tgz#2e07b33837b4bb7662f288c244d1ced1ef65a78f"
integrity sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==
is-binary-path@~2.1.0:
version "2.1.0"
resolved "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
dependencies:
binary-extensions "^2.0.0"
is-core-module@^2.13.0:
version "2.13.1"
resolved "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384"
integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==
dependencies:
hasown "^2.0.0"
is-extglob@^2.1.1:
version "2.1.1"
resolved "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
is-glob@^4.0.1, is-glob@~4.0.1:
version "4.0.3"
resolved "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
dependencies:
is-extglob "^2.1.1"
is-number@^7.0.0:
version "7.0.0"
resolved "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
jsonfile@^6.0.1:
version "6.1.0"
resolved "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae"
integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==
dependencies:
universalify "^2.0.0"
optionalDependencies:
graceful-fs "^4.1.6"
lower-case@^2.0.2:
version "2.0.2"
resolved "https://registry.npmmirror.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28"
integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==
dependencies:
tslib "^2.0.3"
magic-string@^0.25.7:
version "0.25.9"
resolved "https://registry.npmmirror.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c"
integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==
dependencies:
sourcemap-codec "^1.4.8"
magic-string@^0.30.5:
version "0.30.5"
resolved "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.5.tgz#1994d980bd1c8835dc6e78db7cbd4ae4f24746f9"
integrity sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==
dependencies:
"@jridgewell/sourcemap-codec" "^1.4.15"
mime-db@1.52.0:
version "1.52.0"
resolved "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
mime-types@^2.1.12:
version "2.1.35"
resolved "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
dependencies:
mime-db "1.52.0"
ms@2.1.2:
version "2.1.2"
resolved "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
nanoid@^3.3.6:
version "3.3.7"
resolved "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8"
integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==
no-case@^3.0.4:
version "3.0.4"
resolved "https://registry.npmmirror.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d"
integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==
dependencies:
lower-case "^2.0.2"
tslib "^2.0.3"
normalize-path@^3.0.0, normalize-path@~3.0.0:
version "3.0.0"
resolved "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
param-case@^3.0.4:
version "3.0.4"
resolved "https://registry.npmmirror.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5"
integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==
dependencies:
dot-case "^3.0.4"
tslib "^2.0.3"
pascal-case@^3.1.2:
version "3.1.2"
resolved "https://registry.npmmirror.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb"
integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==
dependencies:
no-case "^3.0.4"
tslib "^2.0.3"
path-case@^3.0.4:
version "3.0.4"
resolved "https://registry.npmmirror.com/path-case/-/path-case-3.0.4.tgz#9168645334eb942658375c56f80b4c0cb5f82c6f"
integrity sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==
dependencies:
dot-case "^3.0.4"
tslib "^2.0.3"
path-parse@^1.0.7:
version "1.0.7"
resolved "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
picocolors@^1.0.0:
version "1.0.0"
resolved "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2:
version "2.3.1"
resolved "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
postcss@^8.4.13, postcss@^8.4.31:
version "8.4.31"
resolved "https://registry.npmmirror.com/postcss/-/postcss-8.4.31.tgz#92b451050a9f914da6755af352bdc0192508656d"
integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==
dependencies:
nanoid "^3.3.6"
picocolors "^1.0.0"
source-map-js "^1.0.2"
promise-polyfill@^7.1.0:
version "7.1.2"
resolved "https://registry.npmmirror.com/promise-polyfill/-/promise-polyfill-7.1.2.tgz#ab05301d8c28536301622d69227632269a70ca3b"
integrity sha512-FuEc12/eKqqoRYIGBrUptCBRhobL19PS2U31vMNTfyck1FxPyMfgsXyW4Mav85y/ZN1hop3hOwRlUDok23oYfQ==
proxy-from-env@^1.1.0:
version "1.1.0"
resolved "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
readdirp@~3.6.0:
version "3.6.0"
resolved "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==
dependencies:
picomatch "^2.2.1"
resolve@^1.22.0:
version "1.22.8"
resolved "https://registry.npmmirror.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d"
integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==
dependencies:
is-core-module "^2.13.0"
path-parse "^1.0.7"
supports-preserve-symlinks-flag "^1.0.0"
"rollup@>=2.59.0 <2.78.0":
version "2.77.3"
resolved "https://registry.npmmirror.com/rollup/-/rollup-2.77.3.tgz#8f00418d3a2740036e15deb653bed1a90ee0cc12"
integrity sha512-/qxNTG7FbmefJWoeeYJFbHehJ2HNWnjkAFRKzWN/45eNBBF/r8lo992CwcJXEzyVxs5FmfId+vTSTQDb+bxA+g==
optionalDependencies:
fsevents "~2.3.2"
sass@^1.56.1:
version "1.69.5"
resolved "https://registry.npmmirror.com/sass/-/sass-1.69.5.tgz#23e18d1c757a35f2e52cc81871060b9ad653dfde"
integrity sha512-qg2+UCJibLr2LCVOt3OlPhr/dqVHWOa9XtZf2OjbLs/T4VPSJ00udtgJxH3neXZm+QqX8B+3cU7RaLqp1iVfcQ==
dependencies:
chokidar ">=3.0.0 <4.0.0"
immutable "^4.0.0"
source-map-js ">=0.6.2 <2.0.0"
sentence-case@^3.0.4:
version "3.0.4"
resolved "https://registry.npmmirror.com/sentence-case/-/sentence-case-3.0.4.tgz#3645a7b8c117c787fde8702056225bb62a45131f"
integrity sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==
dependencies:
no-case "^3.0.4"
tslib "^2.0.3"
upper-case-first "^2.0.2"
snake-case@^3.0.4:
version "3.0.4"
resolved "https://registry.npmmirror.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c"
integrity sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==
dependencies:
dot-case "^3.0.4"
tslib "^2.0.3"
"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.2:
version "1.0.2"
resolved "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
sourcemap-codec@^1.4.8:
version "1.4.8"
resolved "https://registry.npmmirror.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==
supports-preserve-symlinks-flag@^1.0.0:
version "1.0.0"
resolved "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
text-segmentation@^1.0.3:
version "1.0.3"
resolved "https://registry.npmmirror.com/text-segmentation/-/text-segmentation-1.0.3.tgz#52a388159efffe746b24a63ba311b6ac9f2d7943"
integrity sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==
dependencies:
utrie "^1.0.2"
to-regex-range@^5.0.1:
version "5.0.1"
resolved "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
dependencies:
is-number "^7.0.0"
tslib@^2.0.3:
version "2.6.2"
resolved "https://registry.npmmirror.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
universalify@^2.0.0:
version "2.0.1"
resolved "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d"
integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==
upper-case-first@^2.0.2:
version "2.0.2"
resolved "https://registry.npmmirror.com/upper-case-first/-/upper-case-first-2.0.2.tgz#992c3273f882abd19d1e02894cc147117f844324"
integrity sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==
dependencies:
tslib "^2.0.3"
upper-case@^2.0.2:
version "2.0.2"
resolved "https://registry.npmmirror.com/upper-case/-/upper-case-2.0.2.tgz#d89810823faab1df1549b7d97a76f8662bae6f7a"
integrity sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==
dependencies:
tslib "^2.0.3"
utrie@^1.0.2:
version "1.0.2"
resolved "https://registry.npmmirror.com/utrie/-/utrie-1.0.2.tgz#d42fe44de9bc0119c25de7f564a6ed1b2c87a645"
integrity sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==
dependencies:
base64-arraybuffer "^1.0.2"
vant@^3.4.3:
version "3.6.12"
resolved "https://registry.npmmirror.com/vant/-/vant-3.6.12.tgz#a46045c74961744f27d5a6ffbc3bed592f0f4d0b"
integrity sha512-fLzwhpV0ZPQqxrTx6RU8mJVUqP7DSkpdXNeByKubp+O9vKYGcWRX9wFdEwApyy7qLZLLu+rU1Jw52d6lktPL4w==
dependencies:
"@vant/icons" "^1.8.0"
"@vant/popperjs" "^1.2.1"
"@vant/use" "^1.4.2"
vite-plugin-style-import@^1.4.1:
version "1.4.1"
resolved "https://registry.npmmirror.com/vite-plugin-style-import/-/vite-plugin-style-import-1.4.1.tgz#47a66920cce5484640f2faacd73190aac0b91b94"
integrity sha512-lJCRvm7+So0hHdnSJiJPg9gD5mxtL6YY0jmhEph+k7ArpsyvqOh6han2kG5htbWWDZxHkUN9d1BuTFL//yCLLQ==
dependencies:
"@rollup/pluginutils" "^4.1.2"
change-case "^4.1.2"
debug "^4.3.3"
es-module-lexer "^0.9.3"
fs-extra "^10.0.0"
magic-string "^0.25.7"
vite@^2.9.0:
version "2.9.16"
resolved "https://registry.npmmirror.com/vite/-/vite-2.9.16.tgz#daf7ba50f5cc37a7bf51b118ba06bc36e97898e9"
integrity sha512-X+6q8KPyeuBvTQV8AVSnKDvXoBMnTx8zxh54sOwmmuOdxkjMmEJXH2UEchA+vTMps1xw9vL64uwJOWryULg7nA==
dependencies:
esbuild "^0.14.27"
postcss "^8.4.13"
resolve "^1.22.0"
rollup ">=2.59.0 <2.78.0"
optionalDependencies:
fsevents "~2.3.2"
vue-router@^4.0.14:
version "4.2.5"
resolved "https://registry.npmmirror.com/vue-router/-/vue-router-4.2.5.tgz#b9e3e08f1bd9ea363fdd173032620bc50cf0e98a"
integrity sha512-DIUpKcyg4+PTQKfFPX88UWhlagBEBEfJ5A8XDXRJLUnZOvcpMF8o/dnL90vpVkGaPbjvXazV/rC1qBKrZlFugw==
dependencies:
"@vue/devtools-api" "^6.5.0"
vue@^3.2.25:
version "3.3.8"
resolved "https://registry.npmmirror.com/vue/-/vue-3.3.8.tgz#532ff071af24f6a69e5ecc53a66858a9ee874ffc"
integrity sha512-5VSX/3DabBikOXMsxzlW8JyfeLKlG9mzqnWgLQLty88vdZL7ZJgrdgBOmrArwxiLtmS+lNNpPcBYqrhE6TQW5w==
dependencies:
"@vue/compiler-dom" "3.3.8"
"@vue/compiler-sfc" "3.3.8"
"@vue/runtime-dom" "3.3.8"
"@vue/server-renderer" "3.3.8"
"@vue/shared" "3.3.8"
vuex@^4.0.2:
version "4.1.0"
resolved "https://registry.npmmirror.com/vuex/-/vuex-4.1.0.tgz#aa1b3ea5c7385812b074c86faeeec2217872e36c"
integrity sha512-hmV6UerDrPcgbSy9ORAtNXDr9M4wlNP4pEFKye4ujJF8oqgFFuxDCdOLS3eNoRTtq5O3hoBDh9Doj1bQMYHRbQ==
dependencies:
"@vue/devtools-api" "^6.0.0-beta.11"