1571 lines
60 KiB
HTML
1571 lines
60 KiB
HTML
<!DOCTYPE HTML>
|
||
<html lang="zh-cn">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>user list</title>
|
||
|
||
<link href="resources/plugins/bootstrap-3.3.0/css/bootstrap.min.css" rel="stylesheet"/>
|
||
<link href="resources/plugins/material-design-iconic-font-2.2.0/css/material-design-iconic-font.min.css" rel="stylesheet"/>
|
||
<link href="resources/plugins/bootstrap-table-1.17.0/bootstrap-table.min.css" rel="stylesheet"/>
|
||
<link href="resources/plugins/waves-0.7.5/waves.min.css" rel="stylesheet"/>
|
||
<link href="resources/plugins/jquery-confirm/jquery-confirm.min.css" rel="stylesheet"/>
|
||
<link href="resources/css/common.css" rel="stylesheet"/>
|
||
<style>
|
||
.progress {
|
||
position: relative;
|
||
width: 100%; /* 确保进度条宽度适合 */
|
||
}
|
||
|
||
.progress-bar {
|
||
position: relative;
|
||
height: 20px; /* 设置进度条高度 */
|
||
display: flex;
|
||
align-items: center; /* 垂直居中 */
|
||
justify-content: center; /* 水平居中 */
|
||
}
|
||
|
||
.progress-text {
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 50%;
|
||
transform: translate(-50%, -50%);
|
||
display: inline-block;
|
||
color: #000; /* 默认颜色为黑色 */
|
||
transition: color 0.3s; /* 平滑过渡 */
|
||
white-space: nowrap; /* 防止文字换行 */
|
||
font-size: 14px; /* 可以根据需要调整字体大小 */
|
||
z-index: 1; /* 确保文字在进度条之上 */
|
||
}
|
||
|
||
/* 当进度超过50%时改变文字颜色 */
|
||
.progress-bar > .progress-text {
|
||
color: white;
|
||
}
|
||
.zmdi {
|
||
margin-right: 10px; /* 调整图标右侧间距 */
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div id="main">
|
||
<div id="toolbar">
|
||
<a class="waves-effect waves-button" href="javascript:;" onclick="createAction()"><i class="zmdi zmdi-account-add"></i><span class="lang" key="UISTR_USER_ADD">add user</span></a>
|
||
<a class="waves-effect waves-button" href="javascript:;" onclick="deleteAction()"><i class="zmdi zmdi-close"></i><span class="lang" key="UISTR_USER_DELETE">delete user</span></a>
|
||
<a class="waves-effect waves-button optional-btn" href="javascript:;" onclick="saveasexcelAction()"><i class="zmdi zmdi-case-download"></i><span class="lang" key="UISTR_SAVEASEXCEL">Save as Excel</span></a>
|
||
<a class="waves-effect waves-button optional-btn" href="javascript:;" onclick="importfromexcelAction()"><i class="zmdi zmdi-file-plus"></i><span class="lang" key="UISTR_IMPORTFROMEXCEL">Import from Excel</span></a>
|
||
<a class="waves-effect waves-button optional-btn" href="javascript:;" onclick="downloadImagesFromPhotouri()"><i class="zmdi zmdi-download"></i><span class="lang" key="UISTR_DOWNLOAD_PHOTOS">download all photos</span></a>
|
||
<a class="waves-effect waves-button optional-btn" href="javascript:;" onclick="uploadZIPAction()"><i class="zmdi zmdi-upload"></i><span class="lang" key="UISTR_UPLOAD_PHOTOS">upload photos from Zip</span></a>
|
||
<a id="downloadfingerprints" class="waves-effect waves-button optional-btn" href="javascript:;" onclick="downloadfingerprints()"><i class="zmdi zmdi-download"></i><span class="lang" key="UISTR_DOWNLOAD_FINGER">download all finger/palm</span></a>
|
||
<a id="uploadfingerprints" class="waves-effect waves-button optional-btn" href="javascript:;" onclick="uploadfingerprints()"><i class="zmdi zmdi-upload"></i><span class="lang" key="UISTR_UPLOAD_FINGER">upload finger/palm from CSV</span></a>
|
||
</div>
|
||
<table id="table"></table>
|
||
</div>
|
||
<!-- 添加模态窗口 -->
|
||
<div class="modal fade" id="uploadModal" tabindex="-1" role="dialog" aria-labelledby="uploadModalLabel" aria-hidden="true">
|
||
<div class="modal-dialog" role="document">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h5 class="modal-title" id="uploadModalLabel">Upload Users</h5>
|
||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||
<span aria-hidden="true">×</span>
|
||
</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<p><span id="uploading" class="lang" key="UISTR_UPLOADING">Uploading</span></p>
|
||
<p><span id="uploaded" class="lang" key="UISTR_UPLOADED">Uploaded</span></p>
|
||
<div class="progress">
|
||
<div class="progress-bar" role="progressbar" style="width: 0%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">
|
||
<span class="progress-text">0%</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button type="button" class="lang btn btn-secondary" data-dismiss="modal" key="UISTR_CLOSE">Close</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<script src="resources/plugins/jquery.1.12.4.min.js"></script>
|
||
<script src="resources/plugins/bootstrap-3.3.0/js/bootstrap.min.js"></script>
|
||
<script src="resources/plugins/bootstrap-table-1.17.0/bootstrap-table.min.js"></script>
|
||
<script src="resources/plugins/waves-0.7.5/waves.min.js"></script>
|
||
<script src="resources/plugins/jquery-confirm/jquery-confirm.min.js"></script>
|
||
<script src="resources/plugins/jquery.cookie.js"></script>
|
||
<script src="resources/js/notify.js"></script>
|
||
<script src="resources/js/lang.js"></script>
|
||
<script src="resources/js/common.js"></script>
|
||
<script src="resources/js/admin.js"></script>
|
||
<script src="resources/js/xlsx.full.min.js"></script>
|
||
<script src="resources/js/jszip.min.js"></script>
|
||
<script src="resources/js/FileSaver.min.js"></script>
|
||
<script>
|
||
let hasFpsize = $.cookie("fpsize") !== undefined && $.cookie("fpsize") !== "0";
|
||
let hasPalmsize = $.cookie("palmsize") !== undefined && $.cookie("palmsize") !== "0";
|
||
let deviceid = $.cookie("deviceid") || "1";
|
||
let hasElevator = $.cookie("elevator_control") !== undefined && $.cookie("elevator_control") !== "0";
|
||
let DI_CARD_DISP_FORMAT = parseInt($.cookie("CARD_DISP_FORMAT")) || 0;
|
||
let DI_DATE_FORMAT = parseInt($.cookie("DATE_FORMAT")) || 0;
|
||
let DI_USERID_FORMAT = parseInt($.cookie("USERID_FORMAT")) || 0;
|
||
let hasFacetemplate = $.cookie("facetemplate") !== undefined && $.cookie("facetemplate") !== "0";
|
||
let poithide = $.cookie("manufacturer") !== undefined && $.cookie("manufacturer")==="Piot Robotics";
|
||
check_permission();
|
||
async function initializeApp() {
|
||
try {
|
||
// 1. 并行执行 lang_load() 和 format_get()
|
||
await Promise.all([lang_load()]);
|
||
// 2. 顺序执行后续操作
|
||
await get_userlist();
|
||
// 3. 批量设置标题(同步操作)
|
||
const titleMappings = [
|
||
['id', lang.UISTR_ENROLL_ID],
|
||
['name', lang.UISTR_UserName],
|
||
['department', lang.UISTR_Department],
|
||
['shift', lang.UISTR_UserShift],
|
||
['admin', lang.UISTR_Privilege],
|
||
['face', lang.UISTR_FACE_ENROLL],
|
||
['palm', lang.UISTR_PALM_ENROLL],
|
||
['fingerprint', lang.UISTR_FINGER_ENROLL],
|
||
['password', lang.UISTR_PASSWORD_ENROLL],
|
||
['card', lang.UISTR_CARD_ENROLL],
|
||
['weekzone', lang.UISTR_MAINMENU_PASSTIMEID],
|
||
['group', hasElevator ? lang.UISTR_DIRECT_FLOOR : lang.UISTR_MAINMENU_GROUPID],
|
||
['access_times', lang.UISTR_ACCESS_TIMES],
|
||
['verifymode', lang.UISTR_PERSON_VERIFY_MODE],
|
||
['birthday', lang.UISTR_BIRTHDAY],
|
||
['starttime', lang.UISTR_START],
|
||
['endtime', lang.UISTR_END],
|
||
['userprofile', lang.UISTR_USER_PROFILE],
|
||
['photouri', lang.UISTR_Photo_Url],
|
||
['action', lang.UISTR_EDITUSER]
|
||
];
|
||
|
||
titleMappings.forEach(([id, text]) => changetitle(id, text));
|
||
changeSearchPlaceholder();
|
||
|
||
} catch (error) {
|
||
console.error('初始化失败:', error);
|
||
// 可选:显示错误提示给用户
|
||
alert('初始化失败: ' + error.message);
|
||
}
|
||
}
|
||
// 执行入口
|
||
initializeApp();
|
||
const channel = new BroadcastChannel('userlist_channel');
|
||
channel.onmessage = async (e) => {
|
||
const { action,id} = e.data;
|
||
get_userinfo(id)
|
||
};
|
||
if (!hasFpsize && !hasPalmsize) {
|
||
$('#downloadfingerprints').hide(); // 或者使用 .remove() 删除
|
||
$('#uploadfingerprints').hide(); // 或者使用 .remove() 删除
|
||
}
|
||
function convertEnrollId(strid) {
|
||
if (typeof strid === 'number') {
|
||
return strid > 0xFFFFFFFF ? strid.toString() : strid;
|
||
}
|
||
if (typeof strid === 'string' && /^\d+$/.test(strid) && DI_USERID_FORMAT != 2) {
|
||
const num = Number(strid);
|
||
return Number.isSafeInteger(num) ? num : strid;
|
||
}
|
||
return strid;
|
||
}
|
||
function formatCard(card, dispFormat) {
|
||
if (card === null || card === undefined || card === "") {
|
||
return "";
|
||
}
|
||
const dispCard = BigInt(card);
|
||
const bits = dispCard.toString(2).length;
|
||
const byteLen = Math.ceil(bits / 8);
|
||
|
||
if (dispFormat === 1) {
|
||
if (byteLen <= 3 || byteLen === 4) {
|
||
if (byteLen <3) {
|
||
const number = dispCard & 0xFFFFFn;
|
||
return `0,${number.toString().padStart(5, '0')}`;
|
||
} else {
|
||
const area = (dispCard >> 16n) & 0xFFFFn;
|
||
const number = dispCard & 0xFFFFn;
|
||
return `${area},${number.toString().padStart(5, '0')}`;
|
||
}
|
||
} else {
|
||
return dispCard.toString();
|
||
}
|
||
} else if (dispFormat === 2) {
|
||
return `0x${dispCard.toString(16).toUpperCase()}`;
|
||
} else {
|
||
return dispCard.toString();
|
||
}
|
||
}
|
||
function parseCardInput(input) {
|
||
if (!input) return 0;
|
||
const str = input.trim().toUpperCase();
|
||
if (!str) return 0; // 默认返回0(Number类型)
|
||
try {
|
||
let value;
|
||
if (str.includes(',')) {
|
||
const [areaStr, numberStr] = str.split(',');
|
||
const area = BigInt(areaStr);
|
||
const number = BigInt(numberStr);
|
||
value = (area << 16n) + number;
|
||
}
|
||
else if (str.startsWith('0X')) {
|
||
value = BigInt(`0x${str.slice(2)}`);
|
||
}
|
||
else {
|
||
value = BigInt(str);
|
||
}
|
||
const MAX_SAFE = BigInt(Number.MAX_SAFE_INTEGER);
|
||
const MIN_SAFE = BigInt(Number.MIN_SAFE_INTEGER);
|
||
if (value >= MIN_SAFE && value <= MAX_SAFE) {
|
||
return Number(value);
|
||
} else {
|
||
return value.toString();
|
||
}
|
||
} catch (e) {
|
||
return 0; // 解析失败返回0
|
||
}
|
||
}
|
||
function formatDateTimeByType(dateStr, dateType) {
|
||
if (!dateStr) return "";
|
||
const [datePart, timePart] = dateStr.split(' ');
|
||
const [year, month, day] = datePart.split('-');
|
||
let formattedDate;
|
||
switch (dateType) {
|
||
case 1: // MDY格式(月/日/年)
|
||
formattedDate = `${month.padStart(2, '0')}/${day}/${year}`;
|
||
break;
|
||
case 2: // DMY格式(日/月/年)
|
||
formattedDate = `${day}/${month.padStart(2, '0')}/${year}`;
|
||
break;
|
||
default: // YMD格式(年/月/日)
|
||
formattedDate = `${year}/${month.padStart(2, '0')}/${day}`;
|
||
}
|
||
return `${formattedDate} ${timePart}`;
|
||
}
|
||
function formatMonthDay(dateStr, dateType = 0) {
|
||
if (!dateStr) return "";
|
||
if (!/^\d{1,2}-\d{1,2}\**$/.test(dateStr)) {
|
||
return "";
|
||
}
|
||
const [datePart, asterisk = ""] = dateStr.split(/(\d{1,2}-\d{1,2})/).filter(Boolean);
|
||
const [inputMonth, inputDay] = datePart.split('-');
|
||
const month = inputMonth.padStart(2, '0');
|
||
const day = inputDay.padStart(2, '0');
|
||
switch (dateType) {
|
||
case 1: // MDY(月/日)
|
||
return `${month}/${day}${asterisk.replace(/[^*]/g, '')}`; // [2](@ref)
|
||
case 2: // DMY(日/月)
|
||
return `${day}/${month}${asterisk.replace(/[^*]/g, '')}`; // [2](@ref)
|
||
default: // 默认保持原格式
|
||
return `${month}/${day}${asterisk.replace(/[^*]/g, '')}`; // [2](@ref)
|
||
}
|
||
}
|
||
function getVerfymodeString(index) {
|
||
const verifyModeLangMap = {
|
||
0: "UISTR_DEVICE_VERIFY_MODE",
|
||
20: "UISTR_ANY",
|
||
10: "UISTR_VERIFY_CARD_ADD_FACE",
|
||
9: "UISTR_VERIFY_FACE_ADD_PWD",
|
||
14: "UISTR_VERIFY_FACE_ADD_CARD_PWD",
|
||
8: "UISTR_FACE_ONLY",
|
||
21: "UISTR_PALMANDFACE",
|
||
22: "UISTR_FPANDPALM",
|
||
23: "UISTR_CARDANDPALM",
|
||
24: "UISTR_PALMANDPWD",
|
||
16: "UISTR_ONLYPALM",
|
||
12: "UISTR_VERIFY_FP_ADD_FACE",
|
||
5: "UISTR_VERIFY_FP_ADD_CARD",
|
||
4: "UISTR_SET_LIST_FP",
|
||
1: "UISTR_FINGER_ONLY",
|
||
11: "UISTR_SET_LIST_CP",
|
||
3: "UISTR_CARD_ONLY",
|
||
2: "UISTR_PASSWORD_ONLY"
|
||
};
|
||
return lang[verifyModeLangMap[index]] || "";
|
||
}
|
||
function reverseVerifyMode(str) {
|
||
const reverseMap = {
|
||
[lang.UISTR_DEVICE_VERIFY_MODE]: 0,
|
||
[lang.UISTR_ANY]: 20,
|
||
[lang.UISTR_VERIFY_CARD_ADD_FACE]: 10,
|
||
[lang.UISTR_VERIFY_FACE_ADD_PWD]: 9,
|
||
[lang.UISTR_VERIFY_FACE_ADD_CARD_PWD]: 14,
|
||
[lang.UISTR_FACE_ONLY]: 8,
|
||
[lang.UISTR_PALMANDFACE]: 21,
|
||
[lang.UISTR_FPANDPALM]: 22,
|
||
[lang.UISTR_CARDANDPALM]: 23,
|
||
[lang.UISTR_PALMANDPWD]: 24,
|
||
[lang.UISTR_ONLYPALM]: 16,
|
||
[lang.UISTR_VERIFY_FP_ADD_FACE]: 12,
|
||
[lang.UISTR_VERIFY_FP_ADD_CARD]: 5,
|
||
[lang.UISTR_SET_LIST_FP]: 4,
|
||
[lang.UISTR_FINGER_ONLY]: 1,
|
||
[lang.UISTR_SET_LIST_CP]: 11,
|
||
[lang.UISTR_CARD_ONLY]: 3,
|
||
[lang.UISTR_PASSWORD_ONLY]: 2
|
||
};
|
||
return reverseMap[str] ??parseInt(str, 10);
|
||
}
|
||
function getAdminString(index) {
|
||
const AdminLangMap = {
|
||
0: "UISTR_USER",
|
||
1: "UISTR_ENROLL_MANAGER",
|
||
2: "UISTR_ENROLL_SUPER_USER",
|
||
};
|
||
return lang[AdminLangMap[index]] || "";
|
||
}
|
||
function reverseAdmin(str) {
|
||
const reverseMap = {
|
||
[lang.UISTR_USER]: 0,
|
||
[lang.UISTR_ENROLL_MANAGER]: 1,
|
||
[lang.UISTR_ENROLL_SUPER_USER]: 2
|
||
};
|
||
return reverseMap[str] ?? parseInt(str, 10);
|
||
}
|
||
function formatUserprofileContent(userprofile) {
|
||
if (userprofile === null || userprofile === undefined || userprofile === "") return "";
|
||
if (typeof userprofile === 'object') {
|
||
return Object.entries(userprofile)
|
||
.map(([key, value]) => `${key}:${value}`)
|
||
.join(',');
|
||
}
|
||
if (typeof userprofile === 'string') {
|
||
try {
|
||
const parsed = JSON.parse(userprofile);
|
||
return Object.entries(parsed)
|
||
.map(([key, value]) => `${key}:${value}`)
|
||
.join(',');
|
||
} catch (e) {
|
||
return userprofile;
|
||
}
|
||
}
|
||
return String(userprofile);
|
||
}
|
||
|
||
function get_userinfo(enrollid) {
|
||
let send = new Object()
|
||
send["password"] = $.cookie("pwd")
|
||
send["cmd"] = 'getuserinfo'
|
||
send["enrollid"] = enrollid
|
||
console.log(send)
|
||
$.ajax({
|
||
url: debugserver+'/api',
|
||
type: 'POST',
|
||
contentType:"application/json",
|
||
dataType: "json",
|
||
data: JSON.stringify(send),
|
||
beforeSend: function() {
|
||
|
||
},
|
||
success: function(json){
|
||
console.log("done", json)
|
||
if (json.result) {
|
||
const transformedRecord = {
|
||
id: json.enrollid || "",
|
||
name: json.name || "",
|
||
department: json.department || "",
|
||
shift: json.shiftid || "",
|
||
admin: getAdminString(json.admin)|| "",
|
||
fingerprint: json.fpcnt || "",
|
||
palm: json.palmcnt || "",
|
||
face: json.faceflag || "",
|
||
password: json.pwd || "",
|
||
card: formatCard(json.card,DI_CARD_DISP_FORMAT)|| "",
|
||
weekzone: json.zoneid || "",
|
||
group: json.groupid || "",
|
||
access_times: json.access_times || "",
|
||
verifymode: getVerfymodeString(json.verifymode) || "",
|
||
birthday: formatMonthDay(json.birthday,DI_DATE_FORMAT),
|
||
starttime: formatDateTimeByType(json.starttime,DI_DATE_FORMAT),
|
||
endtime: formatDateTimeByType(json.endtime,DI_DATE_FORMAT),
|
||
userprofile: formatUserprofileContent(json.userprofile),
|
||
photouri: json.photourl || ""
|
||
};
|
||
const $table = $('#table');
|
||
$table.bootstrapTable('refresh');
|
||
const currentData = $table.bootstrapTable('getData');
|
||
const row = $table.bootstrapTable('getRowByUniqueId', json.enrollid);
|
||
if (row) {
|
||
$table.bootstrapTable('updateByUniqueId', {
|
||
id: json.enrollid,
|
||
row: transformedRecord
|
||
});
|
||
} else {
|
||
$table.bootstrapTable('append', transformedRecord);
|
||
}
|
||
$table.bootstrapTable('scrollTo', {
|
||
unit: 'rows',
|
||
value: rowIndex >= 0 ? rowIndex : $table.bootstrapTable('getData').length - 1,
|
||
scrollSpeed: 'fast'
|
||
});
|
||
} else {
|
||
console.log("error", json);
|
||
}
|
||
},
|
||
error: function(error){
|
||
console.log("error::::",error);
|
||
}
|
||
});
|
||
}
|
||
function get_userlist(stn = 1) {
|
||
let send = {
|
||
password: $.cookie("pwd"),
|
||
cmd: 'getuserlist',
|
||
stn: stn
|
||
};
|
||
|
||
$.ajax({
|
||
url: debugserver + '/api',
|
||
type: 'POST',
|
||
contentType: "application/json",
|
||
dataType: "json",
|
||
data: JSON.stringify(send),
|
||
beforeSend: function() {},
|
||
|
||
success: function(json) {
|
||
console.log("done", json);
|
||
|
||
let records = json.record;
|
||
let transformedData = [];
|
||
|
||
for (let record of records) {
|
||
let transformedRecord = {
|
||
id: record.id || "",
|
||
name: record.name || "",
|
||
department: record.department || "",
|
||
shift: record.shift || "",
|
||
admin: getAdminString(record.admin)|| "",
|
||
fingerprint: record.fingerprint || "",
|
||
palm: record.palm || "",
|
||
face: record.face || "",
|
||
password: record.password || "",
|
||
card: formatCard(record.card,DI_CARD_DISP_FORMAT)|| "",
|
||
weekzone: record.weekzone || "",
|
||
group: record.group || "",
|
||
access_times: record.access_times || "",
|
||
verifymode: getVerfymodeString(record.verifymode) || "",
|
||
birthday: formatMonthDay(record.birthday,DI_DATE_FORMAT),
|
||
starttime: formatDateTimeByType(record.starttime,DI_DATE_FORMAT),
|
||
endtime: formatDateTimeByType(record.endtime,DI_DATE_FORMAT),
|
||
userprofile: formatUserprofileContent(record.userprofile),
|
||
photouri: record.photourl || ""
|
||
};
|
||
transformedData.push(transformedRecord);
|
||
}
|
||
|
||
// Load or append data into the table
|
||
if (stn === 1) {
|
||
$table.bootstrapTable('load', transformedData);
|
||
} else {
|
||
$table.bootstrapTable('append', transformedData);
|
||
}
|
||
|
||
// Check if more data needs to be fetched
|
||
if (json.count > 0 && json.to !== undefined && json.to < json.count) {
|
||
// 递归调用来获取更多数据
|
||
get_userlist(0); // stn = 0 for subsequent requests
|
||
}
|
||
},
|
||
|
||
error: function(error) {
|
||
console.log("error::::", error);
|
||
}
|
||
});
|
||
}
|
||
var $table = $('#table');
|
||
$(function() {
|
||
$(document).on('focus', 'input[type="text"]', function() {
|
||
$(this).parent().find('label').addClass('active');
|
||
}).on('blur', 'input[type="text"]', function() {
|
||
if ($(this).val() == '') {
|
||
$(this).parent().find('label').removeClass('active');
|
||
}
|
||
});
|
||
// bootstrap table初始化
|
||
// http://bootstrap-table.wenzhixin.net.cn/zh-cn/documentation/
|
||
var columns = [
|
||
{field: 'state', checkbox: true},
|
||
{field: 'id', title:'id', sortable: true, halign: 'center', align: 'center'},
|
||
{field: 'name', title: 'name', sortable: true, halign: 'center', align: 'center'},
|
||
{field: 'department', title: 'department', sortable: true, halign: 'center', align: 'center'},
|
||
{field: 'shift', title: 'shift', sortable: true, halign: 'center', align: 'center'},
|
||
{field: 'admin', title: 'admin', sortable: true, halign: 'center', align: 'center'},
|
||
{field: 'face', title: 'face', sortable: true, halign: 'center' , formatter: 'actionfaceFormatter', align: 'center'},
|
||
hasPalmsize ? {field: 'palm', title: 'palm', sortable: true, halign: 'center', align: 'center'} : null,
|
||
hasFpsize ? {field: 'fingerprint', title: 'fp', sortable: true, halign: 'center', align: 'center'} : null,
|
||
{field: 'password', title: 'pwd', sortable: true, halign: 'center', align: 'center'},
|
||
{field: 'card', title: 'card', sortable: true, halign: 'center', align: 'center'},
|
||
{field: 'weekzone', title: 'weekzone', sortable: true, halign: 'center', align: 'center'},
|
||
{field: 'group', title: 'group', sortable: true, halign: 'center', align: 'center'},
|
||
{field: 'access_times', title: 'access_times', sortable: true, halign: 'center', align: 'center'},
|
||
{field: 'verifymode', title: 'verifymode', sortable: true, halign: 'center', align: 'center'},
|
||
{field: 'birthday', title: 'birthday', sortable: false, halign: 'center', align: 'center'},
|
||
{field: 'starttime', title: 'starttime', sortable: true, halign: 'center', align: 'center'},
|
||
{field: 'endtime', title: 'endtime', sortable: true, halign: 'center', align: 'center'},
|
||
{field: 'userprofile', title: 'userprofile', sortable: false, halign: 'center', align: 'center'},
|
||
{field: 'photouri', title: 'photouri', visible: false},
|
||
{field: 'action', title: 'action', halign: 'center', align: 'center', formatter: 'actionFormatter', events: 'actionEvents', clickToSelect: false}
|
||
].filter(Boolean);
|
||
|
||
$table.bootstrapTable({
|
||
data: [],
|
||
height: getHeight(),
|
||
striped: true,
|
||
search: true,
|
||
searchOnEnterKey: true,
|
||
showRefresh: true,
|
||
//showToggle: true,
|
||
showColumns: true,
|
||
minimumCountColumns: 2,
|
||
//showPaginationSwitch: true,
|
||
clickToSelect: true,
|
||
detailView: true,
|
||
detailFormatter: 'detailFormatter',
|
||
pagination: true,
|
||
pageSize: 15,
|
||
pageList: [10, 15, 30, 50, 100],
|
||
paginationLoop: false,
|
||
classes: 'table table-hover table-no-bordered',
|
||
//sidePagination: 'server',
|
||
//silentSort: false,
|
||
smartDisplay: false,
|
||
uniqueId: 'id',
|
||
idField: 'id',
|
||
sortName: 'department',
|
||
sortOrder: 'asc',
|
||
escape: true,
|
||
searchOnEnterKey: true,
|
||
maintainSelected: true,
|
||
toolbar: '#toolbar',
|
||
columns: columns
|
||
}).on('all.bs.table', function (e, name, args) {
|
||
$('[data-toggle="tooltip"]').tooltip();
|
||
$('[data-toggle="popover"]').popover();
|
||
});
|
||
});
|
||
function changeSearchPlaceholder() {
|
||
const options = $table.bootstrapTable('getOptions');
|
||
options.formatSearch = () => lang.UISTR_USER_FIND || 'Search...';
|
||
options.formatRefresh = () => lang.UISTR_Refresh || 'Refresh';
|
||
options.formatRefreshButton = (icon, text) =>
|
||
`<i class="${icon}"></i> ${text || lang.UISTR_Refresh || 'Refresh'}`;
|
||
options.formatColumns = () => lang.UISTR_Columns || 'Columns'; // [4](@ref)
|
||
options.formatColumnsButton = (icon, text) =>
|
||
`<i class="${icon}"></i> ${text || lang.UISTR_Columns || 'Columns'}`;
|
||
options.formatToggle = () => lang.UISTR_Toggle || 'Toggle'; // 悬浮提示(Tooltip)
|
||
options.formatToggleButton = (icon, text) =>
|
||
`<i class="${icon}"></i> ${text || lang.UISTR_Toggle || 'Toggle'}`; // 按钮文本
|
||
options.formatNoMatches = () => lang.UISTR_NoMatches || 'No matching records found';
|
||
options.formatShowingRows = (from, to, total) => {
|
||
const template = lang.UISTR_ShowingRows
|
||
|| 'Showing #1 to #2 of #3 rows';
|
||
return template
|
||
.replace(/#1/g, from)
|
||
.replace(/#2/g, to)
|
||
.replace(/#3/g, total);
|
||
};
|
||
options.formatRecordsPerPage = (pageSize) => {
|
||
const template = lang.UISTR_RecordsPerPage
|
||
|| '#3 rows per page';
|
||
return template.replace(/#3/g, pageSize);
|
||
};
|
||
|
||
// 强制刷新配置
|
||
$table.bootstrapTable('refreshOptions', options);
|
||
}
|
||
function changetitle(field, newTitle) {
|
||
var columns = $table.bootstrapTable('getOptions').columns[0];
|
||
for (var i = 0; i < columns.length; i++) {
|
||
if (columns[i].field === field) {
|
||
columns[i].title = newTitle;
|
||
break;
|
||
}
|
||
}
|
||
$table.bootstrapTable('refreshOptions', {columns: columns});
|
||
}
|
||
$table.on('refresh.bs.table', function () {
|
||
$table.bootstrapTable('removeAll');
|
||
get_userlist();
|
||
});
|
||
function actionFormatter(value, row, index) {
|
||
const editTitle = lang.UISTR_EDITUSER || 'Edit';
|
||
const removeTitle = lang.UISTR_USER_DELETE || 'Remove';
|
||
return [
|
||
'<a class="edit ml10" href="javascript:void(0)" data-toggle="tooltip" title="' + editTitle + '">',
|
||
'<i class="glyphicon glyphicon-edit"></i></a> ',
|
||
'<a class="remove ml10" href="javascript:void(0)" data-toggle="tooltip" title="' + removeTitle + '">',
|
||
'<i class="glyphicon glyphicon-remove"></i></a>'
|
||
].join('');
|
||
}
|
||
function actionfaceFormatter(value, row, index) {
|
||
return value ? `<i class="glyphicon glyphicon-user"></i>` : '';
|
||
}
|
||
window.actionEvents = {
|
||
'click .face': function (e, value, row, index) {
|
||
console.log(value, row, index);
|
||
},
|
||
'click .edit': function (e, value, row, index) {
|
||
console.log(value, row, index);
|
||
if (window.parent && window.parent.Tab && typeof window.parent.Tab.addTab === 'function') {
|
||
var url = 'add_member.html?id=' + encodeURIComponent(row.id);
|
||
window.parent.Tab.addTab(lang.UISTR_USER_EDIT, url);
|
||
} else {
|
||
console.error('Tab object or addTab method is not accessible.');
|
||
}
|
||
},
|
||
'click .remove': function (e, value, row, index) {
|
||
e.preventDefault();
|
||
$.confirm({
|
||
type: 'red',
|
||
title: false,
|
||
content: lang.UISTR_DELETE_QMARK,
|
||
buttons: {
|
||
confirm: {
|
||
text: lang.UISTR_YES_OK,
|
||
btnClass: 'waves-effect waves-button',
|
||
action: function () {
|
||
let data = {
|
||
cmd: "deleteusers",
|
||
list: [row.id],
|
||
password: $.cookie("pwd")
|
||
};
|
||
$.ajax({
|
||
url: debugserver+'/api',
|
||
type: 'POST',
|
||
data: JSON.stringify(data),
|
||
contentType: 'application/json',
|
||
success: function (response) {
|
||
if (response.ret == "deleteusers" && response.result == true) {
|
||
$('#table').bootstrapTable('remove', {
|
||
field: 'id',
|
||
values: [row.id]
|
||
});
|
||
$.alert(lang.UISTR_DELETE_OK);
|
||
onUploadSuccess();
|
||
} else {
|
||
$.alert(lang.UISTR_ERRNONESUCCESS);
|
||
}
|
||
},
|
||
error: function (xhr, status, error) {
|
||
console.error('Error:', error);
|
||
$.alert(lang.UISTR_CONNECT_FAILED);
|
||
}
|
||
});
|
||
}
|
||
},
|
||
cancel: {
|
||
text: lang.UISTR_NO_ESC,
|
||
btnClass: 'waves-effect waves-button'
|
||
}
|
||
}
|
||
});
|
||
}
|
||
};
|
||
function detailFormatter(index, row) {
|
||
var html = [];
|
||
var detailsHtml = [];
|
||
// Manually add the information you want to display
|
||
detailsHtml.push('<p><b>'+lang.UISTR_ENROLL_ID+':</b>' + row.id + '</p>');
|
||
detailsHtml.push('<p><b>'+lang.UISTR_UserName+':</b>' + row.name + '</p>');
|
||
detailsHtml.push('<p><b>'+lang.UISTR_Privilege+':</b>' + row.admin + '</p>');
|
||
if(!poithide)
|
||
{
|
||
detailsHtml.push('<p><b>'+lang.UISTR_Department+':</b>' + row.department + '</p>');
|
||
detailsHtml.push('<p><b>'+lang.UISTR_UserShift+':</b>' + row.shift + '</p>');
|
||
if(hasPalmsize)
|
||
detailsHtml.push('<p><b>'+lang.UISTR_PALM_ENROLL+':</b>' + row.palm + '</p>');
|
||
if(hasFpsize)
|
||
detailsHtml.push('<p><b>'+lang.UISTR_FINGER_ENROLL+':</b>' + row.fingerprint + '</p>');
|
||
detailsHtml.push('<p><b>'+lang.UISTR_PASSWORD_ENROLL+':</b>' + row.password + '</p>');
|
||
detailsHtml.push('<p><b>'+lang.UISTR_CARD_ENROLL+':</b>' + row.card + '</p>');
|
||
detailsHtml.push('<p><b>'+lang.UISTR_MAINMENU_PASSTIMEID+':</b>' + row.weekzone + '</p>');
|
||
}
|
||
if(hasElevator)
|
||
detailsHtml.push('<p><b>'+lang.UISTR_DIRECT_FLOOR+':</b>' + row.group + '</p>');
|
||
else
|
||
detailsHtml.push('<p><b>'+lang.UISTR_MAINMENU_GROUPID+':</b>' + row.group + '</p>');
|
||
detailsHtml.push('<p><b>'+lang.UISTR_PERSON_VERIFY_MODE+':</b>' + row.verifymode + '</p>');
|
||
if(!poithide)
|
||
{
|
||
detailsHtml.push('<p><b>'+lang.UISTR_ACCESS_TIMES+':</b>' + row.access_times + '</p>');
|
||
detailsHtml.push('<p><b>'+lang.UISTR_BIRTHDAY+':</b>' + row.birthday + '</p>');
|
||
}
|
||
detailsHtml.push('<p><b>'+lang.UISTR_START+':</b>' + row.starttime + '</p>');
|
||
detailsHtml.push('<p><b>'+lang.UISTR_END+':</b>' + row.endtime + '</p>');
|
||
detailsHtml.push('<p><b>'+lang.UISTR_USER_PROFILE+':</b>' + row.userprofile + '</p>');
|
||
|
||
html.push('<div class="row">');
|
||
html.push(' <div class="col-md-8 col-sm-8">');
|
||
html.push(' <div class="row">');
|
||
html.push(' <div class="col-md-6 col-sm-6">' + detailsHtml.slice(0, Math.ceil(detailsHtml.length / 2)).join('') + '</div>');
|
||
html.push(' <div class="col-md-6 col-sm-6">' + detailsHtml.slice(Math.ceil(detailsHtml.length / 2)).join('') + '</div>');
|
||
html.push(' </div>');
|
||
html.push(' </div>');
|
||
html.push(' <div class="col-md-4 col-sm-4 d-flex justify-content-end">');
|
||
|
||
var imageUrl = debugserver + row.photouri;
|
||
if (imageUrl && imageUrl.trim()) {
|
||
html.push(' <a href="' + imageUrl+'?t=' + new Date().getTime() + '" target="_blank">');
|
||
html.push(' <img src="' + imageUrl+'?t=' + new Date().getTime() + '" alt="User Image" style="max-height:200px; max-width:100%;" />');
|
||
html.push(' </a>');
|
||
}
|
||
|
||
html.push(' </div>');
|
||
html.push('</div>');
|
||
|
||
return html.join('');
|
||
}
|
||
|
||
function createAction() {
|
||
if (window.parent && window.parent.Tab && typeof window.parent.Tab.addTab === 'function') {
|
||
window.parent.Tab.addTab(lang.UISTR_USER_ENROLL, 'add_member.html');
|
||
} else {
|
||
console.error('Tab object or addTab method is not accessible.');
|
||
}
|
||
}
|
||
function updateAction() {
|
||
var rows = $table.bootstrapTable('getSelections');
|
||
if (rows.length === 0) {
|
||
$.confirm({
|
||
title: false,
|
||
content: '请至少选择一条记录!',
|
||
autoClose: 'cancel|3000',
|
||
backgroundDismiss: true,
|
||
buttons: {
|
||
cancel: {
|
||
text: lang.UISTR_NO_ESC,
|
||
btnClass: 'waves-effect waves-button'
|
||
}
|
||
}
|
||
});
|
||
} else if (rows.length > 1) {
|
||
$.confirm({
|
||
title: false,
|
||
content: '请选择单条记录进行编辑!',
|
||
autoClose: 'cancel|3000',
|
||
backgroundDismiss: true,
|
||
buttons: {
|
||
cancel: {
|
||
text: lang.UISTR_NO_ESC,
|
||
btnClass: 'waves-effect waves-button'
|
||
}
|
||
}
|
||
});
|
||
} else {
|
||
if (window.parent && window.parent.Tab && typeof window.parent.Tab.addTab === 'function') {
|
||
var url = 'add_member.html?id=' + encodeURIComponent(rows[0].id);
|
||
window.parent.Tab.addTab(lang.UISTR_EDITUSER, url);
|
||
} else {
|
||
console.error('Tab object or addTab method is not accessible.');
|
||
}
|
||
}
|
||
}
|
||
function deleteAction() {
|
||
var rows = $table.bootstrapTable('getSelections');
|
||
if (rows.length == 0) {
|
||
$.confirm({
|
||
title: false,
|
||
content: '请至少选择一条记录!',
|
||
autoClose: 'cancel|3000',
|
||
backgroundDismiss: true,
|
||
buttons: {
|
||
cancel: {
|
||
text: lang.UISTR_NO_ESC,
|
||
btnClass: 'waves-effect waves-button'
|
||
}
|
||
}
|
||
});
|
||
} else {
|
||
$.confirm({
|
||
type: 'red',
|
||
animationSpeed: 300,
|
||
title: false,
|
||
content: lang.UISTR_DELETE_QMARK,
|
||
buttons: {
|
||
confirm: {
|
||
text: lang.UISTR_YES_OK,
|
||
btnClass: 'waves-effect waves-button',
|
||
action: function () {
|
||
var ids = [];
|
||
for (var i in rows) {
|
||
ids.push(rows[i].id);
|
||
}
|
||
let send = new Object();
|
||
send["password"] = $.cookie("pwd");
|
||
send["cmd"] = 'deleteusers';
|
||
send["list"] = ids;
|
||
|
||
$.ajax({
|
||
url: debugserver+'/api',
|
||
type: 'POST',
|
||
data: JSON.stringify(send),
|
||
contentType: 'application/json',
|
||
success: function (response) {
|
||
if (response.ret === "deleteusers" && response.result === true) {
|
||
$table.bootstrapTable('remove', {
|
||
field: 'id',
|
||
values: ids
|
||
});
|
||
$.alert(lang.UISTR_DELETE_OK);
|
||
onUploadSuccess();
|
||
}
|
||
else if(response.result === false)
|
||
{
|
||
$.alert(lang.UISTR_ERRNONESUCCESS);
|
||
}
|
||
},
|
||
error: function (xhr, status, error) {
|
||
console.log('Error: ' + error);
|
||
}
|
||
});
|
||
}
|
||
},
|
||
cancel: {
|
||
text: lang.UISTR_NO_ESC,
|
||
btnClass: 'waves-effect waves-button'
|
||
}
|
||
}
|
||
});
|
||
}
|
||
}
|
||
/////////////////////////////////////////////down all photos
|
||
function extractFileNameFromPath(path) {
|
||
const parts = path.split('/');
|
||
return parts[parts.length - 1];
|
||
}
|
||
|
||
function updateProgressBar(total, uploaded,fileName=null,isdownload=false) {
|
||
const progressBar = $('.progress-bar');
|
||
var percentage = (uploaded / total) * 100;
|
||
progressBar.css('width', `${percentage}%`).attr('aria-valuenow', percentage);
|
||
var textuploaded=lang.UISTR_UPLOADED;
|
||
var textuploading=lang.UISTR_UPLOADING;
|
||
if(isdownload)
|
||
{
|
||
textuploaded=lang.UISTR_DOWNLOADED;
|
||
textuploading=lang.UISTR_DOWNLOADING;
|
||
}
|
||
|
||
$('#uploaded').text(textuploaded+': '+uploaded+' / '+total);
|
||
|
||
if(fileName)
|
||
$('#uploading').text(textuploading+': '+fileName);
|
||
else
|
||
$('#uploading').text(textuploading+': --');
|
||
|
||
// 更新进度条中的文字
|
||
const progressText = $('.progress-text');
|
||
progressText.text(`${Math.round(percentage)}%`);
|
||
|
||
// 根据进度调整文字颜色
|
||
if (percentage > 52) {
|
||
progressText.css('color', 'white');
|
||
} else {
|
||
progressText.css('color', 'black');
|
||
}
|
||
}
|
||
|
||
// 新增全局ZIP对象和配置
|
||
let zip = null;
|
||
let zipFileName = `EnrollPhoto_${deviceid}.zip`;
|
||
function downloadImagesFromPhotouri(batchSize = 5) {
|
||
const allRows = $table.bootstrapTable('getData');
|
||
const filteredRows = allRows.filter(row => row.photouri !== null && row.photouri !== "");
|
||
const totalRows = filteredRows.length;
|
||
let currentIndex = 0;
|
||
let uploadedCount = 0;
|
||
|
||
// 初始化ZIP实例
|
||
zip = new JSZip();
|
||
$('#uploadModalLabel').text(lang.UISTR_DOWNLOAD_PHOTOS);
|
||
$('#uploadModal').modal('show');
|
||
|
||
async function downloadBatch() {
|
||
const endIndex = Math.min(currentIndex + batchSize, totalRows);
|
||
const batch = filteredRows.slice(currentIndex, endIndex);
|
||
|
||
try {
|
||
await Promise.all(batch.map(async row => {
|
||
const imageUrl = debugserver + row.photouri + `?random=${Math.random()}`;
|
||
const fileName = extractFileNameFromPath(row.photouri);
|
||
const blob = await fetch(imageUrl)
|
||
.then(response => {
|
||
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
||
return response.blob();
|
||
});
|
||
zip.file(fileName, blob, { binary: true });
|
||
uploadedCount++;
|
||
updateProgressBar(totalRows, uploadedCount,fileName,true);
|
||
}));
|
||
|
||
currentIndex = endIndex;
|
||
|
||
if (currentIndex < totalRows) {
|
||
setTimeout(downloadBatch, 1000);
|
||
} else {
|
||
const content = await zip.generateAsync({ type: "blob" });
|
||
saveAs(content, zipFileName);
|
||
$('#uploadModal').modal('hide');
|
||
zip = null;
|
||
}
|
||
} catch (error) {
|
||
console.error('Batch error:', error);
|
||
$('#uploadModal').modal('hide');
|
||
}
|
||
}
|
||
|
||
downloadBatch();
|
||
}
|
||
// 处理单个图片文件
|
||
async function post_image(file, index, totalPhotos, successCount, failCount) {
|
||
const reader = new FileReader();
|
||
return new Promise((resolve, reject) => {
|
||
reader.onload = function(event) {
|
||
// 获取Base64编码数据,去除头部
|
||
const base64String = event.target.result.split(',')[1];
|
||
const fileName = file.name;
|
||
const enrollId = validateFileName(fileName);
|
||
|
||
if (enrollId !== null) {
|
||
set_image(enrollId, base64String, index, totalPhotos, successCount, failCount)
|
||
.then((result) => resolve(result))
|
||
.catch((error) => reject(error));
|
||
} else {
|
||
console.warn(`Invalid file name: ${fileName}`);
|
||
reject({ success: false, message: `Invalid file name: ${fileName}` });
|
||
}
|
||
};
|
||
reader.readAsDataURL(file);
|
||
});
|
||
}
|
||
|
||
// 验证文件名
|
||
function validateFileName(fileName) {
|
||
const regex = /^LF(\d{1,8})\.jpg$|^LF(\d{9,64})\.jpg$|^CF_([\w-]+)\.jpg$/;
|
||
const match = fileName.match(regex);
|
||
if (match) {
|
||
if (match[1]) {
|
||
return parseInt(match[1], 10);
|
||
}
|
||
else if (match[2]) {
|
||
return BigInt(match[2]);
|
||
}
|
||
else if (match[3]) {
|
||
return match[3];
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
async function set_image(enrollId, base64String, index, totalPhotos, successCount, failCount) {
|
||
let send = new Object();
|
||
send["password"] = $.cookie("pwd");
|
||
send["cmd"] = 'setuserinfo';
|
||
send["enrollid"] = enrollId;
|
||
|
||
if (base64String) {
|
||
send["face"] = base64String;
|
||
}
|
||
|
||
console.log(send);
|
||
|
||
return new Promise((resolve, reject) => {
|
||
$.ajax({
|
||
url: debugserver + '/api',
|
||
type: 'POST',
|
||
contentType: "application/json",
|
||
dataType: "json",
|
||
data: JSON.stringify(send),
|
||
beforeSend: function() {
|
||
},
|
||
success: function(json) {
|
||
console.log("done", json);
|
||
if (json.ret === "setuserinfo" && json.result === true) {
|
||
resolve({ success: true });
|
||
} else {
|
||
reject({ success: false, message: `Upload failed for ${enrollId}: ${json.message}` });
|
||
}
|
||
},
|
||
error: function(error) {
|
||
console.log("error::::", error);
|
||
reject({ success: false, message: `Error uploading image: ${error.responseText}` });
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////
|
||
function uploadZIPAction() {
|
||
const fileInput = document.createElement('input');
|
||
fileInput.type = 'file';
|
||
fileInput.accept = '.zip'; // 限制只允许ZIP文件[8](@ref)
|
||
|
||
fileInput.onchange = async (e) => {
|
||
const zipFile = e.target.files[0];
|
||
if (!zipFile) return;
|
||
$('#uploadModalLabel').text(lang.UISTR_UPLOAD_PHOTOS);
|
||
$('#uploadModal').modal('show');
|
||
try {
|
||
const zip = await JSZip.loadAsync(zipFile);
|
||
const entries = Object.values(zip.files);
|
||
|
||
// 过滤目录并统计有效图片数量[4](@ref)
|
||
const imageEntries = entries.filter(entry =>
|
||
!entry.dir && entry.name.match(/\.(jpg|jpeg|png)$/i)
|
||
);
|
||
const totalPhotos = imageEntries.length;
|
||
let successCount = 0, failCount = 0;
|
||
// 批量处理ZIP内图片
|
||
for (const [index, entry] of imageEntries.entries()) {
|
||
try {
|
||
const blob = await entry.async('blob');
|
||
const file = new File([blob], entry.name.split('/').pop(), {
|
||
type: 'image/jpeg'
|
||
});
|
||
await post_image(file, index, totalPhotos);
|
||
successCount++;
|
||
updateProgressBar(totalPhotos, successCount, entry.name);
|
||
} catch (error) {
|
||
console.error(`文件上传失败: ${entry.name}`, error);
|
||
failCount++;
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('ZIP处理失败:', error);
|
||
alert(`ZIP文件处理失败: ${error.message}`);
|
||
} finally {
|
||
$('#uploadModal').modal('hide');
|
||
}
|
||
};
|
||
fileInput.click();
|
||
}
|
||
///////////////////////////////////////////excel
|
||
function saveasexcelAction() {
|
||
var allRows = $table.bootstrapTable('getData');
|
||
var workbook = XLSX.utils.book_new();
|
||
var exceldata = [];
|
||
exceldata.push([lang.UISTR_STAFF]);
|
||
exceldata.push([lang.UISTR_USERLIST_NOTE]);
|
||
var columnNames = [
|
||
lang.UISTR_ENROLL_ID,
|
||
lang.UISTR_UserName,
|
||
lang.UISTR_Department,
|
||
lang.UISTR_UserShift,
|
||
lang.UISTR_ENROLL_MANAGER,
|
||
hasPalmsize ? lang.UISTR_PALM_ENROLL : null,
|
||
hasFpsize ? lang.UISTR_FINGER_ENROLL : null,
|
||
lang.UISTR_FACE_ENROLL,
|
||
lang.UISTR_PASSWORD_ENROLL,
|
||
lang.UISTR_CARD_ENROLL,
|
||
lang.UISTR_LOCK_TIMEZONE,
|
||
lang.UISTR_MAINMENU_GROUPID,
|
||
lang.UISTR_ACCESS_TIMES,
|
||
lang.UISTR_VERIFY_mode,
|
||
lang.UISTR_BIRTHDAY,
|
||
lang.UISTR_START,
|
||
lang.UISTR_END,
|
||
lang.UISTR_USER_PROFILE
|
||
];
|
||
columnNames = columnNames.filter(columnName => columnName !== null);
|
||
exceldata.push(columnNames);
|
||
allRows.forEach(function(row) {
|
||
var rowData = [
|
||
row.id,
|
||
row.name,
|
||
row.department,
|
||
row.shift,
|
||
reverseAdmin(row.admin),
|
||
hasPalmsize ? row.palm : null,
|
||
hasFpsize ? row.fingerprint : null,
|
||
row.face,
|
||
row.password,
|
||
parseCardInput(row.card),
|
||
row.weekzone,
|
||
row.group,
|
||
row.access_times,
|
||
reverseVerifyMode(row.verifymode),
|
||
row.birthday,
|
||
row.starttime,
|
||
row.endtime,
|
||
row.userprofile
|
||
];
|
||
rowData = rowData.filter(value => value !== null);
|
||
exceldata.push(rowData);
|
||
});
|
||
var worksheet = XLSX.utils.aoa_to_sheet(exceldata);
|
||
var fontStyle = {
|
||
font: {
|
||
bold: true,
|
||
color: { rgb: [255, 0, 0] },
|
||
sz: 14,
|
||
family: 2,
|
||
name: 'Arial'
|
||
}
|
||
};
|
||
var alignmentStyle = {
|
||
wrapText: true,
|
||
vertical: 'center',
|
||
horizontal: 'center'
|
||
};
|
||
worksheet['A1'].f = fontStyle;
|
||
worksheet['A1'].s = alignmentStyle;
|
||
worksheet['A2'].f = fontStyle;
|
||
worksheet['A2'].s = alignmentStyle;
|
||
worksheet['!merges'] = [
|
||
{ s: { r: 0, c: 0 }, e: { r: 0, c: columnNames.length - 1 } },
|
||
{ s: { r: 1, c: 0 }, e: { r: 1, c: columnNames.length - 1 } }
|
||
];
|
||
XLSX.utils.book_append_sheet(workbook, worksheet, lang.UISTR_STAFF);
|
||
XLSX.writeFile(workbook, lang.UISTR_STAFF + '.xls', { bookType: 'xls' });
|
||
let field = window.lang.UISTR_DOWNLOAD_OK;
|
||
notify(field);
|
||
}
|
||
////////////////////////////////////////down finger/palm
|
||
function downloadfingerprints() {
|
||
var allRows = $table.bootstrapTable('getData');
|
||
const filteredRows = allRows.filter(row =>
|
||
(typeof row.palm === 'string' ? parseInt(row.palm, 10) : row.palm) > 0 ||
|
||
(hasFacetemplate&&(typeof row.face === 'string' ? parseInt(row.face, 10) : row.face)> 0) ||
|
||
(typeof row.fingerprint === 'string' ? parseInt(row.fingerprint, 10) : row.fingerprint) > 0
|
||
);
|
||
// 计算总的行数,包括所有的手指和手掌数据
|
||
const totalRows = filteredRows.reduce((sum, row) => {
|
||
const palm = typeof row.palm === 'string' ? parseInt(row.palm, 10) : row.palm;
|
||
const face =hasFacetemplate?(typeof row.face === 'string' ? parseInt(row.face, 10) : row.face):0;
|
||
const fingerprint = typeof row.fingerprint === 'string' ? parseInt(row.fingerprint, 10) : row.fingerprint;
|
||
return sum + (isNaN(palm) ? 0 : palm)+ (isNaN(face) ? 0 : face) + (isNaN(fingerprint) ? 0 : fingerprint);
|
||
}, 0);
|
||
let currentIndex = 0;
|
||
let dataForCsv = [];
|
||
let havesetcsv=false;
|
||
let allcount=0;
|
||
|
||
currentIndex=0;
|
||
havesetcsv=false;
|
||
// 显示模态对话框
|
||
$('#uploadModalLabel').text(lang.UISTR_DOWNLOAD_FINGER);
|
||
$('#uploadModal').modal('show');
|
||
function processRow(row) {
|
||
if (row.fingerprint > 0) {
|
||
for (let i = 0; i < row.fingerprint; i++) {
|
||
get_userinfo(row.id, i).then(fingerprintData => {
|
||
// 将指纹数据添加到 dataForCsv 数组中
|
||
dataForCsv.push({
|
||
index: allcount++,
|
||
deviceid: deviceid,
|
||
userid: row.id,
|
||
backupnumber: i,
|
||
data: fingerprintData
|
||
});
|
||
currentIndex++;
|
||
updateProgressBar(totalRows, currentIndex,row.id,true);
|
||
checkIfAllDataCollected(currentIndex);
|
||
}).catch(error => {
|
||
console.error('Error getting fingerprint data:', error);
|
||
currentIndex++;
|
||
checkIfAllDataCollected(currentIndex);
|
||
});
|
||
}
|
||
}
|
||
if (row.palm > 0) {
|
||
for (let i = 0; i < row.palm; i++) {
|
||
get_userinfo(row.id, i + 40).then(palmData => {
|
||
dataForCsv.push({
|
||
index: allcount++,
|
||
deviceid: deviceid,
|
||
userid: row.id,
|
||
backupnumber: i + 40,
|
||
data: palmData
|
||
});
|
||
currentIndex++;
|
||
updateProgressBar(totalRows, currentIndex,row.id,true);
|
||
checkIfAllDataCollected(currentIndex);
|
||
}).catch(error => {
|
||
console.error('Error getting palm data:', error);
|
||
currentIndex++;
|
||
checkIfAllDataCollected(currentIndex);
|
||
});
|
||
}
|
||
}
|
||
if(hasFacetemplate&&row.face){
|
||
get_userinfo(row.id, 51).then(facedata => {
|
||
dataForCsv.push({
|
||
index: allcount++,
|
||
deviceid: deviceid,
|
||
userid: row.id,
|
||
backupnumber: 51,
|
||
data: facedata
|
||
});
|
||
currentIndex++;
|
||
updateProgressBar(totalRows, currentIndex,row.id,true);
|
||
checkIfAllDataCollected(currentIndex);
|
||
}).catch(error => {
|
||
console.error('Error getting palm data:', error);
|
||
currentIndex++;
|
||
checkIfAllDataCollected(currentIndex);
|
||
});
|
||
}
|
||
}
|
||
|
||
function checkIfAllDataCollected(ncurrentIndex) {
|
||
if (ncurrentIndex >= totalRows) {
|
||
if (!havesetcsv) {
|
||
generateAndDownloadCsv(dataForCsv);
|
||
havesetcsv = true;
|
||
}
|
||
}
|
||
}
|
||
function generateAndDownloadCsv(data) {
|
||
let header = `${allcount}\tTMNo\tEnNo\tBackupNum\tData\t\r\n`;
|
||
const csvContent = "data:text/csv;charset=utf-8,"
|
||
+ header
|
||
+ data.map(row => [
|
||
row.index,
|
||
row.deviceid,
|
||
row.userid,
|
||
row.backupnumber,
|
||
row.data
|
||
].join("\t") + "\r\n").join(''); // 确保每行数据后都有换行符
|
||
const encodedUri = encodeURI(csvContent);
|
||
const link = document.createElement("a");
|
||
let paddedDeviceId = String(deviceid).padStart(3, '0');
|
||
link.setAttribute("href", encodedUri);
|
||
link.setAttribute("download", `CFP_${paddedDeviceId}.CSV`);
|
||
document.body.appendChild(link);
|
||
link.click();
|
||
}
|
||
function get_userinfo(userid, backupnum) {
|
||
return new Promise((resolve, reject) => {
|
||
let send = new Object();
|
||
let enrollid;
|
||
if (/^\d+$/.test(userid)) {
|
||
const isSafe = Number(userid) <= Number.MAX_SAFE_INTEGER;
|
||
enrollid = isSafe ? Number(userid) : userid;
|
||
}
|
||
else {
|
||
enrollid = userid;
|
||
}
|
||
send["password"] = $.cookie("pwd");
|
||
send["cmd"] = 'getuserinfo'
|
||
send["enrollid"] = enrollid;
|
||
send["backupnum"] = backupnum;
|
||
console.log(send);
|
||
$.ajax({
|
||
url: debugserver + '/api',
|
||
type: 'POST',
|
||
contentType: "application/json",
|
||
dataType: "json",
|
||
data: JSON.stringify(send),
|
||
beforeSend: function () { },
|
||
success: function (json) {
|
||
if (json.result) {
|
||
resolve(json.record);
|
||
} else {
|
||
reject(json);
|
||
}
|
||
},
|
||
error: function (error) {
|
||
reject(error);
|
||
}
|
||
});
|
||
});
|
||
}
|
||
// 开始处理每一行
|
||
filteredRows.forEach(processRow);
|
||
}
|
||
|
||
function uploadfingerprints() {
|
||
// 创建一个文件输入元素
|
||
const fileInput = document.createElement('input');
|
||
fileInput.type = 'file';
|
||
fileInput.accept = '.CSV';
|
||
|
||
// 当文件被选中时触发
|
||
fileInput.onchange = function(event) {
|
||
const file = event.target.files[0];
|
||
if (file) {
|
||
// 使用 FileReader 读取文件内容
|
||
const reader = new FileReader();
|
||
reader.onload = function(e) {
|
||
const csvContent = e.target.result;
|
||
processDataFromCSV(csvContent);
|
||
};
|
||
reader.readAsText(file);
|
||
}
|
||
};
|
||
|
||
// 触发文件选择框
|
||
fileInput.click();
|
||
}
|
||
function processDataFromCSV(csvContent) {
|
||
var allRows = [];
|
||
var lines = csvContent.split('\n');
|
||
// 跳过标题行
|
||
var headerLine = lines.shift();
|
||
|
||
for (var i = 0; i < lines.length; i++) {
|
||
var columns = lines[i].split('\t');
|
||
if (columns.length >= 5) {
|
||
var row = {
|
||
id: convertEnrollId(columns[2]),
|
||
backupnumber: Number(columns[3]),
|
||
record: columns[4].replace(/\r/g, '')
|
||
};
|
||
allRows.push(row);
|
||
}
|
||
}
|
||
const totalRows = allRows.length;
|
||
let currentIndex = 0;
|
||
|
||
// 显示模态对话框
|
||
$('#uploadModalLabel').text(lang.UISTR_UPLOAD_FINGER);
|
||
$('#uploadModal').modal('show');
|
||
function processRow(row) {
|
||
set_fingerprint(row).then(() => {
|
||
currentIndex++;
|
||
updateProgressBar(totalRows, currentIndex);
|
||
if (currentIndex < totalRows) {
|
||
processRow(allRows[currentIndex]);
|
||
} else {
|
||
// 所有数据处理完成
|
||
$('#uploadModal').modal('hide');
|
||
}
|
||
}).catch(error => {
|
||
console.error('Error setting user info:', error);
|
||
currentIndex++;
|
||
if (currentIndex < totalRows) {
|
||
processRow(allRows[currentIndex]);
|
||
} else {
|
||
// 所有数据处理完成
|
||
$('#uploadModal').modal('hide');
|
||
}
|
||
});
|
||
}
|
||
// 开始处理第一行
|
||
if (allRows.length > 0) {
|
||
processRow(allRows[0]);
|
||
}
|
||
}
|
||
function set_fingerprint(row) {
|
||
return new Promise((resolve, reject) => {
|
||
let send = new Object();
|
||
send["password"] = $.cookie("pwd");
|
||
send["cmd"] = 'setuserinfo';
|
||
send["enrollid"] = convertEnrollId(row.id);
|
||
send["backupnum"] = row.backupnumber;
|
||
send["record"] = row.record;
|
||
console.log(send);
|
||
|
||
$.ajax({
|
||
url: debugserver + '/api',
|
||
type: 'POST',
|
||
contentType: "application/json",
|
||
dataType: "json",
|
||
data: JSON.stringify(send),
|
||
beforeSend: function () { },
|
||
success: function (json) {
|
||
if (json.ret === "setuserinfo" && json.result === true) {
|
||
resolve(); // 成功时解析
|
||
} else {
|
||
reject(json); // 结果为 false 时拒绝
|
||
}
|
||
},
|
||
error: function (error) {
|
||
reject(error); // AJAX 请求失败时拒绝
|
||
}
|
||
});
|
||
});
|
||
}
|
||
//////////////////////////////////////////////////////
|
||
function importfromexcelAction() {
|
||
// 创建一个文件输入元素
|
||
const fileInput = document.createElement('input');
|
||
fileInput.type = 'file';
|
||
fileInput.accept = '.xls';
|
||
// 当文件被选中时触发
|
||
fileInput.onchange = function(event) {
|
||
const file = event.target.files[0];
|
||
if (file) {
|
||
const reader = new FileReader();
|
||
reader.onload = function(e) {
|
||
var data = e.target.result;
|
||
var workbook = XLSX.read(data, { type: 'binary' });
|
||
var sheet_name_list = workbook.SheetNames;
|
||
var first_worksheet = workbook.Sheets[sheet_name_list[0]];
|
||
var excel_data = XLSX.utils.sheet_to_json(first_worksheet, { header: 1 });
|
||
handleExcelData(excel_data);
|
||
};
|
||
reader.readAsBinaryString(file);
|
||
}
|
||
};
|
||
fileInput.click();
|
||
}
|
||
function handleExcelData(data) {
|
||
// 显示模态对话框
|
||
$('#uploadModalLabel').text(lang.UISTR_IMPORTFROMEXCEL);
|
||
$('#uploadModal').modal('show');
|
||
// 移除标题行和列名校验行
|
||
data.shift(); // 移除第一行
|
||
data.shift(); // 移除第二行
|
||
|
||
// 从第三行读取列名
|
||
var columnNames = data[0]; // 第三行
|
||
var columnMapping = {};
|
||
for (var i = 0; i < columnNames.length; i++) {
|
||
var columnName = columnNames[i];
|
||
if (columnName) {
|
||
columnMapping[columnName.trim()] = i;
|
||
}
|
||
}
|
||
|
||
// 移除已经读取的第三行
|
||
data.shift(); // 移除第三行
|
||
|
||
// 初始化进度条
|
||
var filteredData = data.filter(row => !isEmptyRow(row));
|
||
var totalRows = filteredData.length;
|
||
var currentRow = 0;
|
||
|
||
// 定义一个异步函数来处理每一行数据
|
||
async function processRow(row) {
|
||
let send = {
|
||
password: $.cookie("pwd"),
|
||
cmd: 'setuserinfo'
|
||
};
|
||
var showname=null;
|
||
// 根据列名处理数据
|
||
if (row[columnMapping[lang.UISTR_ENROLL_ID.trim()]]) {
|
||
send["enrollid"] = convertEnrollId(row[columnMapping[lang.UISTR_ENROLL_ID.trim()]]);
|
||
showname=send["enrollid"];
|
||
}
|
||
if (row[columnMapping[lang.UISTR_UserName.trim()]]) {
|
||
send["name"] = row[columnMapping[lang.UISTR_UserName.trim()]];
|
||
showname=send["name"];
|
||
}
|
||
if (row[columnMapping[lang.UISTR_Department.trim()]]) {
|
||
send["department"] = row[columnMapping[lang.UISTR_Department.trim()]];
|
||
}
|
||
if (row[columnMapping[lang.UISTR_UserShift.trim()]]) {
|
||
send["shiftid"] = parseInt(row[columnMapping[lang.UISTR_UserShift.trim()]], 10);
|
||
}
|
||
if (row[columnMapping[lang.UISTR_ENROLL_MANAGER.trim()]]) {
|
||
send["admin"] = reverseAdmin(row[columnMapping[lang.UISTR_ENROLL_MANAGER.trim()]], 10);
|
||
}
|
||
if (row[columnMapping[lang.UISTR_PASSWORD_ENROLL.trim()]]) {
|
||
send["pwd"] = parseInt(row[columnMapping[lang.UISTR_PASSWORD_ENROLL.trim()]], 10);
|
||
}
|
||
if (row[columnMapping[lang.UISTR_CARD_ENROLL.trim()]]) {
|
||
send["card"] = parseInt(row[columnMapping[lang.UISTR_CARD_ENROLL.trim()]], 10);
|
||
}
|
||
if (row[columnMapping[lang.UISTR_LOCK_TIMEZONE.trim()]]) {
|
||
send["zoneid"] = parseInt(row[columnMapping[lang.UISTR_LOCK_TIMEZONE.trim()]], 10);
|
||
}
|
||
if (row[columnMapping[lang.UISTR_MAINMENU_GROUPID.trim()]]) {
|
||
send["groupid"] = parseInt(row[columnMapping[lang.UISTR_MAINMENU_GROUPID.trim()]], 10);
|
||
}
|
||
if (row[columnMapping[lang.UISTR_ACCESS_TIMES.trim()]]) {
|
||
send["access_times"] = parseInt(row[columnMapping[lang.UISTR_ACCESS_TIMES.trim()]], 10);
|
||
}
|
||
if (row[columnMapping[lang.UISTR_VERIFY_mode.trim()]]) {
|
||
send["verifymode"] = reverseVerifyMode(row[columnMapping[lang.UISTR_VERIFY_mode.trim()]], 10);
|
||
}
|
||
if (row[columnMapping[lang.UISTR_BIRTHDAY.trim()]]) {
|
||
send["birthday"] = row[columnMapping[lang.UISTR_BIRTHDAY.trim()]];
|
||
}
|
||
if (row[columnMapping[lang.UISTR_START.trim()]]) {
|
||
send["starttime"] = row[columnMapping[lang.UISTR_START.trim()]];
|
||
}
|
||
if (row[columnMapping[lang.UISTR_END.trim()]]) {
|
||
send["endtime"] = row[columnMapping[lang.UISTR_END.trim()]];
|
||
}
|
||
if (row[columnMapping[lang.UISTR_USER_PROFILE.trim()]]) {
|
||
send["userprofile"] = row[columnMapping[lang.UISTR_USER_PROFILE.trim()]];
|
||
}
|
||
|
||
// 更新进度条
|
||
updateProgressBar(totalRows,currentRow + 1,showname);
|
||
// 发送请求
|
||
return new Promise((resolve, reject) => {
|
||
sendToServer(send)
|
||
.then(() => resolve())
|
||
.catch(error => reject(error));
|
||
});
|
||
}
|
||
|
||
// 检查是否是空行
|
||
function isEmptyRow(row) {
|
||
return row.every(cell => {
|
||
if (typeof cell === 'string') {
|
||
return cell.trim() === '';
|
||
}
|
||
return !cell; // 对于非字符串类型,只要值为 false、null、undefined 或者 0 都视为空
|
||
});
|
||
}
|
||
|
||
// 使用 async/await 循环处理每一行数据
|
||
(async () => {
|
||
for (let row of filteredData) {
|
||
try {
|
||
await processRow(row);
|
||
currentRow++; // 增加当前行计数
|
||
} catch (error) {
|
||
console.error("Error processing row:", error);
|
||
break; // 如果发生错误,停止处理
|
||
}
|
||
}
|
||
})();
|
||
}
|
||
|
||
// 修改 sendToServer 函数以返回 Promise
|
||
function sendToServer(send) {
|
||
return new Promise((resolve, reject) => {
|
||
$.ajax({
|
||
url: debugserver + '/api',
|
||
type: 'POST',
|
||
contentType: "application/json",
|
||
dataType: "json",
|
||
data: JSON.stringify(send),
|
||
beforeSend: function() {
|
||
},
|
||
success: function(json) {
|
||
console.log("done", json);
|
||
if (json.ret === "setuserinfo" && json.result === true) {
|
||
resolve(); // 成功处理
|
||
} else {
|
||
reject(new Error("Failed to set user info")); // 处理失败
|
||
}
|
||
},
|
||
error: function(error) {
|
||
console.log("error::::", error);
|
||
reject(error);
|
||
}
|
||
});
|
||
});
|
||
}
|
||
function onUploadSuccess() {
|
||
const homeChannel = new BroadcastChannel('home_refresh_channel');
|
||
homeChannel.postMessage({type: 'refresh_home'});
|
||
homeChannel.close();
|
||
}
|
||
//////////////////////////////////////////
|
||
$(document).ready(function() {
|
||
$('#uploadModal').on('hidden.bs.modal', function () {
|
||
get_userlist();
|
||
onUploadSuccess();
|
||
});
|
||
if(poithide)
|
||
{
|
||
document.querySelectorAll('.optional-btn').forEach(el => {
|
||
el.style.display = 'none';
|
||
});
|
||
const hideColumns = ['department', 'shift', 'face','password', 'card', 'weekzone','access_times','birthday','fingerprint','palm'];
|
||
hideColumns.forEach(col => {
|
||
$table.bootstrapTable('hideColumn', col);
|
||
});
|
||
}
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|