微信小程序增加移动考勤管理

dev_xd
lj7788 2025-08-29 17:43:14 +08:00
parent 65eed2b6ce
commit 9876060c5f
35 changed files with 1611 additions and 59 deletions

View File

@ -32,9 +32,17 @@ const listForBG = (data) => {
});
};
const selectCountForBGByProjectId = (projetId) => {
return request({
url: `/manage/problemmodify/selectCountForBGByProjectId/`+projetId,
method: "get",
});
};
export default {
groupByProblemType,
groupByDangerType,
listCountForBG,
listForBG,
selectCountForBGByProjectId
};

View File

@ -69,6 +69,7 @@ export default {
deptInfo: {},
todayCnt: 0,
weekCnt: 0,
baseData: [],
warningType: [
{
name: "AI预警",
@ -96,10 +97,25 @@ export default {
created() {
this.$bus.$on('projectChange', debounce(res => {
this.prjInfo = res;
this.getMonitAndWarning();
//this.getMonitAndWarning();
debugger
this.$api.safety.selectCountForBGByProjectId(res.id).then(d => {
this.baseData = d.data || [];
this.warningType[0].total = this.getBaseData("a");
this.warningType[1].total = this.getBaseData("c");
this.warningType[1].data = this.getBaseData("d");
this.warningType[2].total = this.getBaseData("e");
this.warningType[2].data = this.getBaseData("f");
this.todayCnt = this.getBaseData("g");
this.weekCnt = this.getBaseData("h");
});
}));
},
methods: {
getBaseData(type) {
let tmp = this.baseData.find(item => item.projectName == type);
return tmp ? tmp.id || 0 : 0;
},
doShowDetail(item) {
if (item.name == '劳资预警') {
if (this.type == "detail") {
@ -219,8 +235,9 @@ export default {
font-size: 20px;
background-size: 12px 24px;
}
.survey-oil-summary{
font-size:30px !important;
.survey-oil-summary {
font-size: 30px !important;
}
}
@ -243,9 +260,10 @@ export default {
height: 10px;
margin-right: 8px;
}
.glr-rate-value{
span{
font-size:30px;
.glr-rate-value {
span {
font-size: 30px;
}
}
}
@ -279,8 +297,9 @@ export default {
font-size: 30px;
background-size: 18px 36px;
}
.survey-oil-summary{
font-size:40px !important;
.survey-oil-summary {
font-size: 40px !important;
}
}
@ -302,9 +321,10 @@ export default {
height: 16px;
margin-right: 10px;
}
.glr-rate-value{
span{
font-size:40px;
.glr-rate-value {
span {
font-size: 40px;
}
}
}

View File

@ -310,6 +310,17 @@ public class SmzSspProblemmodifyController extends BaseController
return success(list);
}
/**
* --
* @param projectId
* @return
*/
@GetMapping("/selectCountForBGByProjectId/{projectId}")
public AjaxResult selectCountForBGByProjectId(@PathVariable("projectId") Long projectId){
List<SmzSspProblemmodify> list=smzSspProblemmodifyService.selectCountForBGByProjectId(projectId);
return success(list);
}
/** (ssp_proble_type)
* @param where
* @return

View File

@ -112,4 +112,11 @@ public interface ISmzSspProblemmodifyService
* @return
*/
public List<SmzSspProblemmodify> getListCountBG(SmzSspProblemmodify where);
/**
* --
* @param projectId
* @return
*/
List<SmzSspProblemmodify> selectCountForBGByProjectId(Long projectId);
}

View File

@ -13,6 +13,7 @@ import com.yanzhu.system.api.model.LoginUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
@ -198,4 +199,14 @@ public class SmzSspProblemmodifyServiceImpl implements ISmzSspProblemmodifyServi
return smzSspProblemmodifyMapper.getListCountBG(where);
}
/**
* --
* @param projectId
* @return
*/
@Override
public List<SmzSspProblemmodify> selectCountForBGByProjectId(Long projectId) {
return smzSspProblemmodifyMapper.selectCountForBGByProjectId(projectId);
}
}

View File

@ -54,7 +54,7 @@
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</filter>大屏-项目情况-项目监测预警
</appender>
<!-- 系统模块日志级别控制 -->

Binary file not shown.

View File

@ -1,7 +1,8 @@
{
"pages": [
"pages/login/login",
"pages/project_qr/index"
"pages/project_qr/index",
"pages/test-stepper/index"
],
"subpackages": [
{
@ -43,7 +44,10 @@
"project_schedule/info/index",
"project_checked/list/index",
"project_checked/add/index",
"project_checked/info/index"
"project_checked/info/index",
"mobile_attendance/attendance/index",
"mobile_attendance/attendance_config/list/index",
"mobile_attendance/attendance_config/add/index"
],
"independent": false
}
@ -119,5 +123,5 @@
"requiredPrivateInfos": [
"getLocation"
],
"lazyCodeLoading":"requiredComponents"
"lazyCodeLoading": "requiredComponents"
}

View File

@ -888,7 +888,12 @@ page {
border-radius: 10rpx;
padding: 20rpx;
}
.add_input {
width: calc(100% - 40rpx);
background: #212737;
border-radius: 10rpx;
padding: 20rpx;
}
.add_btn {
padding-top: 100rpx;
display: flex;

View File

@ -0,0 +1,64 @@
# Custom Stepper 自定义步进器组件
## 介绍
自定义步进器组件类似于Vant Weapp的van-stepper包含两个按钮和一个输入框可用于在设定的范围内增加或减少数值。
## 使用示例
在页面的JSON配置文件中注册组件
```json
{
"usingComponents": {
"custom-stepper": "/components/custom-stepper/index"
}
}
```
在WXML中使用组件
```xml
<custom-stepper
value="{{ value }}"
min="0"
max="100"
step="1"
disabled="{{ false }}"
bindchange="onChange" />
```
在页面JS中处理事件
```javascript
Page({
data: {
value: 0
},
onChange(e) {
this.setData({
value: e.detail.value
});
}
});
```
## 属性说明
| 属性名 | 类型 | 默认值 | 说明 |
|-------|------|-------|------|
| value | Number | 0 | 当前值 |
| min | Number | 0 | 最小值 |
| max | Number | 100 | 最大值 |
| step | Number | 1 | 步长 |
| disabled | Boolean | false | 是否禁用 |
## 事件说明
| 事件名 | 说明 | 返回值 |
|-------|------|-------|
| bindchange | 当值发生变化时触发 | { value: Number } |
## 更新日志
### 1.0.0
- 初始版本发布
- 实现基本的步进功能
- 支持设置范围、步长和禁用状态

View File

@ -0,0 +1,119 @@
Component({
options: {
addGlobalClass: true,
},
properties: {
// 当前值
value: {
type: Number,
value: 0,
observer: function (newVal) {
this.setData({
currentValue: newVal
});
}
},
// 最小值
min: {
type: Number,
value: 0
},
// 最大值
max: {
type: Number,
value: 100
},
// 步长
step: {
type: Number,
value: 1
},
// 是否禁用
disabled: {
type: Boolean,
value: false
}
},
data: {
currentValue: 0
},
lifetimes: {
attached: function () {
this.setData({
currentValue: this.data.value
});
}
},
methods: {
// 减少值
onMinus: function () {
if (this.data.disabled) return;
const newValue = this.data.currentValue - this.data.step;
if (newValue >= this.data.min) {
this.updateValue(newValue);
}
},
// 增加值
onPlus: function () {
if (this.data.disabled) return;
const newValue = this.data.currentValue + this.data.step;
if (newValue <= this.data.max) {
this.updateValue(newValue);
}
},
// 输入框输入事件
onInput: function (e) {
if (this.data.disabled) return;
const value = parseFloat(e.detail.value);
if (!isNaN(value)) {
this.setData({
currentValue: value
});
}
},
// 输入框失去焦点事件
onBlur: function () {
if (this.data.disabled) return;
let value = this.data.currentValue;
// 限制范围
if (value < this.data.min) {
value = this.data.min;
} else if (value > this.data.max) {
value = this.data.max;
}
this.updateValue(value);
},
// 更新值
updateValue: function (value) {
// 保留两位小数
value = Math.round(value * 100) / 100;
this.setData({
currentValue: value
});
// 触发自定义事件
this.triggerEvent('change', {
value: value
});
}
}
});

View File

@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}

View File

@ -0,0 +1,5 @@
<view class="custom-stepper">
<view class="stepper-button minus" bindtap="onMinus">-</view>
<input class="stepper-input" value="{{ value }}" bindinput="onInput" bindblur="onBlur" />
<view class="stepper-button plus" bindtap="onPlus">+</view>
</view>

View File

@ -0,0 +1,36 @@
.custom-stepper {
display: flex;
align-items: center;
border: 1px solid #212737;
border-radius: 4px;
}
.stepper-button {
text-align: center;
padding: 8px 0;
background-color: #2196F3;
user-select: none;
width: 80rpx;
}
.stepper-button:active {
background-color: #e0e0e0;
}
.stepper-button.minus {
border-right: 1px solid #212737;
}
.stepper-button.plus {
border-left: 1px solid #212737;
}
.stepper-input {
flex: 2;
text-align: center;
padding: 8px 0;
border: none;
outline: none;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 812 B

View File

@ -0,0 +1,106 @@
// pageage/mobile_attendance/attendance_config/index.js
import config from "../../../config";
import fmt from "../../utils/date.js";
import { getToken, getUserInfo } from "../../../utils/auth";
import { uploadFiles } from "../../utils/upload.js";
import { tryToJson } from '../../utils/tools'
import {
getProjectChecked,
findPlanDatas,
listProProjectInfoSubdeptsUsers,
updateProjectChecked,
addProjectChecked,
} from "../../../api/project";
const app = getApp();
Page({
/**
* 页面的初始数据
*/
data: {
projectUserInfo: {},
projectUserInfo: {},
projectId: "",
projectName: "",
initData: {},
total: 0,
listData: [],
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
if (!getToken()) {
wx.redirectTo({
url: "../../../pages/login/login",
});
}
const proUserInfo = getUserInfo();
this.setData({
projectUserInfo: proUserInfo.projectUserInfo,
projectId: app.globalData.useProjectId,
projectName: app.globalData.useProjectName,
initData: {
id: app.globalData.useProjectId,
text: app.globalData.useProjectName,
},
});
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() { },
/**
* 生命周期函数--监听页面显示
*/
onShow() { },
/**
* 生命周期函数--监听页面隐藏
*/
onHide() { },
/**
* 生命周期函数--监听页面卸载
*/
onUnload() { },
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() { },
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() { },
/**
* 用户点击右上角分享
*/
onShareAppMessage() { },
//项目切换 返回值
onProjectSelect(e) {
let projectId = e.detail.id;
let projectName = e.detail.text;
app.globalData.useProjectId = projectId;
app.globalData.useProjectName = projectName;
this.onLoad();
},
returnToPage: function () {
/*关闭当前页面,跳转到其它页面。*/
if (wx.getStorageSync('nav-menu') == "xmgl") {
wx.redirectTo({
url: '../../project_more/index',
})
} else {
wx.redirectTo({
url: '../../project_quality/index',
})
}
},
});

View File

@ -0,0 +1,6 @@
{
"usingComponents": {},
"navigationStyle": "custom",
"styleIsolation": "apply-shared",
"backgroundColor": "#191d28"
}

View File

@ -0,0 +1,35 @@
<wxs module="format" src="/utils/format.wxs"></wxs>
<view class="header_title">
<view class="header_title_row">
<van-row>
<van-col span="4">
<view class="header_img" bindtap="returnToPage">
<image src="/images/left.png"></image>
</view>
</van-col>
<van-col span="15">
<view class="header_name">移动考勤打卡</view>
</van-col>
</van-row>
</view>
</view>
<scroll-view class="max_content_scroll" type="list" scroll-y>
<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}}">
<view style="padding-top: 70px;text-align: -webkit-center;">
<image src="https://szgcwx.jhncidg.com/staticFiles/nodata.png" style="width: 130px;height: 105px;"></image>
<view style="color: #a5abbb;">暂无数据</view>
</view>
</view>
</scroll-view>

View File

@ -0,0 +1,8 @@
/* pageage/mobile_attendance/attendance/index.wxss */
.modify_video_nav{
margin-top: 2px;
justify-content: left;
padding-left: 40rpx;
color: #fff;
}

View File

@ -0,0 +1,455 @@
// pageage/mobile_attendance/attendance_config/add/index.js
import { getToken, getUserInfo } from "../../../../utils/auth";
import { uploadFiles } from "../../../utils/upload.js";
import { tryToJson } from '../../../utils/tools'
import { findSubGroupsList, subdeptsList } from '../../../../api/project'
import {
findDictCache
} from '../../../../api/publics'
const app = getApp();
// 添加防抖变量避免频繁请求API
let lastRequestTime = 0;
const REQUEST_INTERVAL = 1000; // 1秒间隔
Page({
/**
* 页面的初始数据
*/
data: {
maxDate: new Date(2088, 1, 1).getTime(),
minDate: new Date().getTime(),
projectUserInfo: {},
projectId: "",
projectName: "",
initData: {},
form: {
title: '',
startDate: '',
endDate: '',
longitude: 0,
latitude: 0,
range: 50,
address: '',
subGroup: [],
groupIds: '',
groupNames: [],
},
markers: [],
// 全屏地图相关数据
showFullScreenMap: false,
mapLongitude: 0,
mapLatitude: 0,
fullScreenMarkers: [],
selectedAddress: '',
// 搜索相关数据
searchKeyword: '',
searchResults: [],
subGroupList: [],
groupList: [],
subDeptTypeList: [],
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
if (!getToken()) {
wx.redirectTo({
url: "../../../pages/login/login",
});
}
const proUserInfo = getUserInfo();
this.setData({
projectUserInfo: proUserInfo.projectUserInfo,
projectId: app.globalData.useProjectId,
projectName: app.globalData.useProjectName,
initData: {
id: app.globalData.useProjectId,
text: app.globalData.useProjectName,
},
});
// 获取当前位置作为初始位置
this.getUserLocation();
this.getSubGroupList();
},
getSubGroupList() {
findDictCache("sub_dept_type").then(dict => {
this.setData({
subDeptTypeList: dict.data
})
findSubGroupsList({
projectId: app.globalData.useProjectId,
pageNum: 1,
pageSize: 1000,
}).then(res => {
if (res.code == 200) {
let tmps = res.rows || [];
this.setData({
groupList: tmps
})
let obj = {};
tmps.forEach(it => {
let name = it.subDeptType == 1 ? '施工单位' : it.subDeptName;
if (!obj[name]) {
obj[name] = [it];
} else {
obj[name].push(it);
}
});
let subGroupList = [];
for (let key in obj) {
let val = obj[key][0]
let deptType = this.data.subDeptTypeList.find(dict => dict.dictValue == val.subDeptType)
subGroupList.push({
subDeptId: val.subDeptId,
subDeptName: val.subDeptName,
subDeptType: val.subDeptType,
subDeptTypeName: deptType?.dictLabel || '',
userList: (obj[key]).map(it => {
it.status = false;
it.userId = it.id;
it.userName = it.groupName;
return it;
})
});
}
this.setData({
subGroupList: subGroupList,
});
}
});
});
},
// 获取用户当前位置
getUserLocation() {
wx.getLocation({
type: 'gcj02', // 腾讯地图坐标系
success: (res) => {
const { latitude, longitude } = res;
// 设置地图初始位置
this.setData({
mapLatitude: latitude,
mapLongitude: longitude,
'form.latitude': latitude,
'form.longitude': longitude
});
// 获取地址信息
this.getAddressInfo(latitude, longitude);
},
fail: (err) => {
console.error('获取位置失败', err);
wx.showToast({
title: '请允许位置权限',
icon: 'none'
});
// 引导用户开启权限
setTimeout(() => {
wx.openSetting({
success: (setting) => {
if (setting.authSetting['scope.userLocation']) {
this.getUserLocation();
}
}
});
}, 1500);
}
});
},
// 获取地址信息(逆地理编码)- 带重试机制
getAddressInfo(latitude, longitude, isFullScreen = false) {
const now = Date.now();
// 检查请求间隔,避免频繁请求
if (now - lastRequestTime < REQUEST_INTERVAL) {
console.log('请求过于频繁,跳过本次请求');
return;
}
lastRequestTime = now;
// 使用腾讯地图API进行逆地理编码
// 注意需要替换为你自己的腾讯地图API密钥
const apiKey = 'NUQBZ-UIYCW-H7GRI-YXOXA-WNZB7-IGFLY';
const url = `https://apis.map.qq.com/ws/geocoder/v1/?location=${latitude},${longitude}&key=${apiKey}&get_poi=0`;
// 添加重试机制
this.requestWithRetry(url, 3, (success, data) => {
if (success) {
const address = data.result.address || '获取地址失败';
if (isFullScreen) {
// 全屏地图模式下更新选中地址
this.setData({
selectedAddress: address
});
} else {
// 普通模式下更新表单地址
this.setData({
"form.address": address
});
}
} else {
console.error('逆地理编码失败', data);
const errorMsg = '获取地址失败';
if (isFullScreen) {
this.setData({
selectedAddress: errorMsg
});
} else {
this.setData({
"form.address": errorMsg
});
}
wx.showToast({
title: '地址获取失败,请手动输入',
icon: 'none'
});
}
});
},
// 带重试机制的请求方法
requestWithRetry(url, maxRetries, callback, retryCount = 0) {
wx.request({
url: url,
success: (res) => {
if (res.data.status === 0) {
callback(true, res.data);
} else {
// 如果是因为请求频率限制导致的失败,进行重试
if (res.data.message && res.data.message.includes('请求量') && retryCount < maxRetries) {
console.log(`请求失败,${(retryCount + 1) * 1000}ms后进行第${retryCount + 1}次重试`);
setTimeout(() => {
this.requestWithRetry(url, maxRetries, callback, retryCount + 1);
}, (retryCount + 1) * 1000); // 递增延迟重试
} else {
callback(false, res.data);
}
}
},
fail: (err) => {
// 网络错误时进行重试
if (retryCount < maxRetries) {
console.log(`网络请求失败,${(retryCount + 1) * 1000}ms后进行第${retryCount + 1}次重试`);
setTimeout(() => {
this.requestWithRetry(url, maxRetries, callback, retryCount + 1);
}, (retryCount + 1) * 1000); // 递增延迟重试
} else {
callback(false, err);
}
}
});
},
// 显示全屏地图
showFullScreenMap() {
this.setData({
showFullScreenMap: true,
mapLatitude: this.data.form.latitude || this.data.mapLatitude,
mapLongitude: this.data.form.longitude || this.data.mapLongitude,
fullScreenMarkers: this.data.form.latitude && this.data.form.longitude ? [{
id: 1,
latitude: this.data.form.latitude,
longitude: this.data.form.longitude,
title: '考勤点',
iconPath: '/images/location-marker.png',
width: 30,
height: 30
}] : []
});
// 如果已有坐标,获取地址信息
if (this.data.form.latitude && this.data.form.longitude) {
this.getAddressInfo(this.data.form.latitude, this.data.form.longitude, true);
}
},
// 隐藏全屏地图
hideFullScreenMap() {
this.setData({
showFullScreenMap: false,
searchKeyword: '',
searchResults: []
});
},
// 全屏地图点击事件
onFullScreenMapTap(e) {
const { latitude, longitude } = e.detail;
this.setData({
mapLatitude: latitude,
mapLongitude: longitude,
fullScreenMarkers: [{
id: 1,
latitude: latitude,
longitude: longitude,
title: '考勤点',
iconPath: '/images/location-marker.png',
width: 30,
height: 30
}]
});
// 获取点击位置的地址信息
this.getAddressInfo(latitude, longitude, true);
},
// 确认选择的位置
confirmLocation() {
this.setData({
'form.latitude': this.data.mapLatitude,
'form.longitude': this.data.mapLongitude,
'form.address': this.data.selectedAddress,
showFullScreenMap: false,
markers: [{
id: 1,
latitude: this.data.mapLatitude,
longitude: this.data.mapLongitude,
title: '考勤点',
iconPath: '/images/location-marker.png',
width: 30,
height: 30
}],
searchKeyword: '',
searchResults: []
});
},
// 搜索输入事件
onSearchInput(e) {
this.setData({
searchKeyword: e.detail.value
});
},
// 搜索确认事件
onSearchConfirm() {
const keyword = this.data.searchKeyword.trim();
if (!keyword) {
wx.showToast({
title: '请输入搜索关键词',
icon: 'none'
});
return;
}
// 使用腾讯地图API进行地址搜索
const apiKey = 'NUQBZ-UIYCW-H7GRI-YXOXA-WNZB7-IGFLY';
const url = `https://apis.map.qq.com/ws/place/v1/suggestion/?keyword=${encodeURIComponent(keyword)}&key=${apiKey}&region=全国`;
wx.request({
url: url,
success: (res) => {
if (res.data.status === 0 && res.data.data && res.data.data.length > 0) {
// 更新搜索结果
this.setData({
searchResults: res.data.data
});
} else {
wx.showToast({
title: '未找到相关地址',
icon: 'none'
});
}
},
fail: (err) => {
console.error('搜索地址失败', err);
wx.showToast({
title: '搜索失败,请重试',
icon: 'none'
});
}
});
},
// 选择搜索结果
onSelectSearchResult(e) {
const item = e.currentTarget.dataset.item;
const { lat, lng } = item.location;
// 更新地图位置
this.setData({
mapLatitude: lat,
mapLongitude: lng,
fullScreenMarkers: [{
id: 1,
latitude: lat,
longitude: lng,
title: item.title,
iconPath: '/images/location-marker.png',
width: 30,
height: 30
}],
searchResults: [], // 清空搜索结果
searchKeyword: item.title // 更新搜索框内容
});
// 获取地址信息
this.getAddressInfo(lat, lng, true);
},
onTitleInput(e) {
this.setData({
'form.title': e.detail.value
})
},
onStartDateInput(e) {
this.setData({
'form.startDate': e.detail.value
})
},
onEndDateInput(e) {
this.setData({
'form.endDate': e.detail.value
})
},
onAddGroupList(e) {
if (e.detail.length > 0) {
let _userIds = "";
let _userNames = "";
let groups = [];
e.detail.forEach(it => {
let item = this.data.groupList.find(item => item.id == it.userId);
groups.push(item);
_userIds += "," + item.id;
_userNames += "," + item.userName //+ `[${item.subDeptName}]`;
});
this.setData({
"form.subGroup": groups,
"form.groupIds": _userIds.substring(1),
"form.groupNames": _userNames.substring(1)
})
} else {
this.setData({
"form.subGroup": [],
"form.groupIds": "",
"form.groupNames": ""
})
}
},
onRangeChange(e) {
// 正确处理步进器的值变化
this.setData({
'form.range': e.detail.value
})
},
//项目切换 返回值
onProjectSelect(e) {
let projectId = e.detail.id;
let projectName = e.detail.text;
app.globalData.useProjectId = projectId;
app.globalData.useProjectName = projectName;
this.onLoad();
},
doBack(isRefresh) {
/*返回列表页面并刷新*/
if (isRefresh) {
wx.navigateBack({
delta: 1
});
} else {
wx.redirectTo({
url: "../list/index",
})
}
},
returnToPage: function () {
this.doBack(false);
},
submitSave() {
}
});

View File

@ -0,0 +1,9 @@
{
"usingComponents": {
"van-stepper": "@vant/weapp/stepper",
"custom-stepper": "/components/custom-stepper/index"
},
"navigationStyle": "custom",
"styleIsolation": "apply-shared",
"backgroundColor": "#191d28"
}

View File

@ -0,0 +1,124 @@
<wxs module="format" src="/utils/format.wxs"></wxs>
<view class="header_title">
<view class="header_title_row">
<van-row>
<van-col span="4">
<view class="header_img" bindtap="returnToPage">
<image src="/images/left.png"></image>
</view>
</van-col>
<van-col span="15">
<view class="header_name">创建移动考勤</view>
</van-col>
</van-row>
</view>
</view>
<scroll-view class="max_content_scroll" type="list" scroll-y>
<project-select init="{{ initData }}" bindchange="onProjectSelect" id="projectSel"></project-select>
<view class="inspect_info">
<view class="inspect_info_list">
<view class="markers inspect_info_title">考勤标题</view>
<input class="add_input" placeholder="请输入考勤标题" placeholder-style="color: #999999;"
placeholder-class="placeholder_class" value="{{ form.title }}" bindinput="onTitleInput" />
</view>
<view class="inspect_info_list">
<view class="markers inspect_info_title">开始时间</view>
<view class="inspect_info_content">
<voucher-date counts="5" placeholder="请选择开始时间" time="{{form.startDate}}" minDate="{{ minDate }}"
maxDate="{{ maxDate||form.endDate }}" bindchange="onStartDateInput"></voucher-date>
</view>
</view>
<view class="inspect_info_list">
<view class="markers inspect_info_title">结束时间</view>
<view class="inspect_info_content">
<voucher-date counts="5" wx:key="form.startDate" placeholder="请选择结束时间" time="{{form.endDate}}"
minDate="{{ form.startDate||minDate }}" maxDate="{{ maxDate }}" bindchange="onEndDateInput"></voucher-date>
</view>
</view>
<view class="inspect_info_list">
<view class="markers inspect_info_title">指定考勤地点</view>
<view class="inspect_info_content">
<view class="selected-location" bindtap="showFullScreenMap">
<text>{{form.address||'点击地图选择具体位置'}}</text>
</view>
</view>
</view>
<view class="inspect_info_list">
<view class="markers inspect_info_title">考勤范围(范围≤{{form.range||50}}米) </view>
<view class="inspect_info_content">
<custom-stepper value="{{form.range}}" min="10" max="5000" step="1" bindchange="onRangeChange" />
</view>
</view>
<view class=" inspect_info_list">
<view class="markers inspect_info_title">指定考勤班组</view>
<view class="inspect_info_content">
<select-group-person rectifierData="{{subGroupList}}" multiple="{{true}}" bindselected="onAddGroupList"
index="3" :title="{{form.groupNames?form.groupNames:'请选择考勤班组'}}" choose="{{form.groupNames}}">
</select-group-person>
</view>
<view wx:if="{{form.subGroup && form.subGroup.length>0}}">
<view wx:for="{{form.subGroup}}" wx:key="item.id">
{{item.groupName}} <view class="dept-name"> [{{item.subDeptName}}]</view>
</view>
</view>
</view>
<view class="problem_submit_to">
<view class="problem_submit_to_btn" bindtap="returnToPage">取消</view>
<view class="problem_submit_to_btn problem_submit_to_save" bindtap="submitSave">保存</view>
</view>
</view>
</scroll-view>
<!-- 全屏地图选择器 -->
<view class="full-screen-map" wx:if="{{showFullScreenMap}}">
<view class="map-header">
<view class="back-btn" bindtap="hideFullScreenMap">
<image src="/images/left.png"></image>
</view>
<view class="map-title">选择考勤地点</view>
<!-- 右侧留空占位 -->
<view style="width: 30px;"></view>
</view>
<!-- 搜索框 -->
<view class="search-container">
<input class="search-input" placeholder="请输入地址搜索" placeholder-style="color: #999999;" value="{{searchKeyword}}"
bindinput="onSearchInput" bindconfirm="onSearchConfirm" />
<view class="search-btn" bindtap="onSearchConfirm">
<text>搜索</text>
</view>
</view>
<!-- 搜索结果列表 -->
<view class="search-results" wx:if="{{searchResults.length > 0}}">
<view class="search-result-item" wx:for="{{searchResults}}" wx:key="index" bindtap="onSelectSearchResult"
data-item="{{item}}">
<view class="search-result-title">{{item.title}}</view>
<view class="search-result-address">{{item.address}}</view>
</view>
</view>
<map class="full-map" id="fullMap" longitude="{{mapLongitude}}" latitude="{{mapLatitude}}" scale="16"
markers="{{fullScreenMarkers}}" bindtap="onFullScreenMapTap" show-location>
</map>
<view class="confirm-bottom-btn" bindtap="confirmLocation">
<text>确定</text>
</view>
<view class="location-info">
<view class="location-address">{{selectedAddress || '获取地址中...'}}</view>
</view>
</view>

View File

@ -0,0 +1,223 @@
/* pageage/mobile_attendance/attendance_config/add/index.wxss */
.map-hint {
color: #1989fa;
font-size: 28rpx;
padding: 20rpx 0;
text-align: center;
background-color: #2d323c; /* 深色背景 */
border-radius: 8rpx;
margin-bottom: 20rpx;
border: 2rpx solid #3d424d;
}
.map-container {
height: 400rpx;
border: 2rpx solid #3d424d;
border-radius: 16rpx;
overflow: hidden;
background-color: #2d323c; /* 深色背景 */
}
.map {
width: 100%;
height: 100%;
}
.selected-location {
padding: 30rpx;
background-color: #212737; /* 深色背景 */
border-radius: 10rpx;
margin-bottom: 20rpx;
font-size: 28rpx;
color: #fff; /* 浅色文字 */
text-align: left;
}
/* 全屏地图样式 */
.full-screen-map {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 9999;
background-color: #191d28; /* 使用项目主色调 */
}
.map-header {
display: flex;
justify-content: space-between;
align-items: center;
height: 100rpx;
padding: 0 30rpx;
background-color: #191d28; /* 使用项目主色调 */
box-shadow: 0 4rpx 20rpx rgba(0,0,0,0.3);
position: fixed;
top: 75rpx;
left: 0;
right: 0;
z-index: 1001;
box-sizing: border-box;
border-bottom: 2rpx solid #2d323c;
}
.back-btn {
width: 60rpx;
height: 60rpx;
padding: 10rpx;
display: flex;
align-items: center;
justify-content: center;
}
.back-btn image {
width: 30rpx;
height: 30rpx;
}
.map-title {
font-size: 32rpx;
font-weight: bold;
color: #ffffff; /* 白色文字 */
flex: 1;
text-align: center;
}
/* 搜索框样式 */
.search-container {
position: fixed;
top: 160rpx;
left: 0;
right: 0;
padding: 20rpx 30rpx;
background-color: #191d28;
z-index: 1001;
display: flex;
box-sizing: border-box;
border-bottom: 2rpx solid #2d323c;
}
.search-input {
flex: 1;
height: 72rpx;
padding: 0 24rpx;
background-color: #2d323c;
border: 2rpx solid #3d424d;
border-radius: 8rpx;
color: #ffffff;
font-size: 28rpx;
margin-right: 20rpx;
}
.search-input::placeholder {
color: #999999;
}
.search-btn {
width: 120rpx;
height: 72rpx;
background-color: #1989fa;
border-radius: 8rpx;
display: flex;
align-items: center;
justify-content: center;
color: #ffffff;
font-size: 28rpx;
}
.dept-name{
display: inline-block;
color:orange;
}
.search-btn:active {
background-color: #0c7ae6;
}
.full-map {
width: 100%;
height: 100%;
margin-top: 194rpx;
box-sizing: border-box;
}
.location-info {
position: fixed;
bottom: 160rpx;
left: 40rpx;
right: 40rpx;
background-color: rgba(40, 44, 52, 0.95); /* 深色背景 */
padding: 30rpx;
border-radius: 16rpx;
box-shadow: 0 4rpx 24rpx rgba(0,0,0,0.3);
z-index: 1000;
backdrop-filter: blur(20rpx);
box-sizing: border-box;
border: 2rpx solid #2d323c;
}
.location-address {
font-size: 28rpx;
color: #e0e0e0; /* 浅灰色文字 */
word-wrap: break-word;
}
.confirm-bottom-btn {
position: fixed;
bottom: 40rpx;
left: 40rpx;
right: 40rpx;
background-color: #1989fa; /* 保持蓝色按钮 */
color: white;
border-radius: 16rpx;
font-size: 32rpx;
text-align: center;
padding: 30rpx;
z-index: 1000;
box-shadow: 0 8rpx 24rpx rgba(25, 137, 250, 0.3);
box-sizing: border-box;
}
.confirm-bottom-btn:active {
background-color: #0c7ae6;
}
/* 搜索结果列表样式 */
.search-results {
position: fixed;
top: 270rpx;
left: 0;
right: 0;
max-height: 90%;
background-color: #2d323c;
z-index: 1002;
overflow-y: auto;
box-sizing: border-box;
border-bottom: 2rpx solid #3d424d;
height:calc(100vh - 270rpx);
}
.search-result-item {
padding: 30rpx;
border-bottom: 2rpx solid #3d424d;
color: #e0e0e0;
font-size: 28rpx;
}
.search-result-item:last-child {
border-bottom: none;
}
.search-result-item:active {
background-color: #3d424d;
}
.search-result-title {
font-weight: bold;
margin-bottom: 10rpx;
}
.search-result-address {
color: #999999;
font-size: 24rpx;
}

View File

@ -0,0 +1,135 @@
// pageage/mobile_attendance/attendance_config/index.js
import config from "../../../../config.js";
import fmt from "../../../utils/date.js";
import { getToken, getUserInfo } from "../../../../utils/auth.js";
import { uploadFiles } from "../../../utils/upload.js";
import { tryToJson } from '../../../utils/tools.js'
import {
getProjectChecked,
findPlanDatas,
listProProjectInfoSubdeptsUsers,
updateProjectChecked,
addProjectChecked,
} from "../../../../api/project.js";
const app = getApp();
Page({
/**
* 页面的初始数据
*/
data: {
projectUserInfo: {},
projectUserInfo: {},
projectId: "",
projectName: "",
initData: {},
listData: [],
counts: [0, 0],
activeState: 'vaild',
pageNum: 1,
pageSize: 10,
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
if (!getToken()) {
wx.redirectTo({
url: "../../../pages/login/login",
});
}
const proUserInfo = getUserInfo();
this.setData({
projectUserInfo: proUserInfo.projectUserInfo,
projectId: app.globalData.useProjectId,
projectName: app.globalData.useProjectName,
initData: {
id: app.globalData.useProjectId,
text: app.globalData.useProjectName,
},
});
},
switchTabJump(e) {
let index = e.currentTarget.dataset.index;
let nav = "";
if (index == 1) {
nav = 'vaild';
} else {
nav = 'invalid';
}
if (nav != this.data.activeState) {
this.setData({
activeState: nav,
pageNum: 1,
pageSize: 10,
listData: [],
});
//this.getListData();
}
},
/**
* 增加
*/
doAddCfg() {
wx.redirectTo({
url: '../add/index',
});
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() { },
/**
* 生命周期函数--监听页面显示
*/
onShow() { },
/**
* 生命周期函数--监听页面隐藏
*/
onHide() { },
/**
* 生命周期函数--监听页面卸载
*/
onUnload() { },
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() { },
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() { },
/**
* 用户点击右上角分享
*/
onShareAppMessage() { },
//项目切换 返回值
onProjectSelect(e) {
let projectId = e.detail.id;
let projectName = e.detail.text;
app.globalData.useProjectId = projectId;
app.globalData.useProjectName = projectName;
this.onLoad();
},
returnToPage() {
/*关闭当前页面,跳转到其它页面。*/
if (wx.getStorageSync('nav-menu') == "xmgl") {
wx.redirectTo({
url: '../../../project_more/index',
})
} else {
wx.redirectTo({
url: '../../../project_quality/index',
})
}
},
});

View File

@ -0,0 +1,6 @@
{
"usingComponents": {},
"navigationStyle": "custom",
"styleIsolation": "apply-shared",
"backgroundColor": "#191d28"
}

View File

@ -0,0 +1,46 @@
<wxs module="format" src="/utils/format.wxs"></wxs>
<view class="header_title">
<view class="header_title_row">
<van-row>
<van-col span="4">
<view class="header_img" bindtap="returnToPage">
<image src="/images/left.png"></image>
</view>
</van-col>
<van-col span="15">
<view class="header_name">移动考勤管理</view>
</van-col>
</van-row>
</view>
</view>
<scroll-view class="max_content_scroll" type="list" scroll-y>
<project-select init="{{ initData }}" bindchange="onProjectSelect" id="projectSel"></project-select>
<view class="modify_video_nav" style="margin-top: 5rpx;">
<view class="{{activeState=='vaild'?'active':''}}" bindtap="switchTabJump" data-index="1">
<text>生效中({{counts[0]}})</text>
</view>
<view class="{{activeState=='invalid'?'active':''}}" bindtap="switchTabJump" data-index="2">
<text>已过期({{counts[1]}})</text>
</view>
</view>
<view wx:if="{{listData.length==0}}">
<view style="padding-top: 70px;text-align: -webkit-center;">
<image src="https://szgcwx.jhncidg.com/staticFiles/nodata.png" style="width: 130px;height: 105px;"></image>
<view style="color: #a5abbb;">暂无数据</view>
</view>
</view>
<view class="inspect_add_to" bindtap="doAddCfg">
<view style="padding-top: 22rpx">
<image src="/images/new_add.png"></image>
<view>新增</view>
</view>
</view>
</scroll-view>

View File

@ -0,0 +1 @@
/* pageage/mobile_attendance/attendance_config/index.wxss */

View File

@ -138,6 +138,7 @@ Page({
copySendList: res.data.copySendList,
checkUserList: res.data.checkUserList,
});
console.log("--->", res.data)
}
});
},

View File

@ -0,0 +1,32 @@
Page({
data: {
value1: 0,
value2: 0,
value3: 5,
value4: 3
},
onChange1(e) {
this.setData({
value1: e.detail.value
});
},
onChange2(e) {
this.setData({
value2: e.detail.value
});
},
onChange3(e) {
this.setData({
value3: e.detail.value
});
},
onChange4(e) {
this.setData({
value4: e.detail.value
});
}
});

View File

@ -0,0 +1,6 @@
{
"usingComponents": {
"custom-stepper": "/components/custom-stepper/index"
},
"navigationBarTitleText": "自定义步进器测试"
}

View File

@ -0,0 +1,25 @@
<view class="container">
<view class="stepper-demo">
<view class="demo-title">基本用法</view>
<custom-stepper value="{{ value1 }}" bindchange="onChange1" />
<text class="current-value">当前值:{{ value1 }}</text>
</view>
<view class="stepper-demo">
<view class="demo-title">设置步长</view>
<custom-stepper value="{{ value2 }}" step="2" bindchange="onChange2" />
<text class="current-value">当前值:{{ value2 }}</text>
</view>
<view class="stepper-demo">
<view class="demo-title">设置范围</view>
<custom-stepper value="{{ value3 }}" min="0" max="10" bindchange="onChange3" />
<text class="current-value">当前值:{{ value3 }}</text>
</view>
<view class="stepper-demo">
<view class="demo-title">禁用状态</view>
<custom-stepper value="{{ value4 }}" disabled bindchange="onChange4" />
<text class="current-value">当前值:{{ value4 }}</text>
</view>
</view>

View File

@ -0,0 +1,20 @@
.container {
padding: 20px;
}
.stepper-demo {
margin-bottom: 30px;
}
.demo-title {
font-size: 16px;
font-weight: bold;
margin-bottom: 10px;
}
.current-value {
display: block;
margin-top: 10px;
font-size: 14px;
color: #666;
}

View File

@ -1,43 +1,43 @@
{
"appid": "wx46466c7828eede2b",
"compileType": "miniprogram",
"libVersion": "3.8.9",
"packOptions": {
"ignore": [],
"include": []
},
"setting": {
"coverView": true,
"es6": true,
"postcss": true,
"minified": true,
"enhance": true,
"showShadowRootInWxmlPanel": true,
"packNpmRelationList": [],
"babelSetting": {
"ignore": [],
"disablePlugins": [],
"outputPath": ""
"appid": "wx46466c7828eede2b",
"compileType": "miniprogram",
"libVersion": "3.8.9",
"packOptions": {
"ignore": [],
"include": []
},
"ignoreDevUnusedFiles": false,
"ignoreUploadUnusedFiles": false,
"condition": false,
"compileWorklet": false,
"uglifyFileName": false,
"uploadWithSourceMap": true,
"packNpmManually": false,
"minifyWXSS": true,
"minifyWXML": true,
"localPlugins": false,
"disableUseStrict": false,
"useCompilerPlugins": false,
"swc": false,
"disableSWC": true
},
"condition": {},
"editorSetting": {
"tabIndent": "auto",
"tabSize": 4
},
"simulatorPluginLibVersion": {}
"setting": {
"coverView": true,
"es6": true,
"postcss": true,
"minified": true,
"enhance": true,
"showShadowRootInWxmlPanel": true,
"packNpmRelationList": [],
"babelSetting": {
"ignore": [],
"disablePlugins": [],
"outputPath": ""
},
"ignoreDevUnusedFiles": false,
"ignoreUploadUnusedFiles": false,
"condition": false,
"compileWorklet": false,
"uglifyFileName": false,
"uploadWithSourceMap": true,
"packNpmManually": false,
"minifyWXSS": true,
"minifyWXML": true,
"localPlugins": false,
"disableUseStrict": false,
"useCompilerPlugins": false,
"swc": false,
"disableSWC": true
},
"condition": {},
"editorSetting": {
"tabIndent": "auto",
"tabSize": 4
},
"simulatorPluginLibVersion": {}
}

View File

@ -220,8 +220,28 @@ export function securitySignFileUpload(file) {
}
export function request(options) {
if (options.method == 'get' && options.params) {
options.url = options.url + "?" + options.params;
// 处理GET请求的params参数
if ((options.method === 'get' || options.method === 'GET') && options.params) {
let paramsStr = '';
if (typeof options.params === 'object' && !Array.isArray(options.params)) {
// 如果params是对象将其转换为查询字符串
const queryParts = [];
Object.keys(options.params).forEach(key => {
if (options.params[key] !== null && options.params[key] !== undefined) {
const encodedKey = encodeURIComponent(key);
const encodedValue = encodeURIComponent(String(options.params[key]));
queryParts.push(encodedKey + '=' + encodedValue);
}
});
paramsStr = queryParts.join('&');
} else {
// 如果params是字符串直接使用
paramsStr = options.params;
}
if (paramsStr) {
options.url += (options.url.includes('?') ? '&' : '?') + paramsStr;
}
}
return doRequest(options.url, options.method, options.data, options.header)
}

View File

@ -11,7 +11,7 @@
<script>
window.yanzhuAppTitle = "数字建安施工";
</script>
<link rel="icon" href="/cdn/bsimages/faviconnewfaviconnew.ico?v=4" />
<link rel="icon" href="/cdn/bsimages/faviconnew.ico?v=4" />
<title>数字建安施工</title>
<script
type="text/javascript"