Compare commits

9 Commits
27 changed files with 2031 additions and 3532 deletions
+1
View File
@@ -0,0 +1 @@
VITE_APP_VERSION=1.3.20260313-70
+5
View File
@@ -0,0 +1,5 @@
# 开发环境配置
NODE_ENV=development
VITE_APP_TITLE=访客系统-开发环境
VITE_APP_BASE_API=/dev-api
VITE_APP_API_URL=http://localhost:8195
+5
View File
@@ -0,0 +1,5 @@
# 生产环境配置
NODE_ENV=production
VITE_APP_TITLE=访客系统-生产环境
VITE_APP_BASE_API=/prod-api
VITE_APP_API_URL=https://visitor.scdswj.cn
+5
View File
@@ -0,0 +1,5 @@
# 测试环境配置
NODE_ENV=test
VITE_APP_TITLE=访客系统-测试环境
VITE_APP_BASE_API=/dev-api
VITE_APP_API_URL=https://test-api.scdswj.cn
+8 -6
View File
@@ -1,14 +1,16 @@
{ {
"name": "styy-mobile", "name": "styy-mobile",
"private": true, "private": true,
"version": "0.0.0", "version": "1.2.20250825-23",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite --mode development",
"build": "vite build", "build": "vite build --mode production",
"build:dev": "vite build --mode development",
"build:test": "vite build --mode test",
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"axios": "^1.6.2", "axios": "1.12.0",
"dingtalk-jsapi": "^3.0.29", "dingtalk-jsapi": "^3.0.29",
"html2canvas": "^1.4.1", "html2canvas": "^1.4.1",
"vant": "^3.4.3", "vant": "^3.4.3",
@@ -18,9 +20,9 @@
"vuex": "^4.0.2" "vuex": "^4.0.2"
}, },
"devDependencies": { "devDependencies": {
"@vitejs/plugin-vue": "^2.3.0", "@vitejs/plugin-vue": "4",
"sass": "^1.56.1", "sass": "^1.56.1",
"vite": "^2.9.0", "vite": "^4.0.5",
"vite-plugin-style-import": "^1.4.1" "vite-plugin-style-import": "^1.4.1"
} }
} }
+78 -81
View File
@@ -1,106 +1,103 @@
import axios from "axios"; import axios from "axios";
import { useRoute, useRouter } from "vue-router"; import {useRoute, useRouter} from "vue-router";
import { useStore } from "vuex"; import {useStore} from "vuex";
import { Toast, Dialog } from "vant"; import {Toast, Dialog} from "vant";
// import { getItem, setItem, removeItem, dalert } from '@/com/index' // import { getItem, setItem, removeItem, dalert } from '@/com/index'
const errorCode = { const errorCode = {
401: "登录状态已过期,即将返回首页重新登录", 401: "登录状态已过期,即将返回首页重新登录",
403: "当前操作没有权限", 403: "当前操作没有权限",
404: "访问资源不存在", 404: "访问资源不存在",
default: "系统未知错误,请反馈给管理员", default: "系统未知错误,请反馈给管理员",
}; };
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();
const store = useStore(); const store = useStore();
const loadNoCloseArr = ["/upload/face"]; const loadNoCloseArr = ["/upload/face"];
// 判断当前的运行环境 process.env.NODE_ENV // 判断当前的运行环境 process.env.NODE_ENV
// isDev 为真 开发环境 --- npm run serve
// isDev 为假 非开发环境(测试环境,生产环境)- npm run build // 使用环境变量配置API地址
const isDev = process.env.NODE_ENV === "development"; const baseURL = import.meta.env.VITE_APP_BASE_API || '/prod-api'
const request = axios.create({ const request = axios.create({
// 根据环境 设置不同的baseURL baseURL: 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' // 'http://rap2api.taobao.org/app/mock/297766/example/1643070528065'
// 请求拦截器 - 所有的请求开始之前先到此处 // 请求拦截器 - 所有的请求开始之前先到此处
request.interceptors.request.use( request.interceptors.request.use(
(config) => { (config) => {
// 可以设置 加载的动画效果 的展示 // 可以设置 加载的动画效果 的展示
// 在必要的路由设置一些额外的参数 ---- token信息携带放在此处 // 在必要的路由设置一些额外的参数 ---- token信息携带放在此处
console.log(config); console.log(config);
return config; return config;
}, },
(error) => { (error) => {
return Promise.reject(error); return Promise.reject(error);
} }
); );
// 响应拦截器 --- 所有请求的相应先到此处 // 响应拦截器 --- 所有请求的相应先到此处
request.interceptors.response.use( request.interceptors.response.use(
(response) => { (response) => {
// 可以设置 加载的动画效果 的隐藏 // 可以设置 加载的动画效果 的隐藏
const code = response.data.code || 200; const code = response.data.code || 200;
const message = const message =
errorCode[code] || response.data.msg || errorCode["default"]; errorCode[code] || response.data.msg || errorCode["default"];
if (code == 200) { if (code === 200) {
if (loadNoCloseArr.includes(response.config.url)) { if (loadNoCloseArr.includes(response.config.url)) {
// 设置请求不关闭加载动画接口 // 设置请求不关闭加载动画接口
} else { } else {
Toast.clear(); Toast.clear();
} }
} else {
Toast.clear();
}
if (code === 401) {
console.log("进了");
Toast.fail(message);
setTimeout(() => {
localStorage.removeItem("token");
localStorage.removeItem("user");
const redirectUrl = localStorage.getItem("redirect");
if (redirectUrl) {
window.location.href = redirectUrl;
} else { } else {
window.location.href = "/adminHome"; Toast.clear();
} }
}, 2000); if (code === 401) {
return Promise.reject("登录状态已过期,即将返回首页重新登录。"); console.log("调试:401 登录状态已过期!");
} else if (code === 500) { localStorage.removeItem("token");
// Message({ message: msg, type: 'error' }) localStorage.removeItem("user");
Toast.fail(message); Toast.fail(message);
return Promise.reject(response.data); setTimeout(() => {
} else if (code === 601) { // const redirectUrl = localStorage.getItem("redirect");
// Message({ message: msg, type: 'warning' }) // if (redirectUrl) {
Toast.fail(message); // window.location.href = redirectUrl;
return Promise.reject("error"); // } else {
} else if (code != 200) { // window.location.href = "/adminHome";
Toast.fail(message); // }
return Promise.reject("error"); // 使用 router 而不是直接修改 window.location
} else { router.push("/adminHome");
return response.data; }, 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 = "后端接口连接异常(" + window.location.href + ")";
} 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);
} }
},
(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; export default request;
+2
View File
@@ -28,6 +28,8 @@ export function getUserListByDept( deptId){
deptId deptId
}, },
headers:{ headers:{
'Cache-Control': 'no-cache',
'Pragma': 'no-cache',
'Authorization': localStorage.getItem('token') 'Authorization': localStorage.getItem('token')
} }
}) })
+13 -19
View File
@@ -4,7 +4,7 @@ import { useStore } from "vuex";
import { Toast, Dialog } from "vant"; import { Toast, Dialog } from "vant";
// import { getItem, setItem, removeItem, dalert } from '@/com/index' // import { getItem, setItem, removeItem, dalert } from '@/com/index'
const errorCode = { const errorCode = {
401: "登录状态已过期,即将返回首页重新登录。", 401: "登录已过期,即将返回首页重新登录。",
403: "当前操作没有权限", 403: "当前操作没有权限",
404: "访问资源不存在", 404: "访问资源不存在",
default: "系统未知错误,请反馈给管理员", default: "系统未知错误,请反馈给管理员",
@@ -13,18 +13,12 @@ const route = useRoute();
const router = useRouter(); const router = useRouter();
const store = useStore(); const store = useStore();
const loadNoCloseArr = ["/upload/face"]; const loadNoCloseArr = ["/upload/face"];
// 判断当前的运行环境 process.env.NODE_ENV
// isDev 为真 开发环境 --- npm run serve // 使用环境变量配置API地址
// isDev 为假 非开发环境(测试环境,生产环境)- npm run build const baseURL = import.meta.env.VITE_APP_BASE_API || '/prod-api'
const isDev = process.env.NODE_ENV === "development";
const request = axios.create({ const request = axios.create({
// 根据环境 设置不同的baseURL baseURL: baseURL,
// 如果配置了单个的反向代理 --- 字符串模式
// 如果配置了多个的反向代理 --- 对象模式
baseURL: isDev
? "http://visitor.scdswj.cn/prod-api"
: "http://visitor.scdswj.cn/prod-api",
// baseURL: isDev ? "http://192.168.31.124:8195" : "http://192.168.31.124:8195",
}); });
// 'http://rap2api.taobao.org/app/mock/297766/example/1643070528065' // 'http://rap2api.taobao.org/app/mock/297766/example/1643070528065'
@@ -48,7 +42,7 @@ request.interceptors.response.use(
const code = response.data.code || 200; const code = response.data.code || 200;
const message = const message =
errorCode[code] || response.data.msg || errorCode["default"]; errorCode[code] || response.data.msg || errorCode["default"];
if (code == 200) { if (code === 200) {
if (loadNoCloseArr.includes(response.config.url)) { if (loadNoCloseArr.includes(response.config.url)) {
// 设置请求不关闭加载动画接口 // 设置请求不关闭加载动画接口
} else { } else {
@@ -58,11 +52,11 @@ request.interceptors.response.use(
Toast.clear(); Toast.clear();
} }
if (code === 401) { if (code === 401) {
Toast.fail(message);
setTimeout(() => {
localStorage.removeItem("token"); localStorage.removeItem("token");
localStorage.removeItem("user"); localStorage.removeItem("user");
localStorage.removeItem("userToken"); localStorage.removeItem("userToken");
Toast.fail(message);
setTimeout(() => {
const redirectUrl = localStorage.getItem("redirect"); const redirectUrl = localStorage.getItem("redirect");
if (redirectUrl) { if (redirectUrl) {
@@ -80,7 +74,7 @@ request.interceptors.response.use(
// Message({ message: msg, type: 'warning' }) // Message({ message: msg, type: 'warning' })
Toast.fail(message); Toast.fail(message);
return Promise.reject("error"); return Promise.reject("error");
} else if (code != 200) { } else if (code !== 200) {
Toast.fail(message); Toast.fail(message);
return Promise.reject("error"); return Promise.reject("error");
} else { } else {
@@ -91,12 +85,12 @@ request.interceptors.response.use(
Toast.clear(); Toast.clear();
console.log(error.message); console.log(error.message);
let { message } = error; let { message } = error;
if (message == "Network Error") { if (message === "Network Error") {
message = "后端接口连接异常"; message = "后端接口连接异常("+baseURL+")";
} else if (message.includes("timeout")) { } else if (message.includes("timeout")) {
message = "系统接口请求超时"; message = "系统接口请求超时";
} else if (message.includes("Request failed with status code")) { } else if (message.includes("Request failed with status code")) {
message = "系统接口" + message.substr(message.length - 3) + "异常"; message = "系统后台接口" + message.substr(message.length - 3) + "异常";
} }
console.log(message); console.log(message);
Toast.fail(message); Toast.fail(message);
+2 -2
View File
@@ -1,5 +1,5 @@
import request from "./index"; import request from "./index";
// 来访预约接口 // 内部人员头像上传接口
export function uploadApi(data){ export function uploadApi(data){
return request({ return request({
url: '/upload/face', url: '/upload/face',
@@ -8,7 +8,7 @@ export function uploadApi(data){
}) })
} }
// 来访人头像上传接口 // 访客头像上传接口
export function uploadGuestFaceApi(data){ export function uploadGuestFaceApi(data){
return request({ return request({
url: '/upload/guestFace', url: '/upload/guestFace',
+48 -1
View File
@@ -7,4 +7,51 @@ $md-size: .3733rem /* 28/75 */;
$size30: .4rem /* 30/75 */; $size30: .4rem /* 30/75 */;
$lg-size: .4267rem /* 32/75 */; $lg-size: .4267rem /* 32/75 */;
// 颜色 // 颜色
$white: #ffffff; $white: #ffffff;
/* PC端适配 - 使用缩放保持比例 */
@media screen and (min-width: 769px) {
body {
background-color: #f5f5f5;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
}
#app {
// 假设设计稿是750px宽度,我们可以按比例缩放
width: 750px;
height: 100vh;
transform-origin: center top;
// 计算缩放比例,假设PC端最小宽度为1200px
// 可以根据实际需要调整这个值
transform: scale(0.8); // 例如缩放到80%
// 或者根据实际视口宽度动态计算
// transform: scale(calc(100vw / 750));
box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
}
// 为特定高度的屏幕调整缩放
@media screen and (min-width: 1200px) {
#app {
transform: scale(1);
}
}
@media screen and (min-width: 1000px) and (max-width: 1199px) {
#app {
transform: scale(0.9);
}
}
@media screen and (min-width: 769px) and (max-width: 999px) {
#app {
transform: scale(0.8);
}
}
}
+117 -71
View File
@@ -12,149 +12,195 @@
</header> </header>
<p class="tips">点击照片框进行人脸照片上传</p> <p class="tips">点击照片框进行人脸照片上传</p>
<div class="button-box"> <div class="button-box">
<div @click="onClickCancel" class="cancel-button">取消</div> <div @click="onClickCancel" class="cancel-button">取消</div>
<div @click="onClickSave" class="save-button">保存</div> <div @click="onClickSave" class="save-button" :class="{ disabled: !file }" :style="{ pointerEvents: file ? 'auto' : 'none' }">保存</div>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref } from 'vue' import {ref} from 'vue'
import { useRouter } from 'vue-router' import {useRouter} from 'vue-router'
import { useStore } from 'vuex' import {useStore} from 'vuex'
import { rulesForm, resetForm, rulesPhone } from '@/com/index' import {rulesForm, resetForm, rulesPhone} from '@/com/index'
/* @data */ /* @data */
const router = useRouter() const router = useRouter()
const store = useStore() const store = useStore()
const emits = defineEmits(['change', 'cancel', 'save']) const emits = defineEmits(['change', 'cancel', 'save'])
const props = defineProps({ const props = defineProps({
faceImg: { faceImg: {
default: '' default: ''
}, },
}) })
const file = ref() const file = ref()
/* @setup */ /* @setup */
/* @method */ /* @method */
// 上传图片 // 上传图片
const onChangeFile = (e) =>{ const onChangeFile = (e) => {
file.value = e.target.files[0] file.value = e.target.files[0]
emits('change', e.target.files[0]) emits('change', e.target.files[0])
}
const onClickCancel = () => {
emits('cancel')
}
const onClickSave = () => {
// 如果既没有新文件也没有原始图片,则不允许保存
if (!file.value && !props.faceImg) {
return;
} }
const onClickCancel = () =>{ emits('save', file.value)
emits('cancel') }
}
const onClickSave = () =>{
emits('save', file.value)
}
</script> </script>
<style lang='scss' scoped> <style lang='scss' scoped>
.fixed-model{ .fixed-model {
position: fixed; position: fixed;
left: 0; left: 0;
top: 0; top: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
background: rgba(60,60,67,0.6); background: rgba(60, 60, 67, 0.6);
.model{
.model {
position: absolute; position: absolute;
left: 50%; left: 50%;
top: 50%; top: 50%;
transform: translateX(-50%) translateY(-65%) ; transform: translateX(-50%) translateY(-65%);
width: 8.1867rem /* 614/75 */; width: 8.1867rem /* 614/75 */
;
// height: 394px; // height: 394px;
background: #FFFFFF; background: #FFFFFF;
border-radius: .5333rem /* 40/75 */; border-radius: .5333rem /* 40/75 */
padding: 0 0 .7733rem /* 58/75 */; ;
padding: 0 0 .7733rem /* 58/75 */
;
box-sizing: border-box; box-sizing: border-box;
header{
header {
width: 100%; width: 100%;
height: 10.5333rem /* 790/75 */; height: 10.5333rem /* 790/75 */
;
background: #FFFFFF; background: #FFFFFF;
border-radius: .5333rem /* 40/75 */; border-radius: .5333rem /* 40/75 */
;
background: url('../assets/img/renlianbeijing.png') left top/100% 100% no-repeat; background: url('../assets/img/renlianbeijing.png') left top/100% 100% no-repeat;
h3{
line-height: .7733rem /* 58/75 */; h3 {
font-size: .5333rem /* 40/75 */; line-height: .7733rem /* 58/75 */
font-family: Source Han Sans CN, Source Han Sans CN; ;
font-size: .5333rem /* 40/75 */
;
font-family: Source Han Sans CN, 'Microsoft YaHei', 'PingFang SC', 'Hiragino Sans GB', 'Source Han Sans SC', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-weight: 500; font-weight: 500;
color: #222222; color: #222222;
padding: .8533rem /* 64/75 */ 0 0 ; padding: .8533rem /* 64/75 */
0 0;
text-align: center; text-align: center;
box-sizing: border-box; box-sizing: border-box;
} }
.upload-box{
.upload-box {
position: relative; position: relative;
width: 4.4rem /* 330/75 */; width: 4.4rem /* 330/75 */
height: 4.4rem /* 330/75 */; ;
height: 4.4rem /* 330/75 */
;
background: #FFFFFF; background: #FFFFFF;
box-shadow: 0px .1067rem /* 8/75 */ .8rem /* 60/75 */ .2667rem /* 20/75 */ rgba(68,140,247,0.4); box-shadow: 0px .1067rem /* 8/75 */
border-radius: .2667rem /* 20/75 */; .8rem /* 60/75 */
margin: 1.28rem /* 96/75 */ auto 0; .2667rem /* 20/75 */
rgba(68, 140, 247, 0.4);
border-radius: .2667rem /* 20/75 */
;
margin: 1.28rem /* 96/75 */
auto 0;
overflow: hidden; overflow: hidden;
.span{
.span {
text-align: center; text-align: center;
line-height: 4.4rem /* 330/75 */; line-height: 4.4rem /* 330/75 */
;
font-size: $size30; font-size: $size30;
font-family: Source Han Sans CN, Source Han Sans CN; font-family: Source Han Sans CN, 'Microsoft YaHei', 'PingFang SC', 'Hiragino Sans GB', 'Source Han Sans SC', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-weight: 400; font-weight: 400;
color: #999999; color: #999999;
} }
// display: flex; // display: flex;
.face-img, input{ .face-img, input {
position: absolute; position: absolute;
left: 0; left: 0;
top: 0; top: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
border-radius: .2667rem /* 20/75 */; border-radius: .2667rem /* 20/75 */
;
opacity: 0; opacity: 0;
} }
.face-img{
.face-img {
opacity: 1; opacity: 1;
} }
} }
} }
.tips{
line-height: .5333rem /* 40/75 */; .tips {
line-height: .5333rem /* 40/75 */
;
font-size: $md-size; font-size: $md-size;
font-family: Source Han Sans CN, Source Han Sans CN; font-family: Source Han Sans CN, 'Microsoft YaHei', 'PingFang SC', 'Hiragino Sans GB', 'Source Han Sans SC', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-weight: 400; font-weight: 400;
color: #5F6583; color: #5F6583;
text-align: center; text-align: center;
margin-top: -1.6rem /* 120/75 */; margin-top: -1.6rem /* 120/75 */
;
} }
.button-box{
.button-box {
// margin-top: 1.1467rem /* 86/75 */; // margin-top: 1.1467rem /* 86/75 */;
display: flex; display: flex;
padding: 0 .7733rem /* 58/75 */; padding: 0 .7733rem /* 58/75 */
;
box-sizing: border-box; box-sizing: border-box;
margin-top: .6133rem /* 46/75 */; margin-top: .6133rem /* 46/75 */
div{ ;
div {
// width: 240px; // width: 240px;
flex: 1; flex: 1;
height: 1.0667rem /* 80/75 */; height: 1.0667rem /* 80/75 */
line-height: 1.0667rem /* 80/75 */; ;
line-height: 1.0667rem /* 80/75 */
;
background: #448CF7; background: #448CF7;
border-radius: 1.3333rem /* 100/75 */; border-radius: 1.3333rem /* 100/75 */
;
text-align: center; text-align: center;
font-size: $md-size; font-size: $md-size;
color: #FFFFFF; color: #FFFFFF;
} }
.cancel-button{
margin-right: .2667rem /* 20/75 */;
background: #D9D9D9;
color: #999999;
.cancel-button {
margin-right: .2667rem /* 20/75 */
;
background: #448CF7; // #D9D9D9;
color: #FFFFFF; // #999999;
}
.save-button {
&.disabled {
background: #D9D9D9;
color: #999999;
}
} }
} }
} }
+6
View File
@@ -86,6 +86,12 @@ const routes = [
component: () => import("../views/specialCrowd.vue"), component: () => import("../views/specialCrowd.vue"),
meta: { title: "特殊人员录入" }, meta: { title: "特殊人员录入" },
}, },
{
path: "/visitorManage",
name: "visitorManage",
component: () => import("../views/visitorManage.vue"),
meta: { title: "访客管理" },
},
{ {
path: "/", path: "/",
name: "", name: "",
+34 -20
View File
@@ -4,30 +4,44 @@ import { createStore } from 'vuex'
const store = createStore({ const store = createStore({
state: { state: {
name: '访客预约系统', name: '访客预约系统',
// 状态映射表(与后端枚举对应)
statusMap: {
// 待审核分组(-1)
0: { statusName: '待审批', className: 'wait', group: -1 },
90: { statusName: '逾期未审', className: 'overdue', group: -1 },
// 待来访分组(-2)
1: { statusName: '已审批', className: 'pass', group: -2 },
11: { statusName: '待到访', className: 'pending', group: -2 },
12: { statusName: '已到访', className: 'visited', group: -2 },
91: { statusName: '未到访', className: 'not-visited', group: -2 },
99: { statusName: '已完成', className: 'finished', group: -2 },
// 已结束分组(-3)
2: { statusName: '已拒绝', className: 'refuse', group: -3 },
3: { statusName: '已撤销', className: 'cancel', group: null } // 不在任何分组
},
// 状态分类Tab(筛选用,与后端状态分组对应)
statusTabs: [
{ label: '待审核', value: -1, className: 'wait', count: 0, fetched: false },
{ label: '待来访', value: -2, className: 'pass', count: 0, fetched: false },
{ label: '已结束', value: -3, className: 'end', count: 0, fetched: false }
],
// 兼容旧版
stateEnum: { stateEnum: {
0: { 0: { statusName: '待审核', className: 'status', statusText: '您的来访预约还未审核,请耐心等待', status: 0 },
statusName:'待审核', 1: { statusName: '已通过', className: 'pass', statusText: '预约审核通过', status: 1 },
className: 'status', 2: { statusName: '未通过', className: 'refuse', statusText: '预约审核已拒绝', status: 2 }
statusText: '您的来访预约还未审核,请耐心等待',
status: 0
},
1: {
statusName: '已通过',
className: 'pass',
statusText: '预约审核通过',
status: 1
},
2: {
statusName:'未通过',
className: 'refuse',
statusText: '预约审核已拒绝',
status: 2
}
} }
}, },
getters: { getters: {
// 根据status获取状态信息
getStatusInfo: (state) => (status) => {
return state.statusMap[status] || { statusName: '未知', className: 'default', group: null }
},
// 获取状态分组值
getStatusGroup: (state) => (status) => {
const info = state.statusMap[status]
return info ? info.group : null
}
}, },
mutations: { mutations: {
-310
View File
@@ -1,310 +0,0 @@
<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>
+349 -156
View File
@@ -1,10 +1,10 @@
<template> <template>
<div v-if="true" class='box'> <div v-if="true" class='box'>
<header> <header>
<h3>Hello~{{userName}}</h3> <h3>Hello~{{ userName }}</h3>
<h4> 你又来审批预约了啊ㄟ( , )</h4> <h4> 你又来审批预约了啊ㄟ( , )<br/>v{{ appVersion }}</h4>
<div class="plate-box"> <div class="plate-box">
<div @click="onClickGo('/Invitation')" class="plate-button"> <div @click="onClickGo('/Invitation')" class="plate-button">
<img src="../assets/ren.png" alt=""> <img src="../assets/ren.png" alt="">
<div class="right"> <div class="right">
@@ -31,7 +31,7 @@
<span @click="onClickGo('/visitorRecord')">查看全部</span> <span @click="onClickGo('/visitorRecord')">查看全部</span>
</h2> </h2>
<div class="content-ul"> <div class="content-ul">
<div v-for="item in recordArr" class="wait-list" :key="item.id"> <div v-for="item in recordArr" @click="onClickItem(item)" class="wait-list" :key="item.id">
<h5>被拜访人{{ item.visitingName }}</h5> <h5>被拜访人{{ item.visitingName }}</h5>
<h5>拜访时间{{ item.hours }}</h5> <h5>拜访时间{{ item.hours }}</h5>
<div class="apply-box"> <div class="apply-box">
@@ -40,278 +40,471 @@
<p><span>随行人数</span><span>{{ item.visitingNum }} </span></p> <p><span>随行人数</span><span>{{ item.visitingNum }} </span></p>
<p><span>来访事由</span><span>{{ item.reason }}</span></p> <p><span>来访事由</span><span>{{ item.reason }}</span></p>
</div> </div>
<div class="status" :class="stateEnum[item.status].className">{{ stateEnum[item.status].statusName }}</div> <div class="status" :class="getStatusInfo(item.status).className">{{ getStatusInfo(item.status).statusName }}</div>
</div> </div>
</div> </div>
<div v-if="isLoading && recordArr.length == 0" class="no-have"> <div v-if="isLoading && isDataLoaded && recordArr.length === 0" class="no-have">
<img class="" src="../assets/nodata.png" alt=""> <img class="" src="../assets/nodata.png" alt="">
<p>暂无待审批数据</p> <p>暂无待审批数据</p>
</div> </div>
<div v-else-if="isLoading && !isDataLoaded" class="no-have">
<img class="" src="../assets/nodata.png" alt="">
<p>待审批数据加载中...</p>
</div>
</div> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
import { reactive, ref } from 'vue' import {reactive, ref, onMounted} from 'vue'
import { useRouter } from 'vue-router' import {useRouter} from 'vue-router'
import { useStore } from 'vuex' import {useStore} from 'vuex'
import { Toast, ImagePreview } from 'vant'; import {Toast, ImagePreview} from 'vant';
import dd from 'dingtalk-jsapi' import dd from 'dingtalk-jsapi'
import { rulesForm, resetForm, rulesPhone } from '@/com/index' import {rulesForm, resetForm, rulesPhone} from '@/com/index'
import { getToken, getVisitorRecord, getWhiteCode } from '@/api/admin.js' import {getToken, getVisitorRecord, getWhiteCode} from '@/api/admin.js'
/* @data */ /* @data */
const router = useRouter() const appVersion = ref(import.meta.env.VITE_APP_VERSION || '1.0.0')
const store = useStore() const router = useRouter()
const isLoading = ref(false) const store = useStore()
const isLoading = ref(false)
const token = ref('') const isDataLoaded = ref(false)
const userName = ref('') // 已经在登录处理中
const isLoginInProgress = ref(false);
const query = reactive({
status: 0, const token = ref('')
size: 2, const userName = ref('')
current: 1,
}) const query = reactive({
const recordArr = ref([]) status: 0,
const stateEnum = store.state.stateEnum size: 2,
token.value = localStorage.getItem('token') current: 1,
localStorage.setItem('redirect', '/adminHome') })
const recordArr = ref([])
const stateEnum = store.state.stateEnum
// 获取完整状态信息
const getStatusInfo = (status) => {
return store.getters.getStatusInfo(status)
}
token.value = localStorage.getItem('token')
localStorage.setItem('redirect', '/adminHome')
/* @setup */ /* @setup */
const login = () =>{ const login = () => {
Toast.loading({ duration: 0, forbidClick: true, message: '登录中', }); // 防止重复登录
dd.getAuthCode({ if (isLoginInProgress.value) {
corpId: 'ding03f8e88dde9b426835c2f4657eb6378f', return;
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() isLoginInProgress.value = true;
const visitorList = () =>{ Toast.loading({duration: 0, forbidClick: true, message: '登录中',});
Toast.loading({ duration: 0, forbidClick: true, message: '加载中', }); dd.getAuthCode({
getVisitorRecord(query).then(res =>{ corpId: 'ding03f8e88dde9b426835c2f4657eb6378f',
isLoading.value = true success: (res) => {
recordArr.value = res.data const {code} = res;
}).catch(err=>{ localStorage.removeItem('token');
isLoading.value = true localStorage.removeItem('user');
}) getToken(code).then(data => {
} // 验证返回的数据
// visitorList() if (data && data.data && data.data.authorization) {
if(token.value){ localStorage.setItem('user', JSON.stringify(data.data));
visitorList() localStorage.setItem('token', data.data.authorization);
console.log((JSON.parse(localStorage.getItem('user'))).name ) userName.value = data.data.name;
userName.value = JSON.parse(localStorage.getItem('user')).name
}else{ Toast.clear();
login() Toast.success('登录成功');
} visitorList();
} else {
throw new Error('登录数据异常');
}
}).catch(err => {
Toast.clear();
Toast.fail('登录失败');
console.error('登录失败:', err);
}).finally(() => {
isLoginInProgress.value = false;
});
},
fail: (err) => {
},
complete: () => {
},
});
}
// login()
const visitorList = () => {
isDataLoaded.value = false
Toast.loading({duration: 0, forbidClick: true, message: '加载中',});
getVisitorRecord(query).then(res => {
isLoading.value = true
isDataLoaded.value = true
recordArr.value = res.data
}).catch(err => {
isLoading.value = true
isDataLoaded.value = true
recordArr.value = []
})
}
/* @method */ /* @method */
const onClickGo = (url = '/adminHome') =>{ const onClickGo = (url = '/adminHome') => {
router.push(url) router.push(url)
}
// 改进页面加载时的初始化逻辑
const initializeApp = () => {
const token = localStorage.getItem('token');
const userStr = localStorage.getItem('user');
if (token && userStr) {
try {
const user = JSON.parse(userStr);
if (user && user.name) {
userName.value = user.name;
visitorList();
} else {
// 用户数据不完整,重新登录
login();
}
} catch (e) {
// 用户数据解析失败,重新登录
console.error('用户数据解析失败:', e);
login();
}
} else {
// 没有token或用户数据,需要登录
login();
} }
};
const onClickItem = (item) => {
// 检查instanceUrl是否存在且非空
let instanceUrl;
if (item.instanceUrl && item.instanceUrl.trim() !== '') {
instanceUrl = item.instanceUrl;
}else{
instanceUrl = `https://aflow.dingtalk.com/dingtalk/mobile/homepage.htm?corpid=ding03f8e88dde9b426835c2f4657eb6378f&dd_share=false&showmenu=false&dd_progress=false&back=native&procInstId=${item.instanceId}&taskId=&swfrom=isv&dinghash=approval&dtaction=os&dd_from=corp#approval`;
}
dd.openLink({
url: instanceUrl,
success: () => {
},
fail: () => {
},
complete: () => {
},
});
}
// 在组件挂载时调用
onMounted(() => {
initializeApp();
});
</script> </script>
<style lang='scss' scoped> <style lang='scss' scoped>
.box{ .box {
background-color: #F9FBFC; background-color: #F9FBFC;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
header{
header {
position: relative; position: relative;
width: 100%; width: 100%;
height: 10.4rem /* 780/75 */; height: 10.4rem /* 780/75 */
;
background: url('../assets/guanlibeijing.png') left top/100% 100% no-repeat; background: url('../assets/guanlibeijing.png') left top/100% 100% no-repeat;
box-sizing: border-box; box-sizing: border-box;
h3{
padding: 1.0933rem /* 82/75 */ .6933rem /* 52/75 */ .1067rem /* 8/75 */; h3 {
padding: 1.0933rem /* 82/75 */
.6933rem /* 52/75 */
.1067rem /* 8/75 */
;
box-sizing: border-box; box-sizing: border-box;
line-height: .72rem /* 54/75 */; line-height: .72rem /* 54/75 */
font-size: .48rem /* 36/75 */; ;
font-family: Source Han Sans CN, Source Han Sans CN; font-size: .48rem /* 36/75 */
;
font-family: Source Han Sans CN, 'Microsoft YaHei', 'PingFang SC', 'Hiragino Sans GB', 'Source Han Sans SC', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-weight: 500; font-weight: 500;
color: #222222; color: #222222;
} }
h4{
line-height: .48rem /* 36/75 */; h4 {
line-height: .48rem /* 36/75 */
;
font-size: $sm-size; font-size: $sm-size;
font-family: Source Han Sans CN, Source Han Sans CN; font-family: Source Han Sans CN, 'Microsoft YaHei', 'PingFang SC', 'Hiragino Sans GB', 'Source Han Sans SC', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-weight: 400; font-weight: 400;
color: #FFFFFF; color: #FFFFFF;
padding: 0 .6933rem /* 52/75 */ 1.12rem /* 84/75 */; padding: 0 .6933rem /* 52/75 */
1.12rem /* 84/75 */
;
box-sizing: border-box; box-sizing: border-box;
} }
.position-img{
.position-img {
position: absolute; position: absolute;
right: .4267rem /* 32/75 */; right: .4267rem /* 32/75 */
top: .2933rem /* 22/75 */; ;
width: 3.6267rem /* 272/75 */; top: .2933rem /* 22/75 */
height: 2.96rem /* 222/75 */; ;
img{ width: 3.6267rem /* 272/75 */
;
height: 2.96rem /* 222/75 */
;
img {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
} }
.plate-box{
.plate-box {
width: 100%; width: 100%;
padding: 0 .4rem /* 30/75 */; padding: 0 .4rem /* 30/75 */
;
box-sizing: border-box; box-sizing: border-box;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
.plate-button{
width: 4.4rem /* 330/75 */; .plate-button {
height: 1.92rem /* 144/75 */; width: 4.4rem /* 330/75 */
background: linear-gradient(180deg, rgba(255,255,255,0.3) 0%, rgba(255,255,255,0.6) 48%); ;
border-radius: .2667rem /* 20/75 */; height: 1.92rem /* 144/75 */
border: .0267rem /* 2/75 */ solid #FFFFFF; ;
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; display: flex;
align-items: center; align-items: center;
img{
width: .9867rem /* 74/75 */; img {
height: .9867rem /* 74/75 */; width: .9867rem /* 74/75 */
margin-left: .32rem /* 24/75 */; ;
height: .9867rem /* 74/75 */
;
margin-left: .32rem /* 24/75 */
;
} }
.right{
margin-left: .3467rem /* 26/75 */; .right {
div{ margin-left: .3467rem /* 26/75 */
line-height: .64rem /* 48/75 */; ;
div {
line-height: .64rem /* 48/75 */
;
font-size: $lg-size; font-size: $lg-size;
font-family: Source Han Sans CN, Source Han Sans CN; font-family: Source Han Sans CN, 'Microsoft YaHei', 'PingFang SC', 'Hiragino Sans GB', 'Source Han Sans SC', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-weight: 500; font-weight: 500;
color: #222222; color: #222222;
} }
P{
line-height: .4rem /* 30/75 */; P {
font-size: .2667rem /* 20/75 */; line-height: .4rem /* 30/75 */
font-family: Source Han Sans CN, Source Han Sans CN; ;
font-size: .2667rem /* 20/75 */
;
font-family: Source Han Sans CN, 'Microsoft YaHei', 'PingFang SC', 'Hiragino Sans GB', 'Source Han Sans SC', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-weight: 400; font-weight: 400;
color: #5F6583; color: #5F6583;
margin-top: .1067rem /* 8/75 */; margin-top: .1067rem /* 8/75 */
;
} }
} }
} }
} }
} }
.content{
.content {
flex: 1; flex: 1;
overflow: auto; overflow: auto;
position: relative; position: relative;
// top: 5.8667rem /* 440/75 */; // top: 5.8667rem /* 440/75 */;
margin-top: -4.5333rem /* 340/75 */; margin-top: -4.5333rem /* 340/75 */
;
width: 100%; width: 100%;
padding: 0 .4rem /* 30/75 */; padding: 0 .4rem /* 30/75 */
;
box-sizing: border-box; box-sizing: border-box;
h2{
h2 {
width: 100%; width: 100%;
padding: .2667rem /* 20/75 */ .32rem /* 24/75 */; padding: .2667rem /* 20/75 */
.32rem /* 24/75 */
;
box-sizing: border-box; box-sizing: border-box;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
line-height: .72rem /* 54/75 */; line-height: .72rem /* 54/75 */
font-size: .48rem /* 36/75 */; ;
font-family: Source Han Sans CN, Source Han Sans CN; font-size: .48rem /* 36/75 */
;
font-family: Source Han Sans CN, 'Microsoft YaHei', 'PingFang SC', 'Hiragino Sans GB', 'Source Han Sans SC', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-weight: 500; font-weight: 500;
color: #222222; color: #222222;
span{
span {
// line-height: 36px; // line-height: 36px;
font-size: $sm-size; font-size: $sm-size;
font-family: Source Han Sans CN, Source Han Sans CN; font-family: Source Han Sans CN, 'Microsoft YaHei', 'PingFang SC', 'Hiragino Sans GB', 'Source Han Sans SC', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-weight: 400; font-weight: 400;
color: #999999; color: #999999;
} }
} }
.content-ul{
.wait-list{ .content-ul {
.wait-list {
position: relative; position: relative;
width: 9.2rem /* 690/75 */; width: 9.2rem /* 690/75 */
;
// height: 470px; // height: 470px;
background: #FFFFFF; background: #FFFFFF;
border-radius: .2667rem /* 20/75 */; border-radius: .2667rem /* 20/75 */
padding: .48rem /* 36/75 */; ;
padding: .48rem /* 36/75 */
;
box-sizing: border-box; box-sizing: border-box;
margin-bottom: .2667rem /* 20/75 */; margin-bottom: .2667rem /* 20/75 */
h5{ ;
height: .6667rem /* 50/75 */;
h5 {
height: .6667rem /* 50/75 */
;
font-size: $md-size; font-size: $md-size;
font-family: Source Han Sans CN, Source Han Sans CN; font-family: Source Han Sans CN, 'Microsoft YaHei', 'PingFang SC', 'Hiragino Sans GB', 'Source Han Sans SC', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-weight: 400; font-weight: 400;
color: #222222; color: #222222;
} }
.apply-box{
.apply-box {
width: 100%; width: 100%;
padding: .4rem /* 30/75 */ .3733rem /* 28/75 */ .5067rem /* 38/75 */; padding: .4rem /* 30/75 */
.3733rem /* 28/75 */
.5067rem /* 38/75 */
;
box-sizing: border-box; box-sizing: border-box;
border-radius: .1333rem /* 10/75 */; border-radius: .1333rem /* 10/75 */
;
font-size: $md-size; font-size: $md-size;
font-family: Source Han Sans CN, Source Han Sans CN; font-family: Source Han Sans CN, 'Microsoft YaHei', 'PingFang SC', 'Hiragino Sans GB', 'Source Han Sans SC', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-weight: 400; font-weight: 400;
color: #999999; color: #999999;
line-height: .72rem /* 54/75 */; line-height: .72rem /* 54/75 */
;
background: #F5F7F9; background: #F5F7F9;
span:last-child{
span:last-child {
color: #5F6583; color: #5F6583;
} }
} }
.status{
.status {
position: absolute; position: absolute;
top: 0; top: 0;
right: 0; right: 0;
width: 1.52rem /* 114/75 */; width: 1.52rem /* 114/75 */
height: .6667rem /* 50/75 */; ;
line-height: .6667rem /* 50/75 */; height: .6667rem /* 50/75 */
;
line-height: .6667rem /* 50/75 */
;
background: #FFE9E4; background: #FFE9E4;
border-radius: 0px .2667rem /* 20/75 */ 0px .1333rem /* 10/75 */; border-radius: 0 .2667rem /* 20/75 */
0 .1333rem /* 10/75 */
;
color: #FF6643; color: #FF6643;
font-size: $sm-size; font-size: $sm-size;
font-family: Source Han Sans CN, Source Han Sans CN; font-family: Source Han Sans CN, 'Microsoft YaHei', 'PingFang SC', 'Hiragino Sans GB', 'Source Han Sans SC', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-weight: 400; font-weight: 400;
color: #FF6643;
text-align: center; text-align: center;
} }
.refuse{
.refuse {
color: #5F6583; color: #5F6583;
background: #EAEEF6; background: #EAEEF6;
} }
.pass{
.pass {
color: #30BF78; color: #30BF78;
background: #E7F8F5; background: #E7F8F5;
} }
// 新增状态样式
.wait {
color: #E6A23C;
background: #FFF7CC;
}
.cancel {
color: #909399;
background: #F4F4F5;
}
.pending {
color: #409EFF;
background: #ECF5FF;
}
.visited {
color: #67C23A;
background: #F0F9EB;
}
.overdue {
color: #E6A23C;
background: #FEF0F0;
}
.not-visited {
color: #909399;
background: #F4F4F5;
}
.finished {
color: #999999;
background: #F5F5F5;
}
.default {
color: #666666;
background: #F5F5F5;
}
} }
} }
.no-have{
.no-have {
// display: block; // display: block;
width: 7.4667rem /* 560/75 */; width: 7.4667rem /* 560/75 */
height: 5.3867rem /* 404/75 */; ;
margin: .8rem /* 60/75 */ auto; height: 5.3867rem /* 404/75 */
img{ ;
margin: .8rem /* 60/75 */
auto;
img {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
// font-size: 15px; // font-size: 15px;
font-family: Source Han Sans CN, Source Han Sans CN; font-family: Source Han Sans CN, 'Microsoft YaHei', 'PingFang SC', 'Hiragino Sans GB', 'Source Han Sans SC', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-weight: 400; font-weight: 400;
color: #999999; color: #999999;
text-align: center; text-align: center;
-720
View File
@@ -1,720 +0,0 @@
<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
@@ -1,720 +0,0 @@
<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>
+15 -71
View File
@@ -9,26 +9,6 @@
<span>拜访信息<span>(我找谁)</span></span> <span>拜访信息<span>(我找谁)</span></span>
</h3> </h3>
<div class="apply-content-form"> <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-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"/> <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"/> <Selectdate v-model="form.hours" label="来访时间" :isMust="true" :isline="false" :minDate="minDate"/>
@@ -123,8 +103,6 @@ import { uploadGuestFaceApi } from '@/api/upload'
visitingNum: '', visitingNum: '',
}) })
const isSubmit = ref(false) const isSubmit = ref(false)
const visitDict = ref([]) // 被访客数据
const visitAllDict = ref([]) // 被访客数据
const isDisabled = ref(false) // 是否能提交 const isDisabled = ref(false) // 是否能提交
const isSuccess = ref(false) // 是否提交成功 const isSuccess = ref(false) // 是否提交成功
const isSelect = ref( false) // 是否显示下拉选择 const isSelect = ref( false) // 是否显示下拉选择
@@ -156,58 +134,28 @@ import { uploadGuestFaceApi } from '@/api/upload'
} }
const onBlurTel = (phone) =>{ const onBlurTel = (phone) =>{
console.log(phone) if (rulesPhone(phone)) {
if(rulesPhone(phone)){ getUserNameByTel(phone).then(res => {
getUserNameByTel(phone).then(res =>{ if (res.data) {
console.log(res) telName.value = res.data;
if(res.data){ } else {
telName.value = res.data Toast.fail('未找到该手机号对应的被访人');
telName.value = '';
} }
}).catch(err =>{ }).catch(err => {
Toast.clear(); Toast.clear();
console.log(err) console.log(err);
Dialog.alert({ Dialog.alert({
message: err.msg, message: err.msg || '查询失败',
}).then(() => {
// on close
}); });
}) telName.value = '';
});
} else {
Toast.fail('请输入正确的手机号');
telName.value = '';
} }
} }
// 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 = () =>{ const onClickAffirm = () =>{
router.go(-1) router.go(-1)
@@ -365,10 +313,6 @@ import { uploadGuestFaceApi } from '@/api/upload'
form.visitingTel = route.query.tel form.visitingTel = route.query.tel
onBlurTel(route.query.tel) onBlurTel(route.query.tel)
} }
getVisitor().then( res=>{
visitDict.value = res.data
visitAllDict.value = res.data
})
</script> </script>
+33 -3
View File
@@ -3,8 +3,8 @@
<div v-for="item in recordArr" class="apply-list" :key="item.id"> <div v-for="item in recordArr" class="apply-list" :key="item.id">
<h3 class="h3"> <h3 class="h3">
<span>提交时间{{ item.createTime }}</span> <span>提交时间{{ item.createTime }}</span>
<!-- refuse拒绝 pass通过 状态 --> <!-- 显示完整状态 -->
<div class="status" :class="stateEnum[item.status].className">{{ stateEnum[item.status].statusName }}</div> <div class="status" :class="getStatusInfo(item.status).className">{{ getStatusInfo(item.status).statusName }}</div>
</h3> </h3>
<div class="apply-content"> <div class="apply-content">
<div class="user-info"> <div class="user-info">
@@ -16,7 +16,7 @@
<span>拜访时间{{ item.hours }}</span> <span>拜访时间{{ item.hours }}</span>
</div> </div>
<p> <p>
{{ item.status == 2 && item.remark ? item.remark : stateEnum[item.status].statusText }} {{ item.status == 2 && item.remark ? item.remark : getStatusInfo(item.status).statusText }}
<span class="triangle"></span> <span class="triangle"></span>
</p> </p>
</div> </div>
@@ -44,6 +44,11 @@ import { Toast, ImagePreview } from 'vant';
const total = ref() const total = ref()
const recordArr = ref([]) const recordArr = ref([])
const stateEnum = store.state.stateEnum const stateEnum = store.state.stateEnum
// 获取完整状态信息
const getStatusInfo = (status) => {
return store.getters.getStatusInfo(status)
}
/* @setup */ /* @setup */
console.log(localStorage.getItem('userToken')) console.log(localStorage.getItem('userToken'))
@@ -142,6 +147,31 @@ import { Toast, ImagePreview } from 'vant';
.refuse{ .refuse{
color: #FF6643; color: #FF6643;
} }
// 新增状态样式
.wait{
color: #E6A23C;
}
.cancel{
color: #909399;
}
.pending{
color: #409EFF;
}
.visited{
color: #67C23A;
}
.overdue{
color: #E6A23C;
}
.not-visited{
color: #909399;
}
.finished{
color: #999999;
}
.default{
color: #666666;
}
} }
.apply-content{ .apply-content{
width: 100%; width: 100%;
-786
View File
@@ -1,786 +0,0 @@
<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>
+399 -245
View File
@@ -1,8 +1,11 @@
<template> <template>
<div class="box"> <div class="box">
<header> <header>
<h2>门禁人脸信息</h2> <div class="header-top">
<h3> <img src="../assets/img/dunpai.png" alt=""> 公司门禁系统人脸上传和查看 </h3> <h2>门禁人脸信息</h2>
<button @click="router.push('/visitorManage')" class="visitor-manage-btn">访客管理</button>
</div>
<h3><img src="../assets/img/dunpai.png" alt=""> 公司门禁系统人脸上传和查看 (v{{ appVersion }})</h3>
<div class="entering-box"> <div class="entering-box">
<img src="../assets/img/shenfenzheng.png" alt=""> <img src="../assets/img/shenfenzheng.png" alt="">
<div class="entering-center"> <div class="entering-center">
@@ -13,22 +16,25 @@
</div> </div>
</header> </header>
<div class="content"> <div class="content">
<h2>通讯录</h2><button @click="handleSync" class="entering-button">同步人员信息</button> <h2>通讯录</h2>
<div class="content-box" > <button @click="handleSync" class="entering-button">同步人员信息</button>
<div class="content-box">
<div class="nav"> <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"> <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 }} {{ item.name }}
</div> </div>
</div> </div>
<div class="user-content"> <div class="user-content">
<div class="user-scroll"> <div class="user-scroll">
<div v-for="item in userArr" class="user-list"> <div v-for="item in userArr" class="user-list">
<div @click="onClickPreviewImg(item.img)" class="img-box"> <div @click="onClickUser(item)" class="img-box">
<img v-if="item.img" :src="item.img" alt=""> <img v-if="item.img" :src="item.img" alt="">
</div> </div>
<div class="name">{{ item.name }}</div> <div class="name">{{ item.name }}</div>
<div @click="onClcikUser(item)" v-if="item.img" class="button">查看</div> <div @click="onClickUser(item)" v-if="item.img" class="button active-button">查看</div>
<div @click="onClcikUser(item)" v-else class="button active-button">上传</div> <div @click="onClickUser(item)" v-else class="button active-button">上传</div>
</div> </div>
</div> </div>
@@ -37,375 +43,523 @@
</div> </div>
</div> </div>
<Facemodel @change="onChangeFile" @cancel="isFace = false; form.face =''" @save="onClciksave" v-if="isFace" :faceImg="form.face" /> <Facemodel @change="onChangeFile" @cancel="isFace = false; form.face =''" @save="onClickSave" v-if="isFace"
:faceImg="form.face"/>
</template> </template>
<script setup> <script setup>
import { rulesPhone, compareTime, timeDiff,formatDate } from '../com/index.js' import {syncDeptUsers} from '@/api/user'
// getShop, getKind import {reactive, ref} from 'vue'
import { syncDeptUsers } from '../api/user.js' import {Toast, ImagePreview, Skeleton} from 'vant';
import { reactive, ref } from 'vue' import dd from 'dingtalk-jsapi'
import { Toast, ImagePreview, Skeleton } from 'vant'; import {getToken, getDept, getUserListByDept, putUserId} from '@/api/doorAdmin.js'
import dd from 'dingtalk-jsapi' import Facemodel from '@/components/face-model.vue'
import { getToken, getDept, getUserListByDept, putUserId } from '@/api/doorAdmin.js' import {uploadApi} from '@/api/upload'
import Facemodel from '@/components/face-model.vue' import {useRouter} from 'vue-router';
import { uploadApi } from '@/api/upload'
import { useRouter } from 'vue-router';
// 获取环境变量中的版本号
const appVersion = import.meta.env.VITE_APP_VERSION || '1.0.0';
console.log('Available environment variables:', import.meta.env); // 调试信息,查看所有可用的环境变量
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')
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 // method
// 点击预栏图片 // 点击预栏图片
const onClickPreviewImg = (img) =>{ const onClickPreviewImg1 = (img) => {
dd.previewImage({ dd.previewImage({
urls: [img urls: [img
], ],
current: 1, current: 1,
success: () => {}, success: () => {
fail: () => {}, },
complete: () => {}, fail: () => {
}); },
complete: () => {
},
});
}
// 点击切换部门
const onClickDept = (Id) => {
if (deptId.value !== Id) {
deptId.value = Id
userList()
} }
// 点击切换部门 }
const onClickDept = (Id) =>{ // 点击上传了文件
if(deptId.value != Id){ const onChangeFile = (file) => {
deptId.value = Id console.log(file)
userList() if (file) {
let reader = new FileReader()
reader.readAsDataURL(file) // 转base64
reader.onload = () => { // 简化代码 - 转换完成
form.face = reader.result
} }
} }
// 点击上传了文件
const onChangeFile = (file) =>{ }
console.log(file) // 点击保存
if(file){ const onClickSave = (file) => {
let reader = new FileReader() if (form.face && file) {
reader.readAsDataURL(file) // 转base64 // 有新图片需要上传
reader.onload = () =>{ // 简化代码 - 转换完成 let formData = new FormData()
form.face = reader.result 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
const onClciksave = (file) =>{ let obj = {userId: form.userId, img: form.face}
if(form.face && file){ putUserId(obj).then(response => {
let formData = new FormData() userList()
formData.append('file', file) isFace.value = false
formData.append('userId', form.userId) Toast.success(response.msg);
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 ){ // 说明之前有数据现在没传 }).catch(err => {
isFace.value = false Toast.fail('上传失败,请重试');
}else if(!form.face && !file){ console.error('上传失败:', err);
Toast.fail('请先上传照片!'); })
} } else if (form.face && !file) { // 说明之前有数据现在没传
// if(file){ // 文件是否上传了 isFace.value = false
// let formData = new FormData() } else if (!form.face && !file) {
// formData.append('face', file) Toast.fail('请先上传照片!');
// 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 const onClickUser = (item) => {
form.face = item.img console.log(item)
form.userId = item.userId isFace.value = true
form.name = item.name form.face = item.img
} form.userId = item.userId
const login = () =>{ form.name = item.name
Toast.loading({ duration: 0, forbidClick: true, message: '登录中', }); }
const login = () => {
Toast.loading({duration: 0, forbidClick: true, message: '登录中',});
if (dd.env && dd.env.platform !== 'notInDingTalk') { // ['ios', 'android', 'wechat', 'dingding', 'notInDingTalk']
// 确保钉钉JSAPI准备就绪
dd.getAuthCode({ dd.getAuthCode({
corpId: 'ding03f8e88dde9b426835c2f4657eb6378f', corpId: 'ding03f8e88dde9b426835c2f4657eb6378f',
success: (res) => { success: (res) => {
const { code } = res; const {code} = res;
localStorage.removeItem('token'); localStorage.removeItem('token');
localStorage.removeItem('user'); localStorage.removeItem('user');
getToken(code).then(data =>{ getToken(code).then(res => {
localStorage.setItem('user', data.data) localStorage.setItem('user', res.data)
localStorage.setItem('token', data.data.authorization) localStorage.setItem('token', res.data.authorization)
Toast.clear() Toast.clear()
Toast.success('登录成功') Toast.success('登录成功')
deptList() deptList()
}).catch(err => {
Toast.clear() // 添加这行,确保清除加载提示
Toast.fail('登录失败,请重试');
console.error('登录失败:', err);
}) })
},
fail: (err) => {
Toast.clear()
Toast.fail('登录失败('+err+'),请稍后重试!');
} }
}); });
} else {
Toast.clear()
Toast.fail('登录失败,目前系统只支持在钉钉环境中使用!');
} }
// 获取部门用户 }
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')
}
// 同步钉钉用户信息 // 检查URL中是否有OAuth回调参数
const handleSync = () =>{ const checkOAuthCallback = () => {
syncDeptUsers().then(res =>{ const urlParams = new URLSearchParams(window.location.search);
Toast.success(res.msg) const code = urlParams.get('code');
}) const state = urlParams.get('state');
if (code && state) {
// 验证state参数
const savedState = localStorage.getItem('oauth_state');
if (state === savedState) {
// 清除state
localStorage.removeItem('oauth_state');
// 清理URL参数
window.history.replaceState({}, document.title, window.location.pathname);
// 使用授权码换取用户信息
processOAuthCode(code);
return true;
}
} }
return false;
if(token.value){ }
// localStorage.setItem('token', 123)
// 处理OAuth授权码
const processOAuthCode = (code) => {
Toast.loading({duration: 0, forbidClick: true, message: '处理登录信息...',});
localStorage.removeItem('token');
localStorage.removeItem('user');
getToken(code).then(res => {
localStorage.setItem('user', res.data)
localStorage.setItem('token', res.data.authorization)
Toast.clear()
Toast.success('登录成功')
deptList() deptList()
}else{ }).catch(err => {
login() Toast.fail('登录失败,请重试');
} console.error('登录失败:', err);
});
}
// 获取部门用户
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')
}
// 同步钉钉用户信息
const handleSync = () => {
syncDeptUsers().then(res => {
Toast.success(res.msg)
})
}
if (token.value) {
// localStorage.setItem('token', 123)
deptList()
} else {
login()
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.box{ .box {
display: flex; display: flex;
flex-direction:column ; flex-direction: column;
header{
header {
width: 100%; width: 100%;
height: 5.8933rem /* 442/75 */; height: 5.8933rem /* 442/75 */
;
background: url('../assets/img/shouyebeijing.png') left top/100% 100% no-repeat; background: url('../assets/img/shouyebeijing.png') left top/100% 100% no-repeat;
h2{
line-height: 1.12rem /* 84/75 */; h2 {
font-size: .7467rem /* 56/75 */; line-height: 1.12rem /* 84/75 */
font-family: Source Han Sans CN, Source Han Sans CN; ;
font-size: .7467rem /* 56/75 */
;
font-family: Source Han Sans CN, 'Microsoft YaHei', 'PingFang SC', 'Hiragino Sans GB', 'Source Han Sans SC', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-weight: 500; font-weight: 500;
color: #FFFFFF; color: #FFFFFF;
padding: .8533rem /* 64/75 */ 0 0 .64rem /* 48/75 */; padding: .8533rem /* 64/75 */
0 0 .64rem /* 48/75 */
;
box-sizing: border-box; box-sizing: border-box;
} }
h3{
h3 {
display: flex; display: flex;
padding: .32rem /* 24/75 */ 0 0 .7467rem /* 56/75 */; padding: .32rem /* 24/75 */
0 0 .7467rem /* 56/75 */
;
box-sizing: border-box; box-sizing: border-box;
line-height: .5333rem /* 40/75 */; line-height: .5333rem /* 40/75 */
;
font-size: $sm-size; font-size: $sm-size;
font-family: Source Han Sans CN, Source Han Sans CN; font-family: Source Han Sans CN, 'Microsoft YaHei', 'PingFang SC', 'Hiragino Sans GB', 'Source Han Sans SC', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-weight: 400; font-weight: 400;
color: #FFFFFF; color: #FFFFFF;
img{
width: .5333rem /* 40/75 */; img {
height: .5333rem /* 40/75 */; width: .5333rem /* 40/75 */
margin-right: .1067rem /* 8/75 */; ;
height: .5333rem /* 40/75 */
;
margin-right: .1067rem /* 8/75 */
;
} }
} }
.entering-box{
width: 8rem /* 600/75 */; .entering-box {
height: 1.6rem /* 120/75 */; width: 8rem /* 600/75 */
margin: .6667rem /* 50/75 */ auto 0; ;
height: 1.6rem /* 120/75 */
;
margin: .6667rem /* 50/75 */
auto 0;
background: #202F44; background: #202F44;
border-radius: .5333rem /* 40/75 */ .5333rem /* 40/75 */ 0px 0px; border-radius: .5333rem /* 40/75 */
padding: 0 .4rem /* 30/75 */ .8rem /* 60/75 */ .4533rem /* 34/75 */; .5333rem /* 40/75 */
0 0;
padding: 0 .4rem /* 30/75 */
.8rem /* 60/75 */
.4533rem /* 34/75 */
;
display: flex; display: flex;
align-items: center; align-items: center;
img{
width: .6133rem /* 46/75 */; img {
height: .48rem /* 36/75 */; width: .6133rem /* 46/75 */
margin-right: .2933rem /* 22/75 */; ;
height: .48rem /* 36/75 */
;
margin-right: .2933rem /* 22/75 */
;
} }
.entering-center{
.entering-center {
flex: 1; flex: 1;
font-size: $md-size; font-size: $md-size;
font-family: Source Han Sans CN, Source Han Sans CN; font-family: Source Han Sans CN, 'Microsoft YaHei', 'PingFang SC', 'Hiragino Sans GB', 'Source Han Sans SC', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-weight: 500; font-weight: 500;
color: #FFFFFF; color: #FFFFFF;
p:nth-child(1){
line-height: .56rem /* 42/75 */; p:nth-child(1) {
padding: .2933rem /* 22/75 */ 0 0; line-height: .56rem /* 42/75 */
;
padding: .2933rem /* 22/75 */
0 0;
} }
p:nth-child(2){
line-height: .4533rem /* 34/75 */; p:nth-child(2) {
line-height: .4533rem /* 34/75 */
;
font-size: $sm-size; font-size: $sm-size;
font-weight: 400; font-weight: 400;
color: #B6B7B8; color: #B6B7B8;
} }
} }
.entering-button{
width: 1.4667rem /* 110/75 */; .entering-button {
height: .6933rem /* 52/75 */; width: 1.4667rem /* 110/75 */
;
height: .6933rem /* 52/75 */
;
background: #F6C976; background: #F6C976;
border-radius: .5333rem /* 40/75 */; border-radius: .5333rem /* 40/75 */
;
outline: none; outline: none;
font-size: $size26; font-size: $size26;
font-family: Source Han Sans CN, Source Han Sans CN; font-family: Source Han Sans CN, 'Microsoft YaHei', 'PingFang SC', 'Hiragino Sans GB', 'Source Han Sans SC', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-weight: 500; font-weight: 500;
color: #202F44; color: #202F44;
border: none; border: none;
} }
} }
} }
.content{
.content {
width: 100%; width: 100%;
background: #FFFFFF; background: #FFFFFF;
border-radius: .5333rem /* 40/75 */ .5333rem /* 40/75 */ 0px 0px; border-radius: .5333rem /* 40/75 */
.5333rem /* 40/75 */
0 0;
margin-top: -20px; margin-top: -20px;
overflow: auto; overflow: auto;
flex: 1; flex: 1;
display: flex; display: flex;
flex-direction: column ; flex-direction: column;
h2{
padding: .4267rem /* 32/75 */ .48rem /* 36/75 */; h2 {
padding: .4267rem /* 32/75 */
.48rem /* 36/75 */
;
box-sizing: border-box; box-sizing: border-box;
line-height: .8rem /* 60/75 */; line-height: .8rem /* 60/75 */
font-size: .5333rem /* 40/75 */; ;
font-family: Source Han Sans CN, Source Han Sans CN; font-size: .5333rem /* 40/75 */
;
font-family: Source Han Sans CN, 'Microsoft YaHei', 'PingFang SC', 'Hiragino Sans GB', 'Source Han Sans SC', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-weight: 500; font-weight: 500;
color: #222222; color: #222222;
} }
.content-box{
.content-box {
overflow: auto; overflow: auto;
display: flex; display: flex;
flex: 1; flex: 1;
.nav{
width: 3.6533rem /* 274/75 */; .nav {
width: 3.6533rem /* 274/75 */
;
height: 100%; height: 100%;
// width: 360px; // width: 360px;
padding: 0 0 0 .4rem /* 30/75 */; padding: 0 0 0 .4rem /* 30/75 */
;
box-sizing: border-box; box-sizing: border-box;
display: flex; display: flex;
align-content: flex-start; align-content: flex-start;
// flex-direction: column; // flex-direction: column;
flex-wrap: wrap; flex-wrap: wrap;
border-right: .0267rem /* 2/75 */ dashed #D9D9D9; border-right: .0267rem /* 2/75 */
dashed #D9D9D9;
.li{
display:block; .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; box-sizing: border-box;
height: .9067rem /* 68/75 */
;
line-height: .9067rem /* 68/75 */
;
border-radius: 0 .8rem /* 60/75 */
.8rem /* 60/75 */
0;
padding: 0 .48rem /* 36/75 */
0 .24rem /* 18/75 */
;
font-size: $md-size; font-size: $md-size;
font-family: Source Han Sans CN, Source Han Sans CN; font-family: Source Han Sans CN, 'Microsoft YaHei', 'PingFang SC', 'Hiragino Sans GB', 'Source Han Sans SC', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-weight: 400; font-weight: 400;
color: #5F6583; color: #5F6583;
margin-bottom: .28rem /* 21/75 */; margin-bottom: .28rem /* 21/75 */
;
} }
.active{
.active {
color: #FFFFFF; color: #FFFFFF;
background: #448CF7; background: #448CF7;
} }
} }
.user-content{
.user-content {
flex: 1; flex: 1;
height: 100%; height: 100%;
overflow: auto; overflow: auto;
.user-scroll{ .user-scroll {
width: 100%; width: 100%;
// height: 100%; // height: 100%;
} }
.user-list{ .user-list {
display: flex; display: flex;
align-items: center; align-items: center;
padding: 0 .4rem /* 30/75 */; padding: 0 .4rem /* 30/75 */
;
box-sizing: border-box; box-sizing: border-box;
margin-bottom: .5333rem /* 40/75 */; margin-bottom: .5333rem /* 40/75 */
.img-box{ ;
width: .9867rem /* 74/75 */;
height: .9867rem /* 74/75 */; .img-box {
border-radius:.1333rem /* 10/75 */; width: .9867rem /* 74/75 */
;
height: .9867rem /* 74/75 */
;
border-radius: .1333rem /* 10/75 */
;
background: #D9D9D9; background: #D9D9D9;
img{
img {
display: block; display: block;
width: 100%; width: 100%;
height: 100%; height: 100%;
border-radius:.1333rem /* 10/75 */; border-radius: .1333rem /* 10/75 */
;
} }
} }
.name{ .name {
padding: 0 0 0 .2933rem /* 22/75 */; padding: 0 0 0 .2933rem /* 22/75 */
;
box-sizing: border-box; box-sizing: border-box;
flex: 1; flex: 1;
line-height: .9867rem /* 74/75 */; line-height: .9867rem /* 74/75 */
;
font-size: $size30; font-size: $size30;
font-family: Source Han Sans CN, Source Han Sans CN; font-family: Source Han Sans CN, 'Microsoft YaHei', 'PingFang SC', 'Hiragino Sans GB', 'Source Han Sans SC', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-weight: 400; font-weight: 400;
color: #4B4C4E; color: #4B4C4E;
} }
.button{
width: 1.2267rem /* 92/75 */; .button {
height: .6667rem /* 50/75 */; width: 1.2267rem /* 92/75 */
;
height: .6667rem /* 50/75 */
;
background: #EDEDED; background: #EDEDED;
border-radius:.1333rem /* 10/75 */; border-radius: .1333rem /* 10/75 */
;
border: none; border: none;
outline: none; outline: none;
font-size: $sm-size; font-size: $sm-size;
font-family: Source Han Sans CN, Source Han Sans CN; font-family: Source Han Sans CN, 'Microsoft YaHei', 'PingFang SC', 'Hiragino Sans GB', 'Source Han Sans SC', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-weight: 400; font-weight: 400;
color: #999999; color: #999999;
box-sizing: border-box; box-sizing: border-box;
text-align: center; text-align: center;
line-height: .6667rem /* 50/75 */; line-height: .6667rem /* 50/75 */
;
margin-left: 0.1rem;
} }
.active-button{
background: #EBFCF4; .active-button {
border: .0267rem /* 2/75 */ solid #30BF78; background: #EBFCF4;
border: .0267rem /* 2/75 */
solid #30BF78;
color: #30BF78; color: #30BF78;
} }
} }
} }
} }
} }
} }
// 访客管理按钮
.header-top {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.64rem 0.4rem 0;
h2 {
padding: 0;
}
.visitor-manage-btn {
background: #fff;
color: #1989fa;
border: none;
padding: 0.2rem 0.4rem;
border-radius: 0.2rem;
font-size: 0.37rem;
}
}
</style> </style>
+15 -1
View File
@@ -5,6 +5,7 @@
<h1><img src="../assets/userhome/dingsheng.png" alt="" /></h1> <h1><img src="../assets/userhome/dingsheng.png" alt="" /></h1>
<img class="h3" src="../assets/userhome/zhiengfangke.png" alt="" /> <img class="h3" src="../assets/userhome/zhiengfangke.png" alt="" />
<p>欢迎进入鼎盛无际智能访客系统</p> <p>欢迎进入鼎盛无际智能访客系统</p>
<p class="version">v {{ appVersion }}</p>
</div> </div>
<div class="plate-box"> <div class="plate-box">
<div @click="onClickGo('/apply')" class="list"> <div @click="onClickGo('/apply')" class="list">
@@ -59,9 +60,10 @@ import { getWeChatCode, login } from "../api/user";
import { Toast, ImagePreview } from "vant"; import { Toast, ImagePreview } from "vant";
/* @data */ /* @data */
const appVersion = ref(import.meta.env.VITE_APP_VERSION || '1.0.0')
const router = useRouter(); const router = useRouter();
const store = useStore(); const store = useStore();
const url = "http://visitor.scdswj.cn/?code=071ucW200YtsQR1AUj3007Tabj2ucW26&state=STATE"; const url = "https://visitor.scdswj.cn/?code=071ucW200YtsQR1AUj3007Tabj2ucW26&state=STATE";
/* @setup */ /* @setup */
// url.split('?')[1].split('&').map(item =>{ // url.split('?')[1].split('&').map(item =>{
// if(item.split('=')[0] === 'code'){ // if(item.split('=')[0] === 'code'){
@@ -177,6 +179,18 @@ if (isWXBrowser) {
padding: 0 0 0 0.7467rem /* 56/75 */; padding: 0 0 0 0.7467rem /* 56/75 */;
box-sizing: border-box; box-sizing: border-box;
} }
.version {
line-height: 0.4rem;
font-size: 0.2667rem /* 20/75 */;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #514e4b;
padding: 0 0 0 0.7467rem /* 56/75 */;
box-sizing: border-box;
margin-top: -0.1333rem /* 10/75 */;
}
} }
.plate-box { .plate-box {
width: 100%; width: 100%;
+229
View File
@@ -0,0 +1,229 @@
<template>
<div class="visitor-manage-page">
<div class="status-tabs">
<span
v-for="tab in statusTabs"
:key="tab.value"
:class="{ active: visitorStatus === tab.value }"
@click="changeVisitorStatus(tab.value)"
>
{{ tab.label }}
<span v-if="tab.fetched && tab.count > -1" class="count">({{ tab.count }})</span>
</span>
</div>
<van-loading v-if="visitorLoading" />
<div v-else class="visitor-list">
<div v-for="item in visitorRecordList" :key="item.id" class="visitor-item">
<div class="visitor-info">
<p><span>访客姓名</span>{{ item.name }}</p>
<p><span>联系方式</span>{{ item.phone }}</p>
<p><span>来访时间</span>{{ item.hours }}</p>
<p><span>被访人</span>{{ item.visitingName }}</p>
<p><span>来访事由</span>{{ item.reason }}</p>
</div>
<div class="visitor-status" :class="getStatusInfo(item.status).className">
{{ getStatusInfo(item.status).statusName }}
</div>
</div>
<div v-if="visitorRecordList.length === 0" class="no-data">暂无数据</div>
</div>
</div>
</template>
<script setup>
import {onMounted, reactive, ref, computed} from 'vue'
import {useStore} from 'vuex'
import {getVisitorRecord} from '@/api/admin.js'
import {Toast} from 'vant'
const store = useStore()
const visitorRecordList = ref([])
const visitorLoading = ref(false)
const visitorStatus = ref(-2) // 默认待来访
const visitorQuery = reactive({
status: -2,
size: 100,
current: 1
})
// 使用computed确保响应式
const statusTabs = computed(() => store.state.statusTabs)
const getStatusInfo = (status) => store.getters.getStatusInfo(status)
// 初始查询各分组数量
const fetchCounts = () => {
const groups = [
{ value: -1, query: { status: -1, size: 1000, current: 1 } },
{ value: -2, query: { status: -2, size: 1000, current: 1 } },
{ value: -3, query: { status: -3, size: 1000, current: 1 } }
]
return Promise.all(groups.map(g => getVisitorRecord(g.query)))
.then(results => {
results.forEach((res, index) => {
statusTabs.value[index].count = res.data?.length || 0
statusTabs.value[index].fetched = true // 标记为已查询
})
})
.catch(err => {
console.error('获取分组数量失败:', err)
})
}
const fetchVisitorRecords = () => {
visitorLoading.value = true
getVisitorRecord(visitorQuery).then(res => {
visitorRecordList.value = res.data || []
visitorLoading.value = false
}).catch(err => {
console.error('获取访客记录失败:', err)
visitorLoading.value = false
Toast.fail('获取访客记录失败')
})
}
// 初始加载
const init = async () => {
await fetchCounts() // 获取各分组数量
fetchVisitorRecords() // 获取当前tab数据
}
const changeVisitorStatus = (status) => {
visitorStatus.value = status
visitorQuery.status = status
fetchVisitorRecords()
}
onMounted(() => {
init()
})
</script>
<style lang="scss" scoped>
.visitor-manage-page {
padding: 0.4rem;
height: 100%;
overflow-y: auto;
background: #f5f5f5;
.status-tabs {
display: flex;
gap: 0.3rem;
margin-bottom: 0.4rem;
span {
flex: 1;
text-align: center;
padding: 0.2rem;
font-size: 0.37rem;
&.active {
color: #1989fa;
font-weight: bold;
}
.count {
font-size: 0.28rem;
color: #666;
margin-left: 0.1rem;
}
}
}
.visitor-list {
.visitor-item {
background: #fff;
border-radius: 0.2rem;
padding: 0.3rem;
margin-bottom: 0.3rem;
display: flex;
justify-content: space-between;
align-items: center;
.visitor-info {
flex: 1;
p {
margin: 0.1rem 0;
font-size: 0.32rem;
color: #666;
span {
color: #333;
}
}
}
.visitor-status {
padding: 0.1rem 0.2rem;
border-radius: 0.1rem;
font-size: 0.28rem;
min-width: 1.2rem;
text-align: center;
&.wait {
background: #fff7cc;
color: #e6a23c;
}
&.pass {
background: #f0f9eb;
color: #67c23a;
}
&.reject {
background: #fef0f0;
color: #f56c6c;
}
&.cancel {
background: #fef0f0;
color: #909399;
}
&.pending {
background: #ecf5ff;
color: #409eff;
}
&.visited {
background: #f0f9eb;
color: #67c23a;
}
&.overdue {
background: #fef0f0;
color: #e6a23c;
}
&.not-visited {
background: #f4f4f5;
color: #909399;
}
&.finished {
background: #f5f5f5;
color: #999;
}
&.end {
background: #f5f5f5;
color: #909399;
}
&.default {
background: #f5f5f5;
color: #666;
}
}
}
.no-data {
text-align: center;
padding: 1rem;
color: #999;
}
}
}
</style>
+179 -86
View File
@@ -1,14 +1,15 @@
<template> <template>
<div class='box'> <div class='box'>
<header class="tabbar"> <header class="tabbar">
<div @click="onClickTabbar(item)" v-for="item in stateEnum" class="tabbar-li" :key="item.status" :class="query.status == item.status ? 'active' : ''"> <div @click="onClickTabbar(item)" v-for="item in statusTabs" class="tabbar-li" :key="item.value"
{{ item.statusName }} :class="query.status === item.value ? 'active' : ''">
<img v-if="query.status == item.status" src="../assets/maomaochong.png" alt=""> {{ item.label }}
<img v-if="query.status === item.value" src="../assets/maomaochong.png" alt="">
</div> </div>
</header> </header>
<div v-if="recordArr.length" class="content"> <div v-if="recordArr.length" class="content">
<div @click="onClcikItem(item)" v-for="item in recordArr" class="wait-list" :key="item.id"> <div @click="onClickItem(item)" v-for="item in recordArr" class="wait-list" :key="item.id">
<h5>被拜访人{{ item.visitingName }}</h5> <h5>被拜访人{{ item.visitingName }}</h5>
<h5>拜访时间{{ item.hours }}</h5> <h5>拜访时间{{ item.hours }}</h5>
<div class="apply-box"> <div class="apply-box">
@@ -17,11 +18,11 @@
<p><span>随行人数</span><span>{{ item.visitingNum }} </span></p> <p><span>随行人数</span><span>{{ item.visitingNum }} </span></p>
<p><span>来访事由</span><span>{{ item.reason }}</span></p> <p><span>来访事由</span><span>{{ item.reason }}</span></p>
</div> </div>
<!-- refuse 拒绝 pass 通过 状态 --> <!-- 显示完整状态 -->
<div class="status" :class="stateEnum[item.status].className">{{ stateEnum[item.status].statusName }}</div> <div class="status" :class="getStatusInfo(item.status).className">{{ getStatusInfo(item.status).statusName }}</div>
</div> </div>
</div> </div>
<div v-if="isLoading && recordArr.length == 0" class="content"> <div v-if="isLoading && recordArr.length === 0" class="content">
<div class="no-have"> <div class="no-have">
暂无数据 暂无数据
</div> </div>
@@ -30,162 +31,254 @@
</template> </template>
<script setup> <script setup>
import { ref, reactive } from 'vue' import {ref, reactive, computed} from 'vue'
import { useRouter, useRoute } from 'vue-router' import {useRouter, useRoute} from 'vue-router'
import { useStore } from 'vuex' import {useStore} from 'vuex'
import dd from 'dingtalk-jsapi' import dd from 'dingtalk-jsapi'
import { Toast, ImagePreview } from 'vant'; import {Toast, ImagePreview} from 'vant';
import { rulesForm, resetForm, rulesPhone } from '@/com/index' import {rulesForm, resetForm, rulesPhone} from '@/com/index'
import { getToken, getVisitorRecord } from '@/api/admin.js' import {getToken, getVisitorRecord} from '@/api/admin.js'
/* @data */ /* @data */
const router = useRouter() const router = useRouter()
const route = useRoute() const route = useRoute()
const store = useStore() const store = useStore()
const stateEnum = store.state.stateEnum const statusTabs = computed(() => store.state.statusTabs)
const recordArr = ref([]) const recordArr = ref([])
const isLoading = ref(false) const isLoading = ref(false)
const query = reactive({ // 获取状态信息
status: 0, const getStatusInfo = (status) => {
size: -1, return store.getters.getStatusInfo(status)
current: 1, }
})
console.log(route) const query = reactive({
status: -1, // 默认待处理(父状态)
size: -1,
current: 1,
})
console.log(route)
/* @setup */ /* @setup */
/* @method */ /* @method */
const onClcikItem = (item) =>{ const onClickItem = (item) => {
// window.location.href = item.instanceUrl // window.location.href = item.instanceUrl
// window.open(item.instanceUrl, '审批详情'); // window.open(item.instanceUrl, '审批详情');
dd.openLink({ // 检查instanceUrl是否存在且非空
url: item.instanceUrl, let instanceUrl;
success: () => {},
fail: () => {}, if (item.instanceUrl && item.instanceUrl.trim() !== '') {
complete: () => {}, instanceUrl = item.instanceUrl;
}); }else{
instanceUrl = `https://aflow.dingtalk.com/dingtalk/mobile/homepage.htm?corpid=ding03f8e88dde9b426835c2f4657eb6378f&dd_share=false&showmenu=false&dd_progress=false&back=native&procInstId=${item.instanceId}&taskId=&swfrom=isv&dinghash=approval&dtaction=os&dd_from=corp#approval`;
} }
const onClickTabbar = (item) =>{ dd.openLink({
if(query.status != item.status){ url: instanceUrl,
query.status = item.status success: () => {
recordArr.value = [] },
visitorList() fail: () => {
} },
complete: () => {
},
});
}
const onClickTabbar = (item) => {
if (query.status !== item.value) {
query.status = item.value
recordArr.value = []
visitorList()
} }
const visitorList = () =>{
Toast.loading({ duration: 0, forbidClick: true, message: '加载中', }); }
getVisitorRecord(query).then(res =>{ const visitorList = () => {
isLoading.value = true Toast.loading({duration: 0, forbidClick: true, message: '加载中',});
recordArr.value = res.data getVisitorRecord(query).then(res => {
}).catch(err =>{ isLoading.value = true
isLoading.value = true recordArr.value = res.data
}) }).catch(err => {
} isLoading.value = true
visitorList() })
}
visitorList()
</script> </script>
<style lang='scss' scoped> <style lang='scss' scoped>
.box{ .box {
background: #F9FBFC;; background: #F9FBFC;;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
.tabbar{
height: 1.8933rem /* 142/75 */; .tabbar {
line-height: .6133rem /* 46/75 */; height: 1.8933rem /* 142/75 */
;
line-height: .6133rem /* 46/75 */
;
display: flex; display: flex;
font-size: $md-size; font-size: $md-size;
font-family: Source Han Sans CN, Source Han Sans CN; font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400; font-weight: 400;
color: #999999; color: #999999;
padding: .56rem /* 42/75 */ .4rem /* 30/75 */ 0; padding: .56rem /* 42/75 */
.4rem /* 30/75 */
0;
box-sizing: border-box; box-sizing: border-box;
.tabbar-li{
.tabbar-li {
position: relative; position: relative;
flex: 1; flex: 1;
text-align: center; text-align: center;
img{
img {
position: absolute; position: absolute;
left: 50%; left: 50%;
transform: translateX(-50%); transform: translateX(-50%);
bottom: .48rem /* 36/75 */; bottom: .48rem /* 36/75 */
width: .6133rem /* 46/75 */; ;
height: .2667rem /* 20/75 */; width: .6133rem /* 46/75 */
;
height: .2667rem /* 20/75 */
;
} }
} }
.active{
.active {
font-size: $size30; font-size: $size30;
font-family: Source Han Sans CN, Source Han Sans CN; font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 500; font-weight: 500;
color: #222222; color: #222222;
} }
} }
.content{
.content {
width: 100%; width: 100%;
flex: 1; flex: 1;
padding: 0 .4rem /* 30/75 */; padding: 0 .4rem /* 30/75 */
;
box-sizing: border-box; box-sizing: border-box;
overflow: auto; overflow: auto;
.wait-list{
.wait-list {
position: relative; position: relative;
width: 100%; width: 100%;
// height: 470px; // height: 470px;
background: #FFFFFF; background: #FFFFFF;
border-radius: .2667rem /* 20/75 */; border-radius: .2667rem /* 20/75 */
padding: .48rem /* 36/75 */; ;
padding: .48rem /* 36/75 */
;
box-sizing: border-box; box-sizing: border-box;
margin-bottom: .2667rem /* 20/75 */; margin-bottom: .2667rem /* 20/75 */
h5{ ;
height: .6667rem /* 50/75 */;
h5 {
height: .6667rem /* 50/75 */
;
font-size: $md-size; font-size: $md-size;
font-family: Source Han Sans CN, Source Han Sans CN; font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400; font-weight: 400;
color: #222222; color: #222222;
} }
.apply-box{
.apply-box {
width: 100%; width: 100%;
padding: .4rem /* 30/75 */ .3733rem /* 28/75 */ .5067rem /* 38/75 */; padding: .4rem /* 30/75 */
.3733rem /* 28/75 */
.5067rem /* 38/75 */
;
box-sizing: border-box; box-sizing: border-box;
border-radius: .1333rem /* 10/75 */; border-radius: .1333rem /* 10/75 */
;
font-size: $md-size; font-size: $md-size;
font-family: Source Han Sans CN, Source Han Sans CN; font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400; font-weight: 400;
color: #999999; color: #999999;
line-height: .72rem /* 54/75 */; line-height: .72rem /* 54/75 */
;
background: #F5F7F9; background: #F5F7F9;
span:last-child{
span:last-child {
color: #5F6583; color: #5F6583;
} }
} }
.status{
.status {
position: absolute; position: absolute;
top: 0; top: 0;
right: 0; right: 0;
width: 1.52rem /* 114/75 */; width: 1.52rem /* 114/75 */
height: .6667rem /* 50/75 */; ;
line-height: .6667rem /* 50/75 */; height: .6667rem /* 50/75 */
;
line-height: .6667rem /* 50/75 */
;
background: #FFE9E4; background: #FFE9E4;
border-radius: 0px .2667rem /* 20/75 */ 0px .1333rem /* 10/75 */; border-radius: 0px .2667rem /* 20/75 */
0px .1333rem /* 10/75 */
;
color: #FF6643; color: #FF6643;
font-size: $sm-size; font-size: $sm-size;
font-family: Source Han Sans CN, Source Han Sans CN; font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400; font-weight: 400;
color: #FF6643; color: #FF6643;
text-align: center; text-align: center;
} }
.refuse{
.refuse {
color: #5F6583; color: #5F6583;
background: #EAEEF6; background: #EAEEF6;
} }
.pass{
.pass {
color: #30BF78; color: #30BF78;
background: #E7F8F5; background: #E7F8F5;
} }
// 新增状态样式
.wait {
color: #E6A23C;
background: #FFF7CC;
}
.cancel {
color: #909399;
background: #F4F4F5;
}
.pending {
color: #409EFF;
background: #ECF5FF;
}
.visited {
color: #67C23A;
background: #F0F9EB;
}
.overdue {
color: #E6A23C;
background: #FEF0F0;
}
.not-visited {
color: #909399;
background: #F4F4F5;
}
.finished {
color: #999999;
background: #F5F5F5;
}
.default {
color: #666666;
background: #F5F5F5;
}
} }
} }
} }
+130
View File
@@ -0,0 +1,130 @@
const fs = require('fs');
const path = require('path');
function changeVersion() {
// 直接读取.env文件中的版本号
const envPath = path.join(__dirname, '.env');
let currentVersion = '1.0.0'; // 默认版本号
if (fs.existsSync(envPath)) {
const fileContent = fs.readFileSync(envPath, 'utf-8');
const versionMatch = fileContent.match(/VITE_APP_VERSION\s*=\s*([^\r\n]+)/);
if (versionMatch && versionMatch[1]) {
currentVersion = versionMatch[1].trim();
}
}
// 解析版本号
const versionParts = currentVersion.split('.');
if (versionParts.length < 3) {
throw new Error('Invalid version format');
}
// 获取当前环境
const env = process.env.NODE_ENV || 'development';
let newVersion;
const lastPart = versionParts[versionParts.length - 1];
const today = new Date().toISOString().slice(0, 10).replace(/-/g, ''); // YYYYMMDD 格式 (8位)
// 检查最后一组是否以8位日期开头 (支持各种分隔符,如 20250906-01 或 20250906_01)
const dateMatch = lastPart.match(/^(\d{8})[^\d]?(\d*)$/);
if (dateMatch) {
// 最后一组以8位日期开头
const currentDatePart = dateMatch[1];
const sequencePart = dateMatch[2];
if (currentDatePart === today) {
// 日期相同,根据环境递增序号
const currentSequence = sequencePart ? parseInt(sequencePart) : 0;
let newSequence;
if (env === 'production') {
// 生产环境:递增到下一个十位整数
newSequence = Math.ceil((currentSequence + 1) / 10) * 10;
} else if (env === 'test') {
// 测试环境:递增到下一个十位整数,但跳过二十及其倍数
newSequence = getNextTestSequence(currentSequence);
} else {
// 开发环境:递增但避开十位整数
newSequence = getNextDevSequence(currentSequence);
}
// 保持三位数格式,不足三位前面补0
const formattedSequence = newSequence.toString().padStart(2, '0');
versionParts[versionParts.length - 1] = today + '-' + formattedSequence;
} else {
// 日期不同,重置序号
let resetSequence;
if (env === 'production' || env === 'prod') {
resetSequence = '20';
} else if (env === 'test') {
resetSequence = '10';
} else {
resetSequence = '01';
}
versionParts[versionParts.length - 1] = today + '-' + resetSequence;
}
} else {
// 最后一组不是日期格式,检查是否为纯数字
if (/^\d+$/.test(lastPart)) {
// 是纯数字,按照原来的方式递增
versionParts[versionParts.length - 1] = parseInt(lastPart) + 1;
} else {
// 不是纯数字,添加新的日期序号
let initialSequence;
if (env === 'production' || env === 'prod') {
initialSequence = '20';
} else if (env === 'test') {
initialSequence = '10';
} else {
initialSequence = '01';
}
versionParts.push(today + '-' + initialSequence);
}
}
newVersion = versionParts.join('.');
// 修改版本号
if (fs.existsSync(envPath)) {
const file = fs.readFileSync(envPath, 'utf-8');
// 使用正则表达式更可靠地替换版本号
const versionRegex = /VITE_APP_VERSION\s*=\s*.*/;
const newVersionLine = `VITE_APP_VERSION=${newVersion}`;
const result = file.replace(versionRegex, newVersionLine);
// 同步写文件
fs.writeFileSync(envPath, result, 'utf-8');
}
return newVersion;
}
// 获取下一个测试环境序号(十位整数但跳过百位整数)
function getNextTestSequence(current) {
let next = Math.ceil((current + 1) / 10) * 10;
// 跳过百位整数
while (next % 20 === 0) {
next += 10;
}
return next;
}
// 获取下一个开发环境序号(避开十位整数)
function getNextDevSequence(current) {
let next = current + 1;
// 跳过十位整数和百位整数
while (next % 10 === 0) {
next++;
}
return next;
}
module.exports = changeVersion;
+75 -46
View File
@@ -1,52 +1,81 @@
import { defineConfig } from "vite"; import {defineConfig, loadEnv} from "vite";
import vue from "@vitejs/plugin-vue"; import vue from "@vitejs/plugin-vue";
import { resolve } from "path"; import {resolve} from "path";
import changeVersion from "./update.version.js";
import styleImport, { VantResolve } from "vite-plugin-style-import"; import styleImport, {VantResolve} from "vite-plugin-style-import";
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig({ export default defineConfig(({mode}) => {
plugins: [ // 更新版本号
vue(), const appVersion = changeVersion();
styleImport({
resolves: [VantResolve()], // 根据不同的环境加载不同的配置
libs: [ const isDev = mode === 'development'
{ const isTest = mode === 'test'
libraryName: "vant", const isProd = mode === 'production'
esModule: true,
resolveStyle: (name) => `../es/${name}/style`, //https://blog.csdn.net/yunchong_zhao/article/details/123552423 // 加载环境变量
const env = loadEnv(mode, process.cwd(), '');
const apiUrl = env.VITE_APP_API_URL || 'http://localhost:8080';
return {
define: {
__APP_VERSION__: JSON.stringify(appVersion)
}, },
], plugins: [
}), vue(),
], styleImport({
server: { resolves: [VantResolve()],
host: "0.0.0.0", //设置地址:http://localhost libs: [
port: 4000, // 设置服务启动端口号 {
hmr: false, // 关闭热更新 libraryName: "vant",
// open: true, // 设置服务启动时是否自动打开浏览器 esModule: true,
// cors: true, // 允许跨域 resolveStyle: (name) => `../es/${name}/style`, //https://blog.csdn.net/yunchong_zhao/article/details/123552423
// proxy: { },
// '/api': { ],
// target: 'http://dining.lzyyj.com/prod-api',//代理的地址 }),
// changeOrigin: true, ],
// // 路径重写 pathRewrite无效使用rewrite server: {
// // pathRewrite: { '/api': '' // 用'/api'代替target里面的地址 }, host: "0.0.0.0", //设置地址:http://localhost
// rewrite: path => path.replace(/^\/api/, '') port: 4000, // 设置服务启动端口号
// } hmr: false, // 关闭热更新
// } open: true, // 设置服务启动时是否自动打开浏览器
}, cors: true, // 允许跨域
resolve: { // 添加自定义头部
alias: { headers: {
"@": resolve(__dirname, "src"), 'Cache-Control': 'no-cache, no-store, must-revalidate',
}, 'Pragma': 'no-cache',
}, 'Expires': '0'
css: { },
// css预处理器 proxy: {
preprocessorOptions: { '/dev-api': {
scss: { target: apiUrl,//代理的地址
charset: false, changeOrigin: true,
additionalData: '@import "./src/assets/style/index.scss";', // 路径重写 pathRewrite无效使用rewrite
}, // pathRewrite: { '/api': '' // 用'/api'代替target里面的地址 },
}, rewrite: path => path.replace(/^\/dev-api/, '')
}, }
}
},
resolve: {
alias: {
"@": resolve(__dirname, "src"),
},
},
css: {
// css预处理器
preprocessorOptions: {
scss: {
charset: false,
additionalData: '@import "./src/assets/style/index.scss";',
}
}
},
// 根据不同环境设置构建选项
build: {
outDir: isProd ? 'dist' : isTest ? 'dist-test' : isDev ? 'dist-dev' :'dist-dev'
}
}
}); });
+283 -188
View File
@@ -7,10 +7,115 @@
resolved "https://registry.npmmirror.com/@babel/parser/-/parser-7.23.3.tgz#0ce0be31a4ca4f1884b5786057cadcb6c3be58f9" resolved "https://registry.npmmirror.com/@babel/parser/-/parser-7.23.3.tgz#0ce0be31a4ca4f1884b5786057cadcb6c3be58f9"
integrity sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw== integrity sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==
"@esbuild/linux-loong64@0.14.54": "@esbuild/android-arm64@0.18.20":
version "0.14.54" version "0.18.20"
resolved "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz#de2a4be678bd4d0d1ffbb86e6de779cde5999028" resolved "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz#984b4f9c8d0377443cc2dfcef266d02244593622"
integrity sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw== integrity sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==
"@esbuild/android-arm@0.18.20":
version "0.18.20"
resolved "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.18.20.tgz#fedb265bc3a589c84cc11f810804f234947c3682"
integrity sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==
"@esbuild/android-x64@0.18.20":
version "0.18.20"
resolved "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.18.20.tgz#35cf419c4cfc8babe8893d296cd990e9e9f756f2"
integrity sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==
"@esbuild/darwin-arm64@0.18.20":
version "0.18.20"
resolved "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz#08172cbeccf95fbc383399a7f39cfbddaeb0d7c1"
integrity sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==
"@esbuild/darwin-x64@0.18.20":
version "0.18.20"
resolved "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz#d70d5790d8bf475556b67d0f8b7c5bdff053d85d"
integrity sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==
"@esbuild/freebsd-arm64@0.18.20":
version "0.18.20"
resolved "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz#98755cd12707f93f210e2494d6a4b51b96977f54"
integrity sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==
"@esbuild/freebsd-x64@0.18.20":
version "0.18.20"
resolved "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz#c1eb2bff03915f87c29cece4c1a7fa1f423b066e"
integrity sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==
"@esbuild/linux-arm64@0.18.20":
version "0.18.20"
resolved "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz#bad4238bd8f4fc25b5a021280c770ab5fc3a02a0"
integrity sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==
"@esbuild/linux-arm@0.18.20":
version "0.18.20"
resolved "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz#3e617c61f33508a27150ee417543c8ab5acc73b0"
integrity sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==
"@esbuild/linux-ia32@0.18.20":
version "0.18.20"
resolved "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz#699391cccba9aee6019b7f9892eb99219f1570a7"
integrity sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==
"@esbuild/linux-loong64@0.18.20":
version "0.18.20"
resolved "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz#e6fccb7aac178dd2ffb9860465ac89d7f23b977d"
integrity sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==
"@esbuild/linux-mips64el@0.18.20":
version "0.18.20"
resolved "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz#eeff3a937de9c2310de30622a957ad1bd9183231"
integrity sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==
"@esbuild/linux-ppc64@0.18.20":
version "0.18.20"
resolved "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz#2f7156bde20b01527993e6881435ad79ba9599fb"
integrity sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==
"@esbuild/linux-riscv64@0.18.20":
version "0.18.20"
resolved "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz#6628389f210123d8b4743045af8caa7d4ddfc7a6"
integrity sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==
"@esbuild/linux-s390x@0.18.20":
version "0.18.20"
resolved "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz#255e81fb289b101026131858ab99fba63dcf0071"
integrity sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==
"@esbuild/linux-x64@0.18.20":
version "0.18.20"
resolved "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz#c7690b3417af318a9b6f96df3031a8865176d338"
integrity sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==
"@esbuild/netbsd-x64@0.18.20":
version "0.18.20"
resolved "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz#30e8cd8a3dded63975e2df2438ca109601ebe0d1"
integrity sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==
"@esbuild/openbsd-x64@0.18.20":
version "0.18.20"
resolved "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz#7812af31b205055874c8082ea9cf9ab0da6217ae"
integrity sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==
"@esbuild/sunos-x64@0.18.20":
version "0.18.20"
resolved "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz#d5c275c3b4e73c9b0ecd38d1ca62c020f887ab9d"
integrity sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==
"@esbuild/win32-arm64@0.18.20":
version "0.18.20"
resolved "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz#73bc7f5a9f8a77805f357fab97f290d0e4820ac9"
integrity sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==
"@esbuild/win32-ia32@0.18.20":
version "0.18.20"
resolved "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz#ec93cbf0ef1085cc12e71e0d661d20569ff42102"
integrity sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==
"@esbuild/win32-x64@0.18.20":
version "0.18.20"
resolved "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz#786c5f41f043b07afb1af37683d7c33668858f6d"
integrity sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==
"@jridgewell/sourcemap-codec@^1.4.15": "@jridgewell/sourcemap-codec@^1.4.15":
version "1.4.15" version "1.4.15"
@@ -40,10 +145,10 @@
resolved "https://registry.npmmirror.com/@vant/use/-/use-1.6.0.tgz#237df3091617255519552ca311ffdfea9de59001" resolved "https://registry.npmmirror.com/@vant/use/-/use-1.6.0.tgz#237df3091617255519552ca311ffdfea9de59001"
integrity sha512-PHHxeAASgiOpSmMjceweIrv2AxDZIkWXyaczksMoWvKV2YAYEhoizRuk/xFnKF+emUIi46TsQ+rvlm/t2BBCfA== integrity sha512-PHHxeAASgiOpSmMjceweIrv2AxDZIkWXyaczksMoWvKV2YAYEhoizRuk/xFnKF+emUIi46TsQ+rvlm/t2BBCfA==
"@vitejs/plugin-vue@^2.3.0": "@vitejs/plugin-vue@4":
version "2.3.4" version "4.6.2"
resolved "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-2.3.4.tgz#966a6279060eb2d9d1a02ea1a331af071afdcf9e" resolved "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-4.6.2.tgz#057d2ded94c4e71b94e9814f92dcd9306317aa46"
integrity sha512-IfFNbtkbIm36O9KB8QodlwwYvTEsJb4Lll4c2IwB3VHc2gie2mSPtSzL0eYay7X2jd/2WX02FjSGTWR6OPr/zg== integrity sha512-kqf7SGFoG+80aZG6Pf+gsZIVvGSCKE98JbiWqcCV9cThtg91Jav0yvYFC9Zb+jKetNGF6ZKeoaxgZfND21fWKw==
"@vue/compiler-core@3.3.8": "@vue/compiler-core@3.3.8":
version "3.3.8" version "3.3.8"
@@ -153,13 +258,13 @@ asynckit@^0.4.0:
resolved "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" resolved "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
axios@^1.6.2: axios@1.12.0:
version "1.6.2" version "1.12.0"
resolved "https://registry.npmmirror.com/axios/-/axios-1.6.2.tgz#de67d42c755b571d3e698df1b6504cde9b0ee9f2" resolved "https://registry.npmmirror.com/axios/-/axios-1.12.0.tgz#11248459be05a5ee493485628fa0e4323d0abfc3"
integrity sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A== integrity sha512-oXTDccv8PcfjZmPGlWsPSwtOJCZ/b6W5jAMCNcfwJbCzDckwG0jrYJFaWH1yvivfCXjVzV/SPDEhMB3Q+DSurg==
dependencies: dependencies:
follow-redirects "^1.15.0" follow-redirects "^1.15.6"
form-data "^4.0.0" form-data "^4.0.4"
proxy-from-env "^1.1.0" proxy-from-env "^1.1.0"
base64-arraybuffer@^1.0.2: base64-arraybuffer@^1.0.2:
@@ -179,6 +284,14 @@ braces@~3.0.2:
dependencies: dependencies:
fill-range "^7.0.1" fill-range "^7.0.1"
call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2:
version "1.0.2"
resolved "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6"
integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==
dependencies:
es-errors "^1.3.0"
function-bind "^1.1.2"
camel-case@^4.1.2: camel-case@^4.1.2:
version "4.1.2" version "4.1.2"
resolved "https://registry.npmmirror.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a" resolved "https://registry.npmmirror.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a"
@@ -284,137 +397,74 @@ dot-case@^3.0.4:
no-case "^3.0.4" no-case "^3.0.4"
tslib "^2.0.3" tslib "^2.0.3"
dunder-proto@^1.0.1:
version "1.0.1"
resolved "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a"
integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==
dependencies:
call-bind-apply-helpers "^1.0.1"
es-errors "^1.3.0"
gopd "^1.2.0"
es-define-property@^1.0.1:
version "1.0.1"
resolved "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa"
integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==
es-errors@^1.3.0:
version "1.3.0"
resolved "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f"
integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
es-module-lexer@^0.9.3: es-module-lexer@^0.9.3:
version "0.9.3" version "0.9.3"
resolved "https://registry.npmmirror.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19" resolved "https://registry.npmmirror.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19"
integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ== integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==
esbuild-android-64@0.14.54: es-object-atoms@^1.0.0, es-object-atoms@^1.1.1:
version "0.14.54" version "1.1.1"
resolved "https://registry.npmmirror.com/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz#505f41832884313bbaffb27704b8bcaa2d8616be" resolved "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1"
integrity sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ== integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==
dependencies:
es-errors "^1.3.0"
esbuild-android-arm64@0.14.54: es-set-tostringtag@^2.1.0:
version "0.14.54" version "2.1.0"
resolved "https://registry.npmmirror.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.54.tgz#8ce69d7caba49646e009968fe5754a21a9871771" resolved "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d"
integrity sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg== integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==
dependencies:
es-errors "^1.3.0"
get-intrinsic "^1.2.6"
has-tostringtag "^1.0.2"
hasown "^2.0.2"
esbuild-darwin-64@0.14.54: esbuild@^0.18.10:
version "0.14.54" version "0.18.20"
resolved "https://registry.npmmirror.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.54.tgz#24ba67b9a8cb890a3c08d9018f887cc221cdda25" resolved "https://registry.npmmirror.com/esbuild/-/esbuild-0.18.20.tgz#4709f5a34801b43b799ab7d6d82f7284a9b7a7a6"
integrity sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug== integrity sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==
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: optionalDependencies:
"@esbuild/linux-loong64" "0.14.54" "@esbuild/android-arm" "0.18.20"
esbuild-android-64 "0.14.54" "@esbuild/android-arm64" "0.18.20"
esbuild-android-arm64 "0.14.54" "@esbuild/android-x64" "0.18.20"
esbuild-darwin-64 "0.14.54" "@esbuild/darwin-arm64" "0.18.20"
esbuild-darwin-arm64 "0.14.54" "@esbuild/darwin-x64" "0.18.20"
esbuild-freebsd-64 "0.14.54" "@esbuild/freebsd-arm64" "0.18.20"
esbuild-freebsd-arm64 "0.14.54" "@esbuild/freebsd-x64" "0.18.20"
esbuild-linux-32 "0.14.54" "@esbuild/linux-arm" "0.18.20"
esbuild-linux-64 "0.14.54" "@esbuild/linux-arm64" "0.18.20"
esbuild-linux-arm "0.14.54" "@esbuild/linux-ia32" "0.18.20"
esbuild-linux-arm64 "0.14.54" "@esbuild/linux-loong64" "0.18.20"
esbuild-linux-mips64le "0.14.54" "@esbuild/linux-mips64el" "0.18.20"
esbuild-linux-ppc64le "0.14.54" "@esbuild/linux-ppc64" "0.18.20"
esbuild-linux-riscv64 "0.14.54" "@esbuild/linux-riscv64" "0.18.20"
esbuild-linux-s390x "0.14.54" "@esbuild/linux-s390x" "0.18.20"
esbuild-netbsd-64 "0.14.54" "@esbuild/linux-x64" "0.18.20"
esbuild-openbsd-64 "0.14.54" "@esbuild/netbsd-x64" "0.18.20"
esbuild-sunos-64 "0.14.54" "@esbuild/openbsd-x64" "0.18.20"
esbuild-windows-32 "0.14.54" "@esbuild/sunos-x64" "0.18.20"
esbuild-windows-64 "0.14.54" "@esbuild/win32-arm64" "0.18.20"
esbuild-windows-arm64 "0.14.54" "@esbuild/win32-ia32" "0.18.20"
"@esbuild/win32-x64" "0.18.20"
estree-walker@^2.0.1, estree-walker@^2.0.2: estree-walker@^2.0.1, estree-walker@^2.0.2:
version "2.0.2" version "2.0.2"
@@ -428,18 +478,20 @@ fill-range@^7.0.1:
dependencies: dependencies:
to-regex-range "^5.0.1" to-regex-range "^5.0.1"
follow-redirects@^1.15.0: follow-redirects@^1.15.6:
version "1.15.3" version "1.15.11"
resolved "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a" resolved "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.11.tgz#777d73d72a92f8ec4d2e410eb47352a56b8e8340"
integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q== integrity sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==
form-data@^4.0.0: form-data@^4.0.4:
version "4.0.0" version "4.0.5"
resolved "https://registry.npmmirror.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" resolved "https://registry.npmmirror.com/form-data/-/form-data-4.0.5.tgz#b49e48858045ff4cbf6b03e1805cebcad3679053"
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== integrity sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==
dependencies: dependencies:
asynckit "^0.4.0" asynckit "^0.4.0"
combined-stream "^1.0.8" combined-stream "^1.0.8"
es-set-tostringtag "^2.1.0"
hasown "^2.0.2"
mime-types "^2.1.12" mime-types "^2.1.12"
fs-extra@^10.0.0: fs-extra@^10.0.0:
@@ -461,6 +513,30 @@ function-bind@^1.1.2:
resolved "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" resolved "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
get-intrinsic@^1.2.6:
version "1.3.0"
resolved "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01"
integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==
dependencies:
call-bind-apply-helpers "^1.0.2"
es-define-property "^1.0.1"
es-errors "^1.3.0"
es-object-atoms "^1.1.1"
function-bind "^1.1.2"
get-proto "^1.0.1"
gopd "^1.2.0"
has-symbols "^1.1.0"
hasown "^2.0.2"
math-intrinsics "^1.1.0"
get-proto@^1.0.1:
version "1.0.1"
resolved "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1"
integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==
dependencies:
dunder-proto "^1.0.1"
es-object-atoms "^1.0.0"
glob-parent@~5.1.2: glob-parent@~5.1.2:
version "5.1.2" version "5.1.2"
resolved "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" resolved "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
@@ -468,15 +544,32 @@ glob-parent@~5.1.2:
dependencies: dependencies:
is-glob "^4.0.1" is-glob "^4.0.1"
gopd@^1.2.0:
version "1.2.0"
resolved "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1"
integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==
graceful-fs@^4.1.6, graceful-fs@^4.2.0: graceful-fs@^4.1.6, graceful-fs@^4.2.0:
version "4.2.11" version "4.2.11"
resolved "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" 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== integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
hasown@^2.0.0: has-symbols@^1.0.3, has-symbols@^1.1.0:
version "2.0.0" version "1.1.0"
resolved "https://registry.npmmirror.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c" resolved "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338"
integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA== integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==
has-tostringtag@^1.0.2:
version "1.0.2"
resolved "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc"
integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==
dependencies:
has-symbols "^1.0.3"
hasown@^2.0.2:
version "2.0.2"
resolved "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003"
integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==
dependencies: dependencies:
function-bind "^1.1.2" function-bind "^1.1.2"
@@ -508,13 +601,6 @@ is-binary-path@~2.1.0:
dependencies: dependencies:
binary-extensions "^2.0.0" 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: is-extglob@^2.1.1:
version "2.1.1" version "2.1.1"
resolved "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" resolved "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
@@ -562,6 +648,11 @@ magic-string@^0.30.5:
dependencies: dependencies:
"@jridgewell/sourcemap-codec" "^1.4.15" "@jridgewell/sourcemap-codec" "^1.4.15"
math-intrinsics@^1.1.0:
version "1.1.0"
resolved "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9"
integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==
mime-db@1.52.0: mime-db@1.52.0:
version "1.52.0" version "1.52.0"
resolved "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" resolved "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
@@ -579,6 +670,11 @@ ms@2.1.2:
resolved "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" resolved "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
nanoid@^3.3.11:
version "3.3.11"
resolved "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b"
integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==
nanoid@^3.3.6: nanoid@^3.3.6:
version "3.3.7" version "3.3.7"
resolved "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" resolved "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8"
@@ -621,22 +717,31 @@ path-case@^3.0.4:
dot-case "^3.0.4" dot-case "^3.0.4"
tslib "^2.0.3" 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: picocolors@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" resolved "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
picocolors@^1.1.1:
version "1.1.1"
resolved "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2: picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2:
version "2.3.1" version "2.3.1"
resolved "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" resolved "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
postcss@^8.4.13, postcss@^8.4.31: postcss@^8.4.27:
version "8.5.6"
resolved "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz#2825006615a619b4f62a9e7426cc120b349a8f3c"
integrity sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==
dependencies:
nanoid "^3.3.11"
picocolors "^1.1.1"
source-map-js "^1.2.1"
postcss@^8.4.31:
version "8.4.31" version "8.4.31"
resolved "https://registry.npmmirror.com/postcss/-/postcss-8.4.31.tgz#92b451050a9f914da6755af352bdc0192508656d" resolved "https://registry.npmmirror.com/postcss/-/postcss-8.4.31.tgz#92b451050a9f914da6755af352bdc0192508656d"
integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ== integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==
@@ -662,19 +767,10 @@ readdirp@~3.6.0:
dependencies: dependencies:
picomatch "^2.2.1" picomatch "^2.2.1"
resolve@^1.22.0: rollup@^3.27.1:
version "1.22.8" version "3.29.5"
resolved "https://registry.npmmirror.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" resolved "https://registry.npmmirror.com/rollup/-/rollup-3.29.5.tgz#8a2e477a758b520fb78daf04bca4c522c1da8a54"
integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== integrity sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==
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: optionalDependencies:
fsevents "~2.3.2" fsevents "~2.3.2"
@@ -709,16 +805,16 @@ snake-case@^3.0.4:
resolved "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" resolved "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
source-map-js@^1.2.1:
version "1.2.1"
resolved "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
sourcemap-codec@^1.4.8: sourcemap-codec@^1.4.8:
version "1.4.8" version "1.4.8"
resolved "https://registry.npmmirror.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" resolved "https://registry.npmmirror.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== 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: text-segmentation@^1.0.3:
version "1.0.3" version "1.0.3"
resolved "https://registry.npmmirror.com/text-segmentation/-/text-segmentation-1.0.3.tgz#52a388159efffe746b24a63ba311b6ac9f2d7943" resolved "https://registry.npmmirror.com/text-segmentation/-/text-segmentation-1.0.3.tgz#52a388159efffe746b24a63ba311b6ac9f2d7943"
@@ -785,15 +881,14 @@ vite-plugin-style-import@^1.4.1:
fs-extra "^10.0.0" fs-extra "^10.0.0"
magic-string "^0.25.7" magic-string "^0.25.7"
vite@^2.9.0: vite@^4.0.5:
version "2.9.16" version "4.5.14"
resolved "https://registry.npmmirror.com/vite/-/vite-2.9.16.tgz#daf7ba50f5cc37a7bf51b118ba06bc36e97898e9" resolved "https://registry.npmmirror.com/vite/-/vite-4.5.14.tgz#2e652bc1d898265d987d6543ce866ecd65fa4086"
integrity sha512-X+6q8KPyeuBvTQV8AVSnKDvXoBMnTx8zxh54sOwmmuOdxkjMmEJXH2UEchA+vTMps1xw9vL64uwJOWryULg7nA== integrity sha512-+v57oAaoYNnO3hIu5Z/tJRZjq5aHM2zDve9YZ8HngVHbhk66RStobhb1sqPMIPEleV6cNKYK4eGrAbE9Ulbl2g==
dependencies: dependencies:
esbuild "^0.14.27" esbuild "^0.18.10"
postcss "^8.4.13" postcss "^8.4.27"
resolve "^1.22.0" rollup "^3.27.1"
rollup ">=2.59.0 <2.78.0"
optionalDependencies: optionalDependencies:
fsevents "~2.3.2" fsevents "~2.3.2"