移动考勤-考勤记录

dev_xd
lj7788@126.com 2025-09-03 15:36:22 +08:00
parent 7b8ee3dc75
commit 6e3b63c1e4
17 changed files with 1044 additions and 25 deletions

View File

@ -16,7 +16,7 @@
<script
type="text/javascript"
src="https://api.map.baidu.com/api?type=webgl&v=1.0&ak=5M76qMCiVjSG7bGOTcYmZdg0MQinsKve"
src="https://api.map.baidu.com/api?type=webgl&v=1.0&ak=eseRcUMFiUlnWA6miQLejNpvS70H8SRN"
></script>
<script src="/cdn/vue/dist/vue.js"></script>
<script src="/cdn/element-ui/lib/index.js"></script>

View File

@ -293,13 +293,13 @@ function initClientClipping(that) {
}
//剖切 -- 服务端渲染
function initClipping(that) {
let api = bimBriefingApi;
api.Public.clearHandler();
api.Measurement.clearAllTrace();
if(that.isClient){
initClientClipping(that);
return;
}
let api = bimBriefingApi;
api.Model.clipByBox(getModels(that));
}

View File

@ -370,7 +370,7 @@ export default {
},
doSelectMenu(index) {
this.selectMenu = index;
if (index == 0) {
if (index == this.selectMenu) {
briefingTools.clearEvent(this);
this.showClipping = false;
briefingTools.clearSelectFeature(this);

View File

@ -0,0 +1,187 @@
package com.yanzhu.manage.domain;
import java.math.BigDecimal;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.yanzhu.common.core.annotation.Excel;
import com.yanzhu.common.core.web.domain.BaseEntity;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
/**
* pro_mobile_attendance_data
*
* @author yanzhu
* @date 2025-09-03
*/
public class ProMobileAttendanceData extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** $column.columnComment */
private Long id;
/** 用户ID */
@Excel(name = "用户ID")
private Long userId;
/** 项目ID */
@Excel(name = "项目ID")
private Long projectId;
/** 配置ID */
@Excel(name = "配置ID")
private Long cfgId;
/** 进还是出 */
@Excel(name = "进还是出")
private String inOut;
/** 经度 */
@Excel(name = "经度")
private BigDecimal longitude;
/** 纬度 */
@Excel(name = "纬度")
private BigDecimal latitude;
/** 考勤时间 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "考勤时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date attDate;
/** 考勤照片 */
@Excel(name = "考勤照片")
private String attImg;
/** $column.columnComment */
@Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()")
private Long isDel;
/** $column.columnComment */
@Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()")
private Long state;
public void setId(Long id)
{
this.id = id;
}
public Long getId()
{
return id;
}
public void setUserId(Long userId)
{
this.userId = userId;
}
public Long getUserId()
{
return userId;
}
public void setProjectId(Long projectId)
{
this.projectId = projectId;
}
public Long getProjectId()
{
return projectId;
}
public void setCfgId(Long cfgId)
{
this.cfgId = cfgId;
}
public Long getCfgId()
{
return cfgId;
}
public void setInOut(String inOut)
{
this.inOut = inOut;
}
public String getInOut()
{
return inOut;
}
public void setLongitude(BigDecimal longitude)
{
this.longitude = longitude;
}
public BigDecimal getLongitude()
{
return longitude;
}
public void setLatitude(BigDecimal latitude)
{
this.latitude = latitude;
}
public BigDecimal getLatitude()
{
return latitude;
}
public void setAttDate(Date attDate)
{
this.attDate = attDate;
}
public Date getAttDate()
{
return attDate;
}
public void setAttImg(String attImg)
{
this.attImg = attImg;
}
public String getAttImg()
{
return attImg;
}
public void setIsDel(Long isDel)
{
this.isDel = isDel;
}
public Long getIsDel()
{
return isDel;
}
public void setState(Long state)
{
this.state = state;
}
public Long getState()
{
return state;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("userId", getUserId())
.append("projectId", getProjectId())
.append("cfgId", getCfgId())
.append("inOut", getInOut())
.append("longitude", getLongitude())
.append("latitude", getLatitude())
.append("attDate", getAttDate())
.append("attImg", getAttImg())
.append("isDel", getIsDel())
.append("remark", getRemark())
.append("state", getState())
.append("createBy", getCreateBy())
.append("createTime", getCreateTime())
.append("updateBy", getUpdateBy())
.append("updateTime", getUpdateTime())
.toString();
}
}

View File

@ -0,0 +1,61 @@
package com.yanzhu.manage.mapper;
import java.util.List;
import com.yanzhu.manage.domain.ProMobileAttendanceData;
/**
* Mapper
*
* @author yanzhu
* @date 2025-09-03
*/
public interface ProMobileAttendanceDataMapper
{
/**
*
*
* @param id
* @return
*/
public ProMobileAttendanceData selectProMobileAttendanceDataById(Long id);
/**
*
*
* @param proMobileAttendanceData
* @return
*/
public List<ProMobileAttendanceData> selectProMobileAttendanceDataList(ProMobileAttendanceData proMobileAttendanceData);
/**
*
*
* @param proMobileAttendanceData
* @return
*/
public int insertProMobileAttendanceData(ProMobileAttendanceData proMobileAttendanceData);
/**
*
*
* @param proMobileAttendanceData
* @return
*/
public int updateProMobileAttendanceData(ProMobileAttendanceData proMobileAttendanceData);
/**
*
*
* @param id
* @return
*/
public int deleteProMobileAttendanceDataById(Long id);
/**
*
*
* @param ids
* @return
*/
public int deleteProMobileAttendanceDataByIds(Long[] ids);
}

View File

@ -0,0 +1,121 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yanzhu.manage.mapper.ProMobileAttendanceDataMapper">
<resultMap type="ProMobileAttendanceData" id="ProMobileAttendanceDataResult">
<result property="id" column="id" />
<result property="userId" column="user_id" />
<result property="projectId" column="project_id" />
<result property="cfgId" column="cfg_id" />
<result property="inOut" column="in_out" />
<result property="longitude" column="longitude" />
<result property="latitude" column="latitude" />
<result property="attDate" column="att_date" />
<result property="attImg" column="att_img" />
<result property="isDel" column="is_del" />
<result property="remark" column="remark" />
<result property="state" column="state" />
<result property="createBy" column="create_by" />
<result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" />
<result property="updateTime" column="update_time" />
</resultMap>
<sql id="selectProMobileAttendanceDataVo">
select id, user_id, project_id, cfg_id, in_out, longitude, latitude, att_date, att_img, is_del, remark, state, create_by, create_time, update_by, update_time from pro_mobile_attendance_data
</sql>
<select id="selectProMobileAttendanceDataList" parameterType="ProMobileAttendanceData" resultMap="ProMobileAttendanceDataResult">
<include refid="selectProMobileAttendanceDataVo"/>
<where>
<if test="userId != null "> and user_id = #{userId}</if>
<if test="projectId != null "> and project_id = #{projectId}</if>
<if test="cfgId != null "> and cfg_id = #{cfgId}</if>
<if test="inOut != null and inOut != ''"> and in_out = #{inOut}</if>
<if test="longitude != null "> and longitude = #{longitude}</if>
<if test="latitude != null "> and latitude = #{latitude}</if>
<if test="attDate != null "> and att_date = #{attDate}</if>
<if test="attImg != null and attImg != ''"> and att_img = #{attImg}</if>
<if test="isDel != null "> and is_del = #{isDel}</if>
<if test="state != null "> and state = #{state}</if>
</where>
</select>
<select id="selectProMobileAttendanceDataById" parameterType="Long" resultMap="ProMobileAttendanceDataResult">
<include refid="selectProMobileAttendanceDataVo"/>
where id = #{id}
</select>
<insert id="insertProMobileAttendanceData" parameterType="ProMobileAttendanceData" useGeneratedKeys="true" keyProperty="id">
insert into pro_mobile_attendance_data
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="userId != null">user_id,</if>
<if test="projectId != null">project_id,</if>
<if test="cfgId != null">cfg_id,</if>
<if test="inOut != null">in_out,</if>
<if test="longitude != null">longitude,</if>
<if test="latitude != null">latitude,</if>
<if test="attDate != null">att_date,</if>
<if test="attImg != null">att_img,</if>
<if test="isDel != null">is_del,</if>
<if test="remark != null">remark,</if>
<if test="state != null">state,</if>
<if test="createBy != null">create_by,</if>
<if test="createTime != null">create_time,</if>
<if test="updateBy != null">update_by,</if>
<if test="updateTime != null">update_time,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="userId != null">#{userId},</if>
<if test="projectId != null">#{projectId},</if>
<if test="cfgId != null">#{cfgId},</if>
<if test="inOut != null">#{inOut},</if>
<if test="longitude != null">#{longitude},</if>
<if test="latitude != null">#{latitude},</if>
<if test="attDate != null">#{attDate},</if>
<if test="attImg != null">#{attImg},</if>
<if test="isDel != null">#{isDel},</if>
<if test="remark != null">#{remark},</if>
<if test="state != null">#{state},</if>
<if test="createBy != null">#{createBy},</if>
<if test="createTime != null">#{createTime},</if>
<if test="updateBy != null">#{updateBy},</if>
<if test="updateTime != null">#{updateTime},</if>
</trim>
</insert>
<update id="updateProMobileAttendanceData" parameterType="ProMobileAttendanceData">
update pro_mobile_attendance_data
<trim prefix="SET" suffixOverrides=",">
<if test="userId != null">user_id = #{userId},</if>
<if test="projectId != null">project_id = #{projectId},</if>
<if test="cfgId != null">cfg_id = #{cfgId},</if>
<if test="inOut != null">in_out = #{inOut},</if>
<if test="longitude != null">longitude = #{longitude},</if>
<if test="latitude != null">latitude = #{latitude},</if>
<if test="attDate != null">att_date = #{attDate},</if>
<if test="attImg != null">att_img = #{attImg},</if>
<if test="isDel != null">is_del = #{isDel},</if>
<if test="remark != null">remark = #{remark},</if>
<if test="state != null">state = #{state},</if>
<if test="createBy != null">create_by = #{createBy},</if>
<if test="createTime != null">create_time = #{createTime},</if>
<if test="updateBy != null">update_by = #{updateBy},</if>
<if test="updateTime != null">update_time = #{updateTime},</if>
</trim>
where id = #{id}
</update>
<delete id="deleteProMobileAttendanceDataById" parameterType="Long">
delete from pro_mobile_attendance_data where id = #{id}
</delete>
<delete id="deleteProMobileAttendanceDataByIds" parameterType="String">
delete from pro_mobile_attendance_data where id in
<foreach item="id" collection="array" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
</mapper>

View File

@ -113,4 +113,16 @@ public class ProMobileAttendanceConfigController extends BaseController
{
return toAjax(proMobileAttendanceConfigService.deleteProMobileAttendanceConfigByIds(ids));
}
/**
*
*/
@RequiresPermissions("manage:mobileAttendConfig:add")
@PostMapping("/attendance")
public AjaxResult attendance(@RequestBody ProMobileAttendanceConfig cfg){
//根据用户上传的照片与用户信息的照片计算相似度
//增加考勤数据
//增加考勤历史记录
return AjaxResult.success("OK");
}
}

View File

@ -0,0 +1,61 @@
package com.yanzhu.manage.service;
import java.util.List;
import com.yanzhu.manage.domain.ProMobileAttendanceData;
/**
* Service
*
* @author yanzhu
* @date 2025-09-03
*/
public interface IProMobileAttendanceDataService
{
/**
*
*
* @param id
* @return
*/
public ProMobileAttendanceData selectProMobileAttendanceDataById(Long id);
/**
*
*
* @param proMobileAttendanceData
* @return
*/
public List<ProMobileAttendanceData> selectProMobileAttendanceDataList(ProMobileAttendanceData proMobileAttendanceData);
/**
*
*
* @param proMobileAttendanceData
* @return
*/
public int insertProMobileAttendanceData(ProMobileAttendanceData proMobileAttendanceData);
/**
*
*
* @param proMobileAttendanceData
* @return
*/
public int updateProMobileAttendanceData(ProMobileAttendanceData proMobileAttendanceData);
/**
*
*
* @param ids
* @return
*/
public int deleteProMobileAttendanceDataByIds(Long[] ids);
/**
*
*
* @param id
* @return
*/
public int deleteProMobileAttendanceDataById(Long id);
}

View File

@ -0,0 +1,100 @@
package com.yanzhu.manage.service.impl;
import java.util.List;
import com.yanzhu.common.core.context.SecurityContextHolder;
import com.yanzhu.common.core.utils.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.yanzhu.manage.mapper.ProMobileAttendanceDataMapper;
import com.yanzhu.manage.domain.ProMobileAttendanceData;
import com.yanzhu.manage.service.IProMobileAttendanceDataService;
/**
* Service
*
* @author yanzhu
* @date 2025-09-03
*/
@Service
public class ProMobileAttendanceDataServiceImpl implements IProMobileAttendanceDataService
{
@Autowired
private ProMobileAttendanceDataMapper proMobileAttendanceDataMapper;
/**
*
*
* @param id
* @return
*/
@Override
public ProMobileAttendanceData selectProMobileAttendanceDataById(Long id)
{
return proMobileAttendanceDataMapper.selectProMobileAttendanceDataById(id);
}
/**
*
*
* @param proMobileAttendanceData
* @return
*/
@Override
public List<ProMobileAttendanceData> selectProMobileAttendanceDataList(ProMobileAttendanceData proMobileAttendanceData)
{
return proMobileAttendanceDataMapper.selectProMobileAttendanceDataList(proMobileAttendanceData);
}
/**
*
*
* @param proMobileAttendanceData
* @return
*/
@Override
public int insertProMobileAttendanceData(ProMobileAttendanceData proMobileAttendanceData)
{
proMobileAttendanceData.setCreateBy(SecurityContextHolder.getUserName());
proMobileAttendanceData.setCreateTime(DateUtils.getNowDate());
return proMobileAttendanceDataMapper.insertProMobileAttendanceData(proMobileAttendanceData);
}
/**
*
*
* @param proMobileAttendanceData
* @return
*/
@Override
public int updateProMobileAttendanceData(ProMobileAttendanceData proMobileAttendanceData)
{
proMobileAttendanceData.setUpdateBy(SecurityContextHolder.getUserName());
proMobileAttendanceData.setUpdateTime(DateUtils.getNowDate());
return proMobileAttendanceDataMapper.updateProMobileAttendanceData(proMobileAttendanceData);
}
/**
*
*
* @param ids
* @return
*/
@Override
public int deleteProMobileAttendanceDataByIds(Long[] ids)
{
return proMobileAttendanceDataMapper.deleteProMobileAttendanceDataByIds(ids);
}
/**
*
*
* @param id
* @return
*/
@Override
public int deleteProMobileAttendanceDataById(Long id)
{
return proMobileAttendanceDataMapper.deleteProMobileAttendanceDataById(id);
}
}

View File

@ -0,0 +1 @@
<svg class="icon" style="width: 1.263671875em;height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1294 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3966"><path d="M0 727.578947l188.631579 0 0 296.421053-188.631579 0 0-296.421053ZM269.473684 565.894737l188.631579 0 0 458.105263-188.631579 0 0-458.105263ZM565.894737 377.263158l161.684211 0 0 646.736842-161.684211 0 0-646.736842ZM835.368421 188.631579l188.631579 0 0 835.368421-188.631579 0 0-835.368421ZM1104.842105 0l188.631579 0 0 1024-188.631579 0 0-1024Z" p-id="3967" fill="#ccc"></path></svg>

After

Width:  |  Height:  |  Size: 598 B

View File

@ -1,6 +1,9 @@
import fmt from "../../../utils/date.js";
import { getToken, getUserInfo } from "../../../../utils/auth.js";
import { securityFileUpload } from "../../../../utils/request.js"; // 导入文件上传工具
const app = getApp();
import { getMobileAttendanceConfigById } from "../../../../api/project.js";
import { calculateDistance } from "../../../../utils/location.js"; // 导入计算距离的工具函数
Page({
/**
@ -15,6 +18,10 @@ Page({
type: "",
cfgData: null,
arrSel: "in",
mapMarkers: [], // 地图标记
mapCircles: [], // 地图圆圈(用于显示考勤范围)
faceImage: "", // 人脸识别图片
faceImageUrl: "", // 上传后的人脸图片URL
},
/**
@ -54,10 +61,14 @@ Page({
loadData(id) {
try {
let cfgData = wx.getStorageSync("editAttCfg");
cfgData.attDate = fmt(new Date()).format("YYYY-MM-DD HH:mm:ss");
if (cfgData) {
this.setData({
cfgData: cfgData,
});
// 初始化地图标记
this.initMapMarkers(cfgData);
} else {
app.toast("参数错误!");
this.returnToPage();
@ -67,7 +78,284 @@ Page({
this.returnToPage();
}
},
// 初始化地图标记
initMapMarkers(cfgData) {
// 考勤地点标记
const markers = [];
const circles = [];
// 用户当前位置标记(如果有的话)
if (cfgData.attLongitude && cfgData.attLatitude) {
markers.push({
id: 1,
longitude: cfgData.attLongitude,
latitude: cfgData.attLatitude,
title: cfgData.attAddress || "当前位置",
iconPath: "/images/location-marker.png", // 使用项目中已有的图标文件
width: 30,
height: 30,
callout: {
content: cfgData.attAddress || "当前位置",
display: "ALWAYS",
fontSize: 12,
borderRadius: 5,
padding: 5,
},
});
}
// 考勤地点标记和范围圆圈
if (cfgData.longitude && cfgData.latitude) {
// 考勤地点标记
markers.push({
id: 2,
longitude: cfgData.longitude,
latitude: cfgData.latitude,
title: cfgData.address || "考勤地点",
iconPath: "/images/location-marker.png", // 使用项目中已有的图标文件
width: 30,
height: 30,
callout: {
content: cfgData.address || "考勤地点",
display: "ALWAYS",
fontSize: 12,
borderRadius: 5,
padding: 5,
},
});
// 考勤范围圆圈(如果设置了范围)
if (cfgData.range) {
circles.push({
id: 1,
longitude: cfgData.longitude,
latitude: cfgData.latitude,
radius: cfgData.range, // 范围半径(米)
strokeWidth: 2,
color: "#FF0000", // 红色边框,符合规范
fillColor: "#FF000033", // 带透明度的红色填充,符合规范
});
}
}
this.setData({
mapMarkers: markers,
mapCircles: circles,
});
},
// 打开摄像头进行人脸识别
openCamera() {
const that = this;
wx.chooseMedia({
count: 1,
mediaType: ["image"],
sourceType: ["camera"],
camera: "front", // 前置摄像头
success(res) {
console.log("chooseMedia success", res);
// 获取到图片临时路径
const tempImagePath = res.tempFiles[0].tempFilePath;
console.log("tempImagePath", tempImagePath);
// 在iPhone上可能需要特殊处理图片路径
that.setData(
{
faceImage: tempImagePath,
},
() => {
// setData完成后的回调
console.log("faceImage set successfully");
}
);
// 这里可以添加人脸识别的逻辑
// 由于项目中没有现成的人脸识别API这里只是演示如何获取图片
// 实际项目中可能需要调用后端的人脸识别接口
//that.performFaceRecognition(tempImagePath);
},
fail(err) {
console.error("打开摄像头失败", err);
app.toast("打开摄像头失败,请重试");
},
});
},
// 执行人脸识别(模拟)
performFaceRecognition() {
let imagePath = this.data.faceImage;
// 这里应该调用实际的人脸识别API
// 由于项目中没有现成的人脸识别功能,这里只是模拟
app.toast("正在识别中...");
// 上传图片到服务器
this.uploadFaceImage();
// 模拟识别过程
setTimeout(() => {
// 模拟识别成功
app.toast("人脸识别成功");
// 可以在这里设置识别成功的状态
this.setData({
isFaceRecognized: true,
});
}, 2000);
},
// 上传人脸图片到服务器
uploadFaceImage(cb) {
let imagePath = this.data.faceImage;
app.toast("正在上传图片...");
securityFileUpload(imagePath)
.then((res) => {
console.log("图片上传成功", res);
if (res.code == 200) {
app.toast("图片上传成功");
// 保存上传后的图片URL
this.setData({
faceImageUrl: res.data.url,
});
cb && cb();
} else {
app.toast("图片上传失败: " + res.msg);
}
})
.catch((err) => {
console.error("图片上传失败", err);
app.toast("图片上传失败");
});
},
doSave() {
debugger;
if (!this.data.faceImage) {
app.toast("未获取到照片!");
return;
}
// 获取当前位置并计算距离
this.getCurrentLocationAndCalculateDistance();
},
// 获取当前位置并计算距离
getCurrentLocationAndCalculateDistance() {
wx.getLocation({
type: "gcj02", // 使用国测局坐标系
success: (res) => {
const { longitude, latitude } = res;
const cfgData = this.data.cfgData;
// 使用现有的工具函数计算距离
const distance = calculateDistance(
longitude,
latitude,
cfgData.longitude,
cfgData.latitude
);
// 检查是否超出考勤范围
if (distance > cfgData.range) {
app.toast("超出考勤范围!");
return;
}
// 更新考勤数据的位置信息
this.setData(
{
"cfgData.attLongitude": longitude,
"cfgData.attLatitude": latitude,
},
() => {
// 在数据更新完成后,更新地图上的用户位置标记
this.updateUserLocationMarker(longitude, latitude);
}
);
// 继续执行打卡操作
this.uploadFaceImage(() => {
console.log(this.data.faceImageUrl);
debugger;
});
},
fail: (err) => {
console.error("获取位置失败", err);
// 检查是否是权限问题
if (err.errMsg && err.errMsg.includes("auth deny")) {
app.toast("请开启位置权限后再试");
// 引导用户开启权限
wx.showModal({
title: "提示",
content:
"需要获取您的位置信息才能进行考勤打卡,请在设置中开启位置权限",
showCancel: true,
confirmText: "去设置",
success: (res) => {
if (res.confirm) {
wx.openSetting({
success: (setting) => {
if (setting.authSetting["scope.userLocation"]) {
// 用户开启了权限,重新获取位置
this.getCurrentLocationAndCalculateDistance();
}
},
});
}
},
});
} else {
app.toast("获取位置失败,请检查定位权限");
}
},
});
},
// 更新用户位置标记
updateUserLocationMarker(longitude, latitude) {
// 创建新的标记数组
let markers = [...this.data.mapMarkers];
// 查找是否已存在用户位置标记id为1
let userMarkerIndex = markers.findIndex((marker) => marker.id === 1);
const userMarker = {
id: 1,
longitude: longitude,
latitude: latitude,
title: "当前位置",
iconPath: "/images/location-marker.png",
width: 30,
height: 30,
callout: {
content: "当前位置",
display: "ALWAYS",
fontSize: 12,
borderRadius: 5,
padding: 5,
},
};
if (userMarkerIndex !== -1) {
// 如果已存在用户位置标记,则更新它
markers[userMarkerIndex] = userMarker;
} else {
// 如果不存在用户位置标记,则添加它
markers.push(userMarker);
}
// 更新地图标记
this.setData({
mapMarkers: markers,
});
},
getDistance(longitude, latitude) {
// 获取当前位置计算距离
// 这里直接计算给定坐标与考勤地点的距离
const cfgData = this.data.cfgData;
if (!cfgData) return 0;
return calculateDistance(
longitude,
latitude,
cfgData.longitude,
cfgData.latitude
);
},
onProjectSelect(e) {
let projectId = e.detail.id;
let projectName = e.detail.text;
@ -80,4 +368,10 @@ Page({
url: "../list/index",
});
},
doSelectArr(e) {
this.setData({
arrSel: e.currentTarget.dataset.set,
"cfgData.attDate": fmt(new Date()).format("YYYY-MM-DD HH:mm:ss"),
});
},
});

View File

@ -17,20 +17,65 @@
<scroll-view class="max_content_scroll" type="list" scroll-y bindscrolltolower="onScrollToLower">
<project-select init="{{ initData }}" bindchange="onProjectSelect" id="projectSel"></project-select>
<view class="content_box">
<view class="content_box_title">
<svg-icon src="switch" color="#9966CC" size="40" class="inline-block"/>
<text class="txt-blue">请选择考勤方向</text>
<view class="content_box">
<view class="content_box_title">
<svg-icon src="switch" color="#9966CC" size="40" class="inline-block" />
<text class="txt-blue"> 请选择考勤方向</text>
</view>
<view class="content_box_content arrow_content">
<view class="arrow_box {{arrSel=='in'?'is-active':''}}" bindtap="doSelectArr" data-set="in">
<svg-icon src="select" wx:if="{{arrSel=='in'}}" colors="{{['#07C160','#fff']}}" size="40" class="img-sel" />
<view>上班考勤打卡</view>
</view>
<view class="content_box_content arrow_content">
<view class="arrow_box {{arrSel=='in'?'is-active':''}}" bindtap="doSelectArr('in')">
<svg-icon src="select" wx:if="{{arrSel=='in'}}" colors="{{['#07C160','#fff']}}" size="40" class="img-sel"/>
<view>上班考勤打卡</view>
</view>
<view class="arrow_box {{arrSel=='out'?'is-active':''}}" bindtap="doSelectArr('out')">
<svg-icon src="select" wx:if="{{arrSel=='out'}}" colors="{{['#07C160','#fff']}}" size="40" class="img-sel"/>
<view>下班考勤打卡</view>
</view>
<view class="arrow_box {{arrSel=='out'?'is-active':''}}" bindtap="doSelectArr" data-set="out">
<svg-icon src="select" wx:if="{{arrSel=='out'}}" colors="{{['#07C160','#fff']}}" size="40" class="img-sel" />
<view>下班考勤打卡</view>
</view>
</view>
</view>
<view class="content_box">
<view class="content_box_title">
<svg-icon src="signal" color="#9966CC" size="40" class="inline-block" />
<text class="txt-blue"> 考勤信息</text>
</view>
<view class="content_box_content att-info">
<view class="box_row">
<view class="row_title txt-blue">考勤标题:</view>
<view class="row-content">{{cfgData.title}}</view>
</view>
<view class="box_row">
<view class="row_title txt-blue">考勤时间:</view>
<view class="row-content">{{cfgData.attDate}}</view>
</view>
<view class="box_row">
<view class="row_title txt-blue">考勤位置:</view>
<view class="row-content">{{cfgData.attAddress}}</view>
</view>
<view class="box_map">
<map id="attendanceMap" longitude="{{cfgData.attLongitude || cfgData.longitude}}" latitude="{{cfgData.attLatitude || cfgData.latitude}}" scale="14"
show-location markers="{{mapMarkers}}" circles="{{mapCircles}}" style="width: 100%; height: 400rpx;">
</map>
</view>
</view>
</view>
<view class="content_box">
<view class="content_box_title">
<span class="markers"></span>
<text class="txt-blue"> 考勤人像刷脸</text>
</view>
<view class="content_box_content att-img">
<image src="{{faceImage}}" mode="aspectFit" wx:if="{{faceImage}}" />
<view wx:else class="placeholder-text">拍照后显示图片</view>
<button bindtap="openCamera">点击打开摄像头刷脸</button>
</view>
</view>
<view class="problem_submit_to">
<view class="problem_submit_to_btn problem_submit_to_save" bindtap="doSave">打卡</view>
<view class="problem_submit_to_btn" bindtap="returnToPage">返回</view>
</view>
</scroll-view>

View File

@ -4,19 +4,26 @@
.content_box_title{
display: flex;
align-items: center;
margin-bottom: 20rpx;
}
.content_box_title text{
margin-left:10rpx;
}
.arrow_content{
display: flex;
}
.arrow_box{
background-color: #ccc;
background-color: #626262;
width:45%;
margin:0 5%;
position: relative;
display: flex;
align-items: center;
border-radius: 10rpx;
color: #aaa;
}
.arrow_box.is-active{
background-color: #37BBDE;
color:#000;
}
.arrow_box .img-sel{
position: absolute;
@ -26,6 +33,65 @@
}
.arrow_box view{
height: 120rpx;
line-height: 120rpx;
height: 160rpx;
line-height: 160rpx;
width:100% ;
text-align: center;
}
/* 地图样式 */
#attendanceMap {
width: 100%;
height: 300rpx;
border-radius: 10rpx;
}
.box_row{
display: flex;
line-height: 50rpx;
}
.att-info{
padding-left:40rpx;
}
.att-info .row-content{
padding-left:10rpx;
}
.box_map{
margin-top: 20rpx;
}
/* 人脸识别区域样式 */
.att-img {
display: flex;
flex-direction: column;
align-items: center;
padding: 20rpx 0;
}
.att-img image {
width: 400rpx;
height: 400rpx;
margin-bottom: 20rpx;
border-radius: 10rpx;
background-color: #1E2737;
}
.att-img .placeholder-text {
width: 400rpx;
height: 400rpx;
margin-bottom: 20rpx;
border-radius: 10rpx;
background-color: #1E2737;
display: flex;
align-items: center;
justify-content: center;
color: #999;
font-size: 24rpx;
}
.att-img button {
background-color: #07C160;
color: white;
border: none;
padding: 20rpx 20rpx;
border-radius: 10rpx;
font-size: 28rpx;
}

View File

@ -17,6 +17,7 @@ Page({
listData: [],
userLongitude: undefined,
userLatitude: undefined,
currentAddress: "", // 当前位置地址
pageNum: 1,
pageSize: 10,
},
@ -48,6 +49,7 @@ Page({
* 获取用户当前位置
*/
getUserLocation() {
debugger;
wx.getLocation({
type: "gcj02", // 使用国测局坐标
success: (res) => {
@ -55,7 +57,21 @@ Page({
userLongitude: res.longitude,
userLatitude: res.latitude,
});
this.getListData();
// 获取当前位置的地址信息
this.getCurrentAddress(res.longitude, res.latitude)
.then((address) => {
this.setData({
currentAddress: address,
});
this.getListData();
})
.catch((err) => {
console.error("获取当前位置地址失败", err);
this.setData({
currentAddress: "地址获取失败",
});
this.getListData();
});
},
fail: (err) => {
console.error("获取位置失败", err);
@ -86,6 +102,9 @@ Page({
it.longitude,
it.latitude
);
it.attLongitude = this.data.userLongitude;
it.attLatitude = this.data.userLatitude;
it.attAddress = this.data.currentAddress;
} else {
it.distance = -1; // 无法计算距离
}
@ -98,6 +117,32 @@ Page({
}
});
},
getCurrentAddress(longitude, latitude) {
// 使用腾讯地图API获取地址信息
const apiKey = "NUQBZ-UIYCW-H7GRI-YXOXA-WNZB7-IGFLY"; // 使用项目中已有的API密钥
const url = `https://apis.map.qq.com/ws/geocoder/v1/?location=${latitude},${longitude}&key=${apiKey}&get_poi=1`;
return new Promise((resolve, reject) => {
wx.request({
url: url,
method: "GET",
success: (res) => {
if (res.statusCode === 200 && res.data.status === 0) {
// 成功获取地址信息
const address = res.data.result.address || "未知地址";
resolve(address);
} else {
// API调用失败
reject(new Error(res.data.message || "获取地址失败"));
}
},
fail: (err) => {
// 网络请求失败
reject(new Error("网络请求失败"));
},
});
});
},
getInfo(e) {
let cfgData = e.currentTarget.dataset.set;
wx.setStorageSync("editAttCfg", cfgData);

View File

@ -19,11 +19,11 @@
<scroll-view class="max_content_scroll" type="list" scroll-y bindscrolltolower="onScrollToLower">
<project-select init="{{ initData }}" bindchange="onProjectSelect" id="projectSel"></project-select>
<view class="modify_video_nav" style="margin-top: 5rpx;">
<view>
<text>生效中的考勤点({{total}})</text>
</view>
</view>
<view wx:if="{{listData.length==0}}">
@ -72,6 +72,11 @@
<view class="inline-block"><rich-text nodes="{{distance.formatDistance(item.distance)}}"></rich-text>
</view>
</view>
<!-- 显示考勤点的地址信息 -->
<view class="content-row" wx:if="{{item.attAddress}}">
当前位置:
{{item.attAddress}}
</view>
</view>
</view>

View File

@ -29,4 +29,25 @@
.module_title{
flex-grow: 1;
text-align: right;
}
/* 用户位置信息样式 */
.user-location-info {
background-color: #f0f8ff;
padding: 20rpx 30rpx;
margin: 20rpx 30rpx;
border-radius: 10rpx;
border: 1px solid #45affb;
}
.location-title {
font-weight: bold;
color: #45affb;
margin-bottom: 10rpx;
}
.location-address {
font-size: 26rpx;
color: #333;
word-wrap: break-word;
}

View File

@ -15,7 +15,7 @@
<title>数字建安施工</title>
<script
type="text/javascript"
src="https://api.map.baidu.com/api?v=1.0&&type=webgl&ak=6zAD8CIavtzWnkGg0a7roush5maGMIPn"
src="https://api.map.baidu.com/api?v=1.0&&type=webgl&ak=eseRcUMFiUlnWA6miQLejNpvS70H8SRN"
></script>
<script src="/cdn/Cesium/Cesium.js"></script>
<script src="/cdn/Cesium/BIMGISEngine.js"></script>