This commit is contained in:
2025-12-25 09:40:44 +08:00
3 changed files with 239 additions and 18 deletions

View File

@@ -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;

View File

@@ -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);
}
} }

View File

@@ -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);