perf: 完善签到功能

This commit is contained in:
kuaifan 2022-12-12 01:31:55 +08:00
parent e3cdb20579
commit 6937bbace8
9 changed files with 254 additions and 59 deletions

View File

@ -29,7 +29,7 @@ class PublicController extends AbstractController
$key = trim(Request::input('key'));
//
$setting = Base::setting('checkinSetting');
if ($setting['wifi'] !== 'open') {
if ($setting['open'] !== 'open') {
return <<<EOF
#!/bin/sh
echo "function off"
@ -84,7 +84,7 @@ class PublicController extends AbstractController
$time = intval(Request::input('time'));
//
$setting = Base::setting('checkinSetting');
if ($setting['wifi'] !== 'open') {
if ($setting['open'] !== 'open') {
return 'function off';
}
if ($key != $setting['key']) {
@ -94,11 +94,7 @@ class PublicController extends AbstractController
$macs = explode(",", $mac);
foreach ($macs as $item) {
$item = strtoupper($item);
if (empty($item) || !preg_match("/^[A-Fa-f\d]{2}:[A-Fa-f\d]{2}:[A-Fa-f\d]{2}:[A-Fa-f\d]{2}:[A-Fa-f\d]{2}:[A-Fa-f\d]{2}$/", $item)) {
continue;
}
$userCheckin = UserCheckin::whereMac($item)->first();
if ($userCheckin) {
if (Base::isMac($item) && $userCheckin = UserCheckin::whereMac($item)->first()) {
UserCheckinRecord::createInstance([
'userid' => $userCheckin->userid,
'mac' => $userCheckin->mac,

View File

@ -223,7 +223,7 @@ class SystemController extends AbstractController
*
* @apiParam {String} type
* - get: 获取(默认)
* - save: 保存设置(参数:['wifi', 'key']
* - save: 保存设置(参数:['open', 'edit', 'key']
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
@ -240,13 +240,14 @@ class SystemController extends AbstractController
$all = Request::input();
foreach ($all as $key => $value) {
if (!in_array($key, [
'wifi',
'open',
'edit',
'key',
])) {
unset($all[$key]);
}
}
if ($all['wifi'] === 'close') {
if ($all['open'] === 'close') {
$all['key'] = md5(Base::generatePassword(32));
}
$setting = Base::setting('checkinSetting', Base::newTrim($all));
@ -259,7 +260,8 @@ class SystemController extends AbstractController
Base::setting('checkinSetting', $setting);
}
//
$setting['wifi'] = $setting['wifi'] ?: 'close';
$setting['open'] = $setting['open'] ?: 'close';
$setting['edit'] = $setting['edit'] ?: 'close';
$setting['cmd'] = "curl -sSL '" . Base::fillUrl("api/public/checkin/install?key={$setting['key']}") . "' | sh";
//
return Base::retSuccess('success', $setting ?: json_decode('{}'));

View File

@ -506,9 +506,13 @@ class UsersController extends AbstractController
* - no: 未认证
* - 其他值: 全部(默认)
* - keys.department 部门ID0表示默认部门不赋值获取所有部门
* - keys.checkin_mac 签到mac地址
*
* @apiParam {Number} [page] 当前页,默认:1
* @apiParam {Number} [pagesize] 每页显示数量,默认:20,最大:50
* @apiParam {Number} [checkin_mac] 获取签到mac地址
* - 0: 不获取(默认)
* - 1: 获取
* @apiParam {Number} [page] 当前页,默认:1
* @apiParam {Number} [pagesize] 每页显示数量,默认:20,最大:50
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
@ -573,11 +577,23 @@ class UsersController extends AbstractController
$builder->where("department", "like", "%,{$keys['department']},%");
}
}
if (isset($keys['checkin_mac'])) {
$builder->whereIn('userid', function ($query) use ($keys) {
$query->select('userid')->from('user_checkins')->where("mac", "like", "%{$keys['checkin_mac']}%");
});
}
} else {
$builder->whereNull('disable_at');
}
$list = $builder->orderByDesc('userid')->paginate(Base::getPaginate(50, 20));
//
if (intval(Request::input('checkin_mac')) === 1) {
$list->transform(function (User $user) {
$user->checkin_macs = UserCheckin::whereUserid($user->userid)->orderBy('id')->pluck('mac');
return $user;
});
}
//
return Base::retSuccess('success', $list);
}
@ -593,6 +609,7 @@ class UsersController extends AbstractController
* @apiParam {String} [type] 操作
* - setadmin 设为管理员
* - clearadmin 取消管理员
* - checkin_macs 修改自动签到mac地址需要参数 checkin_macs
* - department 修改部门(需要参数 department
* - setdisable 设为离职(需要参数 disable_time、transfer_userid
* - cleardisable 取消离职
@ -602,6 +619,7 @@ class UsersController extends AbstractController
* @apiParam {String} [password] 新的密码
* @apiParam {String} [nickname] 昵称
* @apiParam {String} [profession] 职位
* @apiParam {String} [checkin_macs] 自动签到mac地址
* @apiParam {String} [department] 部门
* @apiParam {String} [disable_time] 离职时间
* @apiParam {String} [transfer_userid] 离职交接人
@ -637,6 +655,19 @@ class UsersController extends AbstractController
$upArray['identity'] = array_diff($userInfo->identity, ['admin']);
break;
case 'checkin_macs':
$list = explode(",", $data['checkin_macs']);
$array = [];
foreach ($list as $item) {
$item = strtoupper($item);
if (Base::isMac($item)) {
$array[$item] = [
'mac' => $item,
];
}
}
return UserCheckin::saveMac($userInfo->userid, $array);
case 'department':
if (!is_array($data['department'])) {
$data['department'] = [];
@ -1328,9 +1359,13 @@ class UsersController extends AbstractController
{
$user = User::auth();
//
if (Base::settingFind('checkinSetting', 'wifi') !== 'open') {
$setting = Base::setting('checkinSetting');
if ($setting['open'] !== 'open') {
return Base::retError('此功能未开启,请联系管理员开启');
}
if ($setting['edit'] !== 'open') {
return Base::retError('未开放修改权限,请联系管理员');
}
//
$list = Base::getPostValue('list');
$array = [];
@ -1339,36 +1374,18 @@ class UsersController extends AbstractController
}
foreach ($list AS $item) {
$item = Base::newTrim($item);
if (empty($item['mac']) || !preg_match("/^[A-Fa-f\d]{2}:[A-Fa-f\d]{2}:[A-Fa-f\d]{2}:[A-Fa-f\d]{2}:[A-Fa-f\d]{2}:[A-Fa-f\d]{2}$/", $item['mac'])) {
continue;
if (Base::isMac($item['mac'])) {
$mac = strtoupper($item['mac']);
$array[$mac] = [
'mac' => $mac,
'remark' => substr($item['remark'], 0, 50),
];
}
$array[] = [
'mac' => strtoupper($item['mac']),
'remark' => substr($item['remark'], 0, 50),
];
}
if (count($array) > 3) {
return Base::retError('最多只能添加3个MAC地址');
}
//
return AbstractModel::transaction(function() use ($array, $user) {
$ids = [];
$list = [];
foreach ($array as $item) {
$row = UserCheckin::updateInsert([
'userid' => $user->userid,
'mac' => $item['mac'],
], [
'remark' => $item['remark'],
]);
if ($row) {
$ids[] = $row->id;
$list[] = $row;
}
}
UserCheckin::whereUserid($user->userid)->whereNotIn('id', $ids)->delete();
//
return Base::retSuccess('success', $list);
});
return UserCheckin::saveMac($user->userid, $array);
}
}

View File

@ -27,5 +27,36 @@ use App\Module\Base;
*/
class UserCheckin extends AbstractModel
{
/**
* 保存mac地址
* @param $userid
* @param $array
* @return mixed
*/
public static function saveMac($userid, $array)
{
return AbstractModel::transaction(function() use ($array, $userid) {
$ids = [];
$list = [];
foreach ($array as $item) {
$update = [];
if ($item['remark']) {
$update = [
'remark' => $item['remark']
];
}
$row = UserCheckin::updateInsert([
'userid' => $userid,
'mac' => $item['mac']
], $update);
if ($row) {
$ids[] = $row->id;
$list[] = $row;
}
}
UserCheckin::whereUserid($userid)->whereNotIn('id', $ids)->delete();
//
return Base::retSuccess('修改成功', $list);
});
}
}

View File

@ -1032,6 +1032,20 @@ class Base
}
}
/**
* 正则判断是否MAC地址
* @param $str
* @return bool
*/
public static function isMac($str)
{
if (preg_match("/^[A-Fa-f\d]{2}:[A-Fa-f\d]{2}:[A-Fa-f\d]{2}:[A-Fa-f\d]{2}:[A-Fa-f\d]{2}:[A-Fa-f\d]{2}$/", $str)) {
return true;
} else {
return false;
}
}
/**
* 判断身份证是否正确
* @param $id

View File

@ -91,7 +91,15 @@
</Select>
</div>
</li>
<li>
<li v-if="mode==='checkin_mac'">
<div class="search-label">
{{$L("MAC地址")}}
</div>
<div class="search-content">
<Input v-model="keys.checkin_mac" :placeholder="$L('MAC地址')" clearable/>
</div>
</li>
<li v-else>
<div class="search-label">
{{$L("邮箱认证")}}
</div>
@ -192,6 +200,22 @@
</div>
</Modal>
<!--修改MAC-->
<Modal
v-model="checkinMacEditShow"
:title="$L('修改签到MAC地址')">
<Form :model="checkinMacEditData" label-width="auto" @submit.native.prevent>
<Alert type="error" style="margin-bottom:18px">{{$L(`正在进行帐号【ID:${checkinMacEditData.userid}${checkinMacEditData.nickname}】MAC地址修改。`)}}</Alert>
<FormItem :label="$L('MAC地址')">
<TagInput v-model="checkinMacEditData.checkin_macs"/>
</FormItem>
</Form>
<div slot="footer" class="adaption">
<Button type="default" @click="checkinMacEditShow=false">{{$L('取消')}}</Button>
<Button type="primary" :loading="checkinMacEditLoading > 0" @click="operationUser(checkinMacEditData, true)">{{$L('确定修改')}}</Button>
</div>
</Modal>
<!--修改部门-->
<Modal
v-model="departmentEditShow"
@ -259,6 +283,12 @@ import UserInput from "../../../components/UserInput";
export default {
name: "TeamManagement",
components: {UserInput},
props: {
mode: {
type: String,
default: 'user'
},
},
data() {
return {
loadIng: 0,
@ -403,7 +433,11 @@ export default {
return h('div', this.$L('默认部门'));
} else {
const tmp = []
tmp.push(h('span', departments[0]))
tmp.push(h('span', {
domProps: {
title: departments[0]
}
}, departments[0]))
if (departments.length > 1) {
departments = departments.splice(1)
tmp.push(h('ETooltip', [
@ -462,6 +496,14 @@ export default {
},
}, [h('div', this.$L('修改密码'))]))
if (this.mode === 'checkin_mac') {
dropdownItems.push(h('EDropdownItem', {
props: {
command: 'checkin_mac',
},
}, [h('div', this.$L('修改MAC'))]))
}
dropdownItems.push(h('EDropdownItem', {
props: {
command: 'department',
@ -536,6 +578,10 @@ export default {
total: 0,
noText: '',
checkinMacEditShow: false,
checkinMacEditLoading: 0,
checkinMacEditData: {},
departmentEditShow: false,
departmentEditLoading: 0,
departmentEditData: {},
@ -610,6 +656,45 @@ export default {
dialogList: [],
}
},
created() {
if (this.mode === 'checkin_mac') {
this.columns.splice(5, 0, {
title: this.$L('MAC地址'),
key: 'checkin_mac',
minWidth: 80,
render: (h, {row}) => {
let checkin_macs = $A.cloneJSON(row.checkin_macs || [])
if (checkin_macs.length === 0) {
return h('div', '-');
} else {
const tmp = []
tmp.push(h('span', {
domProps: {
title: checkin_macs[0]
}
}, checkin_macs[0]))
if (checkin_macs.length > 1) {
checkin_macs = checkin_macs.splice(1)
tmp.push(h('ETooltip', [
h('div', {
slot: 'content',
domProps: {
innerHTML: checkin_macs.join("<br/>")
}
}),
h('div', {
class: 'department-tag-num'
}, ` +${checkin_macs.length}`)
]))
}
return h('div', {
class: 'team-table-department-warp'
}, tmp);
}
},
})
}
},
mounted() {
this.getLists();
this.getDepartmentLists();
@ -649,6 +734,7 @@ export default {
url: 'users/lists',
data: {
keys,
checkin_mac: this.mode === 'checkin_mac' ? 1 : 0,
page: Math.max(this.page, 1),
pagesize: Math.max($A.runNum(this.pageSize), 10),
},
@ -709,6 +795,16 @@ export default {
});
break;
case 'checkin_mac':
this.checkinMacEditData = {
type: 'checkin_macs',
userid: row.userid,
nickname: row.nickname,
checkin_macs: (row.checkin_macs || []).join(','),
};
this.checkinMacEditShow = true;
break;
case 'department':
let departments = []
row.department.some(did => {
@ -778,7 +874,9 @@ export default {
operationUser(data, tipErr) {
return new Promise((resolve, reject) => {
if (data.type == 'department') {
if (data.type == 'checkin_macs') {
this.checkinMacEditLoading++;
} else if (data.type == 'department') {
this.departmentEditLoading++;
} else if (data.type == 'setdisable') {
this.disableLoading++;
@ -792,7 +890,9 @@ export default {
$A.messageSuccess(msg);
this.getLists();
resolve()
if (data.type == 'department') {
if (data.type == 'checkin_macs') {
this.checkinMacEditShow = false;
} else if (data.type == 'department') {
this.departmentEditShow = false;
} else if (data.type == 'setdisable') {
this.disableShow = false;
@ -804,7 +904,9 @@ export default {
this.getLists();
reject(msg)
}).finally(_ => {
if (data.type == 'department') {
if (data.type == 'checkin_macs') {
this.checkinMacEditLoading--;
} else if (data.type == 'department') {
this.departmentEditLoading--;
} else if (data.type == 'setdisable') {
this.disableLoading--;

View File

@ -2,18 +2,28 @@
<div class="setting-component-item">
<Form ref="formData" :model="formData" :rules="ruleData" label-width="auto" @submit.native.prevent>
<div class="block-setting-box">
<h3>{{ $L('WIFI签到') }}</h3>
<FormItem :label="$L('功能开启')" prop="wifi">
<RadioGroup v-model="formData.wifi">
<h3>{{ $L('WIFI自动签到') }}</h3>
<FormItem :label="$L('功能开启')" prop="open">
<RadioGroup v-model="formData.open">
<Radio label="open">{{ $L('开启') }}</Radio>
<Radio label="close">{{ $L('关闭') }}</Radio>
</RadioGroup>
<div class="export-data" @click="exportShow=true">{{$L('导出签到数据')}}</div>
<div class="export-data">
<p @click="allUserShow=true">{{$L('管理成员MAC地址')}}</p>
<p @click="exportShow=true">{{$L('导出签到数据')}}</p>
</div>
</FormItem>
<template v-if="formData.wifi === 'open'">
<template v-if="formData.open === 'open'">
<FormItem :label="$L('允许修改')" prop="edit">
<RadioGroup v-model="formData.edit">
<Radio label="open">{{ $L('允许') }}</Radio>
<Radio label="close">{{ $L('禁止') }}</Radio>
</RadioGroup>
<div class="form-tip">{{$L('允许成员自己修改MAC地址')}}</div>
</FormItem>
<FormItem :label="$L('功能说明')" prop="explain">
<p>1. {{$L('此功能仅支持手机客户端使用。')}}</p>
<p>2. {{$L('手机连接上指定路由器WIFI后自动签到。')}}{{$L('(注:理论上不限制连接方式)')}}</p>
<p>2. {{$L('手机连接上指定路由器WIFI后自动签到。')}}{{$L('(注:不限制连接方式)')}}</p>
<p>3. {{$L('签到延迟时长为±1分钟。')}}</p>
</FormItem>
<FormItem :label="$L('安装说明')" prop="install">
@ -38,6 +48,7 @@
<Form ref="export" :model="exportData" label-width="auto" @submit.native.prevent>
<FormItem :label="$L('导出成员')">
<UserInput v-model="exportData.userid" :multiple-max="20" :placeholder="$L('请选择成员')"/>
<div class="form-tip">{{$L('每次最多选择导出20个成员')}}</div>
</FormItem>
<FormItem :label="$L('签到日期')">
<DatePicker
@ -46,7 +57,7 @@
format="yyyy/MM/dd"
style="width:100%"
:placeholder="$L('请选择签到日期')"/>
<div class="page-setting-checkin-export-common">
<div class="form-tip page-setting-checkin-export-common">
{{$L('快捷选择')}}:
<em @click="exportData.date=dateShortcuts('prev')">上个月</em>
<em @click="exportData.date=dateShortcuts('this')">这个月</em>
@ -59,7 +70,7 @@
format="HH:mm"
style="width:100%"
:placeholder="$L('请选择签到时间')"/>
<div class="page-setting-checkin-export-common">
<div class="form-tip page-setting-checkin-export-common">
{{$L('快捷选择')}}:
<em @click="exportData.time=['8:30', '18:00']">8:30-18:00</em>
<em @click="exportData.time=['9:00', '18:00']">9:00-18:00</em>
@ -72,20 +83,31 @@
<Button type="primary" :loading="exportLoadIng > 0" @click="onExport">{{$L('导出')}}</Button>
</div>
</Modal>
<!--查看所有团队-->
<DrawerOverlay
v-model="allUserShow"
placement="right"
:size="1380">
<TeamManagement v-if="allUserShow" mode="checkin_mac"/>
</DrawerOverlay>
</div>
</template>
<script>
import UserInput from "../../../../components/UserInput";
import DrawerOverlay from "../../../../components/DrawerOverlay";
import TeamManagement from "../../components/TeamManagement";
export default {
name: "SystemCheckin",
components: {UserInput},
components: {TeamManagement, DrawerOverlay, UserInput},
data() {
return {
loadIng: 0,
formData: {
wifi: '',
open: '',
edit: '',
cmd: '',
},
ruleData: {},
@ -107,6 +129,8 @@ export default {
]
},
allUserShow: false,
exportShow: false,
exportLoadIng: 0,
exportData: {

View File

@ -148,6 +148,12 @@
display: flex;
align-items: center;
> span {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.department-tag-num {
background-color: #515a6e;
border-radius: 9px;

View File

@ -244,10 +244,13 @@
height: 8px;
}
.export-data {
cursor: pointer;
color: #2b85e4;
&:hover {
text-decoration: underline;
margin-top: 6px;
> p {
cursor: pointer;
color: #2b85e4;
&:hover {
text-decoration: underline;
}
}
}
}