Merge branch 'main' of http://124.243.245.42:3000/sdz/qingdao-employment-service
This commit is contained in:
3
App.vue
3
App.vue
@@ -1,7 +1,6 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { reactive, inject, onMounted } from 'vue';
|
import { reactive, inject, onMounted } from 'vue';
|
||||||
import { onLaunch, onShow, onHide } from '@dcloudio/uni-app';
|
import { onLaunch, onShow, onHide } from '@dcloudio/uni-app';
|
||||||
import { IncreaseRevie } from '@/common/all-in-one-listen.js';
|
|
||||||
import useUserStore from './stores/useUserStore';
|
import useUserStore from './stores/useUserStore';
|
||||||
import usePageAnimation from './hook/usePageAnimation';
|
import usePageAnimation from './hook/usePageAnimation';
|
||||||
import useDictStore from './stores/useDictStore';
|
import useDictStore from './stores/useDictStore';
|
||||||
@@ -22,7 +21,6 @@ import baseDB from '@/utils/db.js';
|
|||||||
import { $confirm } from '@/utils/modal.js';
|
import { $confirm } from '@/utils/modal.js';
|
||||||
import useLocationStore from '@/stores/useLocationStore';
|
import useLocationStore from '@/stores/useLocationStore';
|
||||||
const appword = 'aKd20dbGdFvmuwrt'; // 固定值
|
const appword = 'aKd20dbGdFvmuwrt'; // 固定值
|
||||||
let uQRListen = null;
|
|
||||||
let inactivityManager = null;
|
let inactivityManager = null;
|
||||||
let inactivityModalTimer = null;
|
let inactivityModalTimer = null;
|
||||||
|
|
||||||
@@ -57,7 +55,6 @@ onLaunch((options) => {
|
|||||||
}, 3000);
|
}, 3000);
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
uQRListen = new IncreaseRevie();
|
|
||||||
inactivityManager = new GlobalInactivityManager(handleInactivity, 60 * 1000);
|
inactivityManager = new GlobalInactivityManager(handleInactivity, 60 * 1000);
|
||||||
inactivityManager.start();
|
inactivityManager.start();
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -8,32 +8,46 @@ import {
|
|||||||
playTextDirectly
|
playTextDirectly
|
||||||
} from '@/hook/useTTSPlayer-all-in-one'
|
} from '@/hook/useTTSPlayer-all-in-one'
|
||||||
|
|
||||||
|
|
||||||
export class IncreaseRevie {
|
export class IncreaseRevie {
|
||||||
constructor(arg) {
|
constructor(arg) {
|
||||||
this.myEventCallback = this.myCallback.bind(this);
|
this.myEventCallback = this.myCallback.bind(this);
|
||||||
this._debounceTimer = null;
|
|
||||||
this.init();
|
this._debounceTimer = null; // 防抖计时器
|
||||||
|
this._initTimer = null; // 启动延时计时器
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
|
||||||
|
start() {
|
||||||
this.close();
|
this.close();
|
||||||
setTimeout(() => {
|
|
||||||
|
this._initTimer = setTimeout(() => {
|
||||||
if (window.hh?.on) {
|
if (window.hh?.on) {
|
||||||
|
console.log('开始监听 QR 扫码事件');
|
||||||
window.hh.on('initQRListener', this.myEventCallback);
|
window.hh.on('initQRListener', this.myEventCallback);
|
||||||
}
|
}
|
||||||
|
this._initTimer = null;
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
|
if (this._initTimer) {
|
||||||
|
clearTimeout(this._initTimer);
|
||||||
|
this._initTimer = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (window.hh?.off) {
|
if (window.hh?.off) {
|
||||||
window.hh.off('initQRListener', this.myEventCallback);
|
window.hh.off('initQRListener', this.myEventCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._debounceTimer) {
|
if (this._debounceTimer) {
|
||||||
clearTimeout(this._debounceTimer);
|
clearTimeout(this._debounceTimer);
|
||||||
this._debounceTimer = null;
|
this._debounceTimer = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
myCallback(res) {
|
myCallback(res) {
|
||||||
if (this._debounceTimer) {
|
if (this._debounceTimer) {
|
||||||
clearTimeout(this._debounceTimer);
|
clearTimeout(this._debounceTimer);
|
||||||
@@ -45,29 +59,189 @@ export class IncreaseRevie {
|
|||||||
}, 300);
|
}, 300);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async handleDebouncedCallback(res) {
|
async handleDebouncedCallback(res) {
|
||||||
if (res.data) {
|
if (res.data) {
|
||||||
const code = res.data.qrCode
|
const code = res.data.qrCode;
|
||||||
if (/^\d{6}$/.test(String(code))) {
|
if (/^\d{6}$/.test(String(code))) {
|
||||||
// 把code给到后端,后端拿code兑换用户信息,给前端返回token进行登录
|
|
||||||
// 一体机用户需要清空indexDB
|
|
||||||
$api.createRequest(`/app/qrcodeLogin/${code}`, {}, 'get').then((resData) => {
|
$api.createRequest(`/app/qrcodeLogin/${code}`, {}, 'get').then((resData) => {
|
||||||
useUserStore()
|
useUserStore()
|
||||||
.loginSetToken(resData.token)
|
.loginSetToken(resData.token)
|
||||||
.then((resume) => {
|
.then((resume) => {
|
||||||
playTextDirectly('登录成功')
|
playTextDirectly('登录成功');
|
||||||
|
// 根据是否有一体机岗位ID判断跳转路径
|
||||||
if (resume.data.jobTitleId) {
|
if (resume.data.jobTitleId) {
|
||||||
useUserStore().initSeesionId();
|
useUserStore().initSeesionId();
|
||||||
safeReLaunch('/pages/index/index');
|
safeReLaunch('/pages/index/index');
|
||||||
} else {
|
} else {
|
||||||
safeReLaunch('/pages/login/login');
|
safeReLaunch('/pages/login/login');
|
||||||
}
|
}
|
||||||
|
// 关闭监听,避免重复扫码
|
||||||
|
this.close();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
// 格式不对不做处理,或者提示
|
||||||
|
console.log('QR Code format mismatch');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$api.msg('识别失败')
|
$api.msg('识别失败');
|
||||||
playTextDirectly('识别失败')
|
playTextDirectly('识别失败');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export class FaceLoginService {
|
||||||
|
constructor() {
|
||||||
|
this.isInitialized = false;
|
||||||
|
this.defaultScope = "auth_user,yingpin"; // 默认聚合授权参数
|
||||||
|
|
||||||
|
this._retryTimer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 【新增】生命周期 - 开始
|
||||||
|
* 统一入口,通常在页面加载 (onLoad/onMounted) 时调用
|
||||||
|
*/
|
||||||
|
start(scope = null) {
|
||||||
|
// 启动前先清理可能存在的旧状态
|
||||||
|
this.close();
|
||||||
|
console.log("[FaceLogin] 服务启动...");
|
||||||
|
// 开始执行初始化逻辑
|
||||||
|
this.init(scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 【新增】生命周期 - 关闭
|
||||||
|
* 统一出口,通常在页面卸载 (onUnload/onUnmounted) 时调用
|
||||||
|
*/
|
||||||
|
close() {
|
||||||
|
// 1. 清除正在等待执行的重试定时器
|
||||||
|
if (this._retryTimer) {
|
||||||
|
clearTimeout(this._retryTimer);
|
||||||
|
this._retryTimer = null;
|
||||||
|
console.log("[FaceLogin] 已取消挂起的初始化重试");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 重置初始化状态
|
||||||
|
this.isInitialized = false;
|
||||||
|
|
||||||
|
console.log("[FaceLogin] 服务已关闭");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. 初始化刷脸服务
|
||||||
|
* (已修改:增加了对定时器的管理)
|
||||||
|
*/
|
||||||
|
async init(scope = null, retryCount = 1) {
|
||||||
|
const params = {
|
||||||
|
action: "initFace",
|
||||||
|
event: "open",
|
||||||
|
taskId: this._generateTaskId(),
|
||||||
|
params: {
|
||||||
|
serviceId: "",
|
||||||
|
query: "",
|
||||||
|
scope: scope || this.defaultScope
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this._bridgeCall(params);
|
||||||
|
this.isInitialized = true;
|
||||||
|
console.log("[FaceLogin] 初始化成功");
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`[FaceLogin] 初始化失败: ${err.message}`);
|
||||||
|
// 如果还有重试次数,且服务没有被手动 close 关闭
|
||||||
|
if (retryCount > 0) {
|
||||||
|
console.log("[FaceLogin] 3秒后尝试重新初始化...");
|
||||||
|
this._retryTimer = setTimeout(() => {
|
||||||
|
this._retryTimer = null; // 执行时清空引用
|
||||||
|
this.init(scope, retryCount - 1); // 递归重试
|
||||||
|
}, 3000);
|
||||||
|
} else {
|
||||||
|
// 重试耗尽,抛出错误
|
||||||
|
// throw err; // 可选:视业务逻辑决定是否阻断
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 2. 唤起 1:N 刷脸并获取 AuthCode
|
||||||
|
*/
|
||||||
|
async startFaceLogin() {
|
||||||
|
// 防御性编程:确保已初始化
|
||||||
|
if (!this.isInitialized) {
|
||||||
|
console.warn("[FaceLogin] 服务未初始化,尝试自动补救初始化...");
|
||||||
|
try {
|
||||||
|
// 尝试一次即时初始化(不重试)
|
||||||
|
await this.init(null, 0);
|
||||||
|
if (!this.isInitialized) throw new Error("初始化未完成");
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error("刷脸服务初始化失败,请稍后重试");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
action: "getFaceInfo",
|
||||||
|
event: "open",
|
||||||
|
taskId: this._generateTaskId()
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await this._bridgeCall(params);
|
||||||
|
const {
|
||||||
|
data
|
||||||
|
} = res;
|
||||||
|
// if (!data || !data.extInfo) {
|
||||||
|
// throw new Error("返回数据缺少 extInfo");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let extInfoObj;
|
||||||
|
// try {
|
||||||
|
// extInfoObj = JSON.parse(data.extInfo);
|
||||||
|
// } catch (e) {
|
||||||
|
// throw new Error("extInfo JSON解析失败");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (!extInfoObj.authCode) {
|
||||||
|
// throw new Error("未获取到 authCode");
|
||||||
|
// }
|
||||||
|
|
||||||
|
return data;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
const errMsg = error.message || error.subMessage || "刷脸失败";
|
||||||
|
throw new Error(errMsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基础 Bridge 调用封装
|
||||||
|
*/
|
||||||
|
_bridgeCall(params) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (typeof hh === 'undefined' || !hh.call) {
|
||||||
|
return reject(new Error("Bridge环境未就绪 (hh未定义)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
hh.call("ampeHHCommunication", params, (res) => {
|
||||||
|
res = JSON.parse(res)
|
||||||
|
if (res && res.success) {
|
||||||
|
resolve(res);
|
||||||
|
} else {
|
||||||
|
reject(res || {
|
||||||
|
message: "未知错误",
|
||||||
|
success: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_generateTaskId() {
|
||||||
|
return 'task_' + Date.now() + '_' + Math.floor(Math.random() * 1000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -193,16 +193,18 @@ import { storeToRefs } from 'pinia';
|
|||||||
import tabcontrolVue from './components/tabcontrol.vue';
|
import tabcontrolVue from './components/tabcontrol.vue';
|
||||||
import SelectJobs from '@/components/selectJobs/selectJobs.vue';
|
import SelectJobs from '@/components/selectJobs/selectJobs.vue';
|
||||||
import { reactive, inject, watch, ref, onMounted, onUnmounted } from 'vue';
|
import { reactive, inject, watch, ref, onMounted, onUnmounted } from 'vue';
|
||||||
import { onLoad, onShow } from '@dcloudio/uni-app';
|
import { onLoad, onShow, onHide } from '@dcloudio/uni-app';
|
||||||
import useUserStore from '@/stores/useUserStore';
|
import useUserStore from '@/stores/useUserStore';
|
||||||
import useDictStore from '@/stores/useDictStore';
|
import useDictStore from '@/stores/useDictStore';
|
||||||
import { playTextDirectly } from '@/hook/useTTSPlayer-all-in-one';
|
import { playTextDirectly } from '@/hook/useTTSPlayer-all-in-one';
|
||||||
|
import { IncreaseRevie, FaceLoginService } from '@/common/all-in-one-listen.js';
|
||||||
const { $api, navTo } = inject('globalFunction');
|
const { $api, navTo } = inject('globalFunction');
|
||||||
const { loginSetToken, getUserResume } = useUserStore();
|
const { loginSetToken, getUserResume } = useUserStore();
|
||||||
const { isMachineEnv, hasLogin } = storeToRefs(useUserStore());
|
const { isMachineEnv, hasLogin } = storeToRefs(useUserStore());
|
||||||
const { getDictSelectOption, oneDictData } = useDictStore();
|
const { getDictSelectOption, oneDictData } = useDictStore();
|
||||||
const openSelectPopup = inject('openSelectPopup');
|
const openSelectPopup = inject('openSelectPopup');
|
||||||
|
const qrHandler = new IncreaseRevie();
|
||||||
|
const faceService = new FaceLoginService();
|
||||||
// status
|
// status
|
||||||
const selectJobsModel = ref();
|
const selectJobsModel = ref();
|
||||||
const tabCurrent = ref(1);
|
const tabCurrent = ref(1);
|
||||||
@@ -233,18 +235,33 @@ const scanLineTop = ref(0);
|
|||||||
let scanInterval = null;
|
let scanInterval = null;
|
||||||
const countdown = ref(60);
|
const countdown = ref(60);
|
||||||
let countdownTimer = null;
|
let countdownTimer = null;
|
||||||
const loginMethod = ref('face'); // 'qrcode' / 'face'
|
const loginMethod = ref('qrcode'); // 'qrcode' / 'face'
|
||||||
|
|
||||||
onLoad((parmas) => {
|
onLoad((parmas) => {
|
||||||
getTreeselect();
|
getTreeselect();
|
||||||
if (!isMachineEnv.value) $api.msg('请完善微简历');
|
if (!isMachineEnv.value) {
|
||||||
|
$api.msg('请完善微简历');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (isMachineEnv) {
|
if (isMachineEnv) {
|
||||||
startCountdown();
|
startCountdown();
|
||||||
startScanAnimation();
|
startScanAnimation();
|
||||||
playTextDirectly('请进行用户登录');
|
faceService.start(); // 自动开始初始化流程
|
||||||
|
if (loginMethod.value === 'face') {
|
||||||
|
playTextDirectly('开始刷脸登录');
|
||||||
|
} else {
|
||||||
|
playTextDirectly('请进行登录');
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
if (loginMethod.value === 'face') {
|
||||||
|
handleFaceLogin();
|
||||||
|
}
|
||||||
|
if (loginMethod.value === 'qrcode') {
|
||||||
|
qrHandler.start();
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
@@ -289,12 +306,45 @@ const cancelLogin = () => {
|
|||||||
|
|
||||||
// 切换登录方式
|
// 切换登录方式
|
||||||
const switchLoginMethod = (method) => {
|
const switchLoginMethod = (method) => {
|
||||||
|
if (!isMachineEnv) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (loginMethod.value !== method) {
|
if (loginMethod.value !== method) {
|
||||||
loginMethod.value = method;
|
loginMethod.value = method;
|
||||||
|
switch (method) {
|
||||||
|
case 'qrcode':
|
||||||
|
faceService.close();
|
||||||
|
qrHandler.start();
|
||||||
|
playTextDirectly('扫码登录');
|
||||||
|
break;
|
||||||
|
case 'face':
|
||||||
|
qrHandler.close();
|
||||||
|
handleFaceLogin();
|
||||||
|
playTextDirectly('扫脸登录');
|
||||||
|
break;
|
||||||
|
}
|
||||||
resetCountdown();
|
resetCountdown();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
async function handleFaceLogin() {
|
||||||
|
try {
|
||||||
|
const authCode = await faceService.startFaceLogin();
|
||||||
|
console.log('拿到 AuthCode:', authCode);
|
||||||
|
// 调用后端登录接口...
|
||||||
|
} catch (err) {
|
||||||
|
this.$api.msg(err.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
qrHandler.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
onHide(() => {
|
||||||
|
qrHandler.close();
|
||||||
|
});
|
||||||
|
|
||||||
// 开始动画
|
// 开始动画
|
||||||
const startScanAnimation = () => {
|
const startScanAnimation = () => {
|
||||||
clearInterval(scanInterval);
|
clearInterval(scanInterval);
|
||||||
|
|||||||
Reference in New Issue
Block a user