miaofuhao 1 maand geleden
bovenliggende
commit
d0f9030aeb

+ 1 - 1
web/.env.development

@@ -2,7 +2,7 @@ ENV = 'development'
2 2
 
3 3
 # 接口地址104  110 http://222.143.135.71:29092 http://127.0.0.1:9090http://215y970n20.iask.in:15091 http://36.133.40.34:9091    
4 4
 # VUE_APP_BASE_API  = 'http://192.168.1.15:9090/'    http://39.164.159.226:9090/    http://zhyh.hnyulin.com/api/
5
-VUE_APP_BASE_API  = http://zhyh.hnyulin.com/api/
5
+VUE_APP_BASE_API  = http://192.168.1.15:9090/
6 6
 VUE_APP_WS_API = 'http://222.143.133.238:29092'
7 7
 
8 8
 # VUE_APP_BASE_API  = 'http://127.0.0.1:9090'

+ 4 - 3
web/.env.production

@@ -1,7 +1,8 @@
1 1
 ENV = 'production'
2 2
 
3 3
 # 如果使用 Nginx 代理后端接口,那么此处需要改为 '/',文件查看 Docker 部署篇,Nginx 配置
4
-# 接口地址,注意协议,如果你没有配置 ssl,需要将 https 改为 http  http://222.143.135.71:29092
5
-VUE_APP_BASE_API  = 'http://zhyh.hnyulin.com/api/'
4
+# 接口地址,注意协议,如果你没有配置 ssl,需要将 https 改为 http  http://222.143.135.71:29092   http://192.168.1.15:9090/
5
+# VUE_APP_BASE_API  = 'http://zhyh.hnyulin.com/api/'
6
+VUE_APP_BASE_API = http://192.168.1.15:9090/
6 7
 # 如果接口是 http 形式, wss 需要改为 ws
7
-VUE_APP_WS_API = 'http://zhyh.hnyulin.com/api'
8
+VUE_APP_WS_API = 'http://192.168.1.15:9090/'

+ 2 - 2
wxproject/app.js

@@ -5,8 +5,8 @@ App({
5 5
   globalData: {
6 6
     userInfo: null,
7 7
     httpsUrl: 'https://cgdl.yutingcn.com/',
8
-    // httpsUrlServer: 'http://39.164.159.226:9090', //个人接口地址
9
-    httpsUrlServer:'http://zhyh.hnyulin.com/api/',
8
+    httpsUrlServer: 'http://39.164.159.226:9090', //个人接口地址
9
+    // httpsUrlServer:'http://zhyh.hnyulin.com/api/',
10 10
     zdzUrlServer:'http://58.246.189.90:30000', //市指导站推送测试接口地址
11 11
     openId: '',
12 12
     token: '',

+ 273 - 0
wxproject/packageA/pages/onDuty/onDuty.js

@@ -51,6 +51,22 @@ Page({
51 51
     // 判断是上班打卡还是下班打卡(true为上班打卡,false为下班打卡)
52 52
     startOrenDFlag: 0,
53 53
     showBtnFlag: true, // 判断是否是中心人员(是否需要打卡)
54
+    // 批量操作相关
55
+    showBatchModal: false, // 是否显示批量操作弹窗
56
+    batchSignType: 0, // 批量操作类型:0-签到,1-签退
57
+    batchModalTitle: '', // 弹窗标题
58
+    batchModalButtonText: '', // 弹窗按钮文本
59
+    // 部门和人员选择
60
+    departments: [], // 部门列表
61
+    departmentNames: [], // 部门名称列表
62
+    selectedDepartmentIndex: '', // 选中的部门索引
63
+    selectedDepartmentId: '', // 选中的部门ID
64
+    users: [], // 人员列表
65
+    userList: [], // 人员名称列表
66
+    selectedUserId: [], // 选中的人员ID数组
67
+    selectedUserNames: [], // 选中的人员姓名数组
68
+    // 权限控制
69
+    buttonNodes: [], // 按钮权限节点
54 70
   },
55 71
   //页面跳转
56 72
   nav(e) {
@@ -60,6 +76,249 @@ Page({
60 76
       url: url,
61 77
     });
62 78
   },
79
+  // 显示批量操作弹窗
80
+  showBatchModal(e) {
81
+    this.setData({
82
+      showBatchModal: true,
83
+      batchSignType: 0, // 默认批量上班签到
84
+      batchModalTitle: '批量上班签到',
85
+      batchModalButtonText: '签到',
86
+      selectedDepartmentIndex: '',
87
+      selectedDepartmentId: '',
88
+      selectedUserId: [],
89
+      selectedUserNames: [],
90
+      users: [],
91
+      userList: []
92
+    });
93
+    // 获取部门列表
94
+    this.getDepartments();
95
+  },
96
+  // 批量签到类型选择事件
97
+  bindBatchTypeChange(e) {
98
+    const type = parseInt(e.detail.value); // 将字符串转换为数字
99
+    this.setData({
100
+      batchSignType: type,
101
+      batchModalTitle: type === 0 ? '批量上班签到' : '批量下班签到',
102
+      batchModalButtonText: type === 0 ? '签到' : '签退'
103
+    });
104
+  },
105
+  // 隐藏批量操作弹窗
106
+  hideBatchModal() {
107
+    this.setData({
108
+      showBatchModal: false
109
+    });
110
+  },
111
+  // 获取部门列表
112
+  getDepartments() {
113
+    var that = this;
114
+    var token = wx.getStorageSync('token');
115
+    
116
+    wx.showLoading({
117
+      title: '加载部门中...',
118
+      mask: true
119
+    });
120
+    
121
+    wx.request({
122
+      url: app.globalData.httpsUrlServer + '/sysdepartment/selectDepartmentByPageListpara?stateid=2',
123
+      method: 'post',
124
+      data: {data: {
125
+        current: 1,
126
+        fDeptname: "",
127
+        size: 1000
128
+      }},
129
+      dataType: 'json',
130
+      header: {
131
+        'Content-Type': 'application/json;charset=UTF-8',
132
+        token: token,
133
+        fSource: 1,
134
+      },
135
+      success: function (res) {
136
+        wx.hideLoading();
137
+        if (res.statusCode == 200 && res.data.status == 200) {
138
+          console.log('部门列表:', res.data.data.data);
139
+          var departments = res.data.data.data || [];
140
+          // 注意:work_order_info中的字段名是fDeptname和fDeptid,需要检查实际API返回的字段名
141
+          var formattedDepartments = departments.map(dept => ({
142
+            fDepartmentid: dept.fDeptid || dept.fDepartmentid,
143
+            fDepartmentname: dept.fDeptname || dept.fDepartmentname
144
+          }));
145
+          var departmentNames = formattedDepartments.map(dept => dept.fDepartmentname);
146
+          that.setData({
147
+            departments: formattedDepartments,
148
+            departmentNames: departmentNames
149
+          });
150
+        }
151
+      },
152
+      fail: function (err) {
153
+        wx.hideLoading();
154
+        console.error('获取部门列表失败:', err);
155
+        wx.showToast({
156
+          title: '获取部门失败',
157
+          icon: 'none'
158
+        });
159
+      }
160
+    });
161
+  },
162
+  // 获取人员列表
163
+  getUsers(departmentId) {
164
+    if (!departmentId) return;
165
+    
166
+    var that = this;
167
+    var token = wx.getStorageSync('token');
168
+    
169
+    wx.showLoading({
170
+      title: '加载人员中...',
171
+      mask: true
172
+    });
173
+    
174
+    wx.request({
175
+      url: app.globalData.httpsUrlServer + '/sysuseraccount/selectUserAccountAppByPageList',
176
+      method: 'post',
177
+      data: {data: {
178
+        current: 1,
179
+        size: 9999,
180
+        fDeptid: departmentId
181
+      }},
182
+      dataType: 'json',
183
+      header: {
184
+        'Content-Type': 'application/json;charset=UTF-8',
185
+        token: token,
186
+        fSource: 1,
187
+      },
188
+      success: function (res) {
189
+        wx.hideLoading();
190
+        if (res.statusCode == 200 && res.data.status == 200) {
191
+          var users = res.data.data.data || [];
192
+          console.log('人员列表:', users);
193
+          // 注意:需要检查实际API返回的字段名是否与预期一致  
194
+          var formattedUsers = users.map(user => ({
195
+            fExpandint1: user.fUserid || user.fExpandint1,
196
+            fRealname: user.fUsername || user.fRealname,
197
+            fDepartmentid: user.fDeptid || user.fDepartmentid
198
+          }));
199
+          console.log('格式化后的人员列表:', formattedUsers);
200
+          that.setData({
201
+            users: formattedUsers,
202
+            userList: formattedUsers
203
+          });
204
+        }
205
+      },
206
+      fail: function (err) {
207
+        wx.hideLoading();
208
+        console.error('获取人员列表失败:', err);
209
+        wx.showToast({
210
+          title: '获取人员失败',
211
+          icon: 'none'
212
+        });
213
+      }
214
+    });
215
+  },
216
+  // 部门选择事件
217
+  bindDepartmentChange(e) {
218
+    const index = e.detail.value;
219
+    const departmentId = this.data.departments[index].fDepartmentid;
220
+    this.setData({
221
+      selectedDepartmentIndex: index,
222
+      selectedDepartmentId: departmentId,
223
+      // 重置人员选择
224
+      selectedUserId: [],
225
+      selectedUserNames: []
226
+    });
227
+    // 获取该部门下的人员列表
228
+    this.getUsers(departmentId);
229
+  },
230
+  // 人员选择事件
231
+  bindUserCheckboxChange(e) {
232
+    // 注意:e.detail.value返回的是字符串数组
233
+    console.log('选中的用户ID字符串数组:', e.detail.value);
234
+    const selectedUserIdsStr = e.detail.value;
235
+    // 根据用户ID字符串找到对应的用户信息
236
+    const selectedUsers = this.data.users.filter(user => 
237
+      selectedUserIdsStr.includes(user.fExpandint1.toString())
238
+    );
239
+    console.log('选中的用户信息:', selectedUsers);
240
+    const selectedUserIds = selectedUsers.map(user => user.fExpandint1);
241
+    const selectedUserNames = selectedUsers.map(user => user.fRealname);
242
+    console.log('选中的用户ID数组:', selectedUserIds);
243
+    console.log('选中的用户姓名数组:', selectedUserNames);
244
+    this.setData({
245
+      selectedUserId: selectedUserIds,
246
+      selectedUserNames: selectedUserNames
247
+    });
248
+  },
249
+  // 确认批量签到/签退
250
+  confirmBatchSign() {
251
+    const that = this;
252
+    const { batchSignType, selectedUserId, selectedUserNames, markers, posObj } = this.data;
253
+    
254
+    if (selectedUserId.length === 0) {
255
+      wx.showToast({
256
+        title: '请选择人员',
257
+        icon: 'none',
258
+        duration: 2000
259
+      });
260
+      return;
261
+    }
262
+    
263
+    wx.showLoading({
264
+      title: '处理中...',
265
+      mask: true
266
+    });
267
+    
268
+    const timeStamp = new Date().getTime();
269
+    // 构造请求数据 - 根据selectedUserId的长度创建数组
270
+    const data = [];
271
+    const positionName = markers[0].name;
272
+    const latitude = posObj.latitude;
273
+    const longitude = posObj.longitude;
274
+    
275
+    // 遍历selectedUserId数组,为每个用户创建一个数据对象
276
+    for (let i = 0; i < selectedUserId.length; i++) {
277
+      data.push({
278
+        fExpand1: positionName, // 当前位置名称
279
+        fWorktime: timeStamp, // 当前时间戳
280
+        fX: latitude, // 当前纬度
281
+        fY: longitude, // 当前经度
282
+        fExpandint1: selectedUserId[i], // 单个用户ID
283
+        fRealname: selectedUserNames[i] // 单个用户姓名
284
+      });
285
+    }
286
+    
287
+    // 根据操作类型选择API接口
288
+    const url = batchSignType === 0 
289
+      ? app.globalData.httpsUrlServer + '/tbaseemployeeworkrecords/batchSignIn'
290
+      : app.globalData.httpsUrlServer + '/tbaseemployeeworkrecords/batchSignOut';
291
+    
292
+    // 调用API
293
+    console.log(url, data)
294
+
295
+    app.postReq(url, data, (res) => {
296
+      wx.hideLoading();
297
+      if (res.message === '请求成功') {
298
+        wx.showToast({
299
+          title: batchSignType === 0 ? '批量签到成功' : '批量签退成功',
300
+          icon: 'success',
301
+          duration: 2000
302
+        });
303
+        // 隐藏弹窗
304
+        that.hideBatchModal();
305
+      } else {
306
+        wx.showToast({
307
+          title: res.message || '操作失败',
308
+          icon: 'none',
309
+          duration: 2000
310
+        });
311
+      }
312
+    }, (err) => {
313
+      wx.hideLoading();
314
+      wx.showToast({
315
+        title: '网络错误',
316
+        icon: 'none',
317
+        duration: 2000
318
+      });
319
+      console.error('批量操作失败:', err);
320
+    });
321
+  },
63 322
   // 签到事件
64 323
   openSetting() {
65 324
     var that = this;
@@ -241,9 +500,23 @@ Page({
241 500
     that.getLocation();
242 501
     // 查询当前登录人员是否有上班打卡记录
243 502
     that.getSelectCurrentUserIsSgin();
503
+    // 获取权限控制按钮节点
504
+    this.getButtonNodes();
244 505
 
245 506
     wx.hideLoading();
246 507
   },
508
+  // 获取权限控制按钮节点
509
+  getButtonNodes() {
510
+    // 获取缓存中的按钮节点数据
511
+    const buttonNodes = wx.getStorageSync('buttonNodes') || [];
512
+    // 筛选出与考勤相关的数据并提取fFunctioncode
513
+    const attendanceButtons = buttonNodes
514
+      .filter(node => node.parentFFunctioncode === 'attendanceManagement' && node.fFunctioncode)
515
+      .map(node => node.fFunctioncode);
516
+    this.setData({
517
+      buttonNodes: attendanceButtons
518
+    });
519
+  },
247 520
 
248 521
   /**
249 522
    * 生命周期函数--监听页面初次渲染完成

+ 49 - 0
wxproject/packageA/pages/onDuty/onDuty.wxml

@@ -19,8 +19,57 @@
19 19
 			<view class="signIn">
20 20
 				<button class="btn" bindtap="openSetting" wx:if="{{startOrenDFlag == 0}}">上班签到</button>
21 21
 				<button class="btn" bindtap="openSetting" wx:elif="{{startOrenDFlag == 1}}">下班签到</button>
22
+				<button class="btn" bindtap="showBatchModal" wx:if="{{startOrenDFlag == 0 || startOrenDFlag == 1}}">批量签到</button>
22 23
 				<button class="btn_all"  wx:elif="{{startOrenDFlag == 2}}">打卡已完成</button>
23 24
 			</view>
25
+
26
+			<!-- 批量签到/签退弹出层 -->
27
+			<view class="batch-modal" wx:if="{{showBatchModal}}">
28
+				<view class="batch-modal-content">
29
+					<view class="batch-modal-header">
30
+						<view class="batch-modal-title">{{batchModalTitle}}</view>
31
+						<view class="batch-modal-close" bindtap="hideBatchModal">×</view>
32
+					</view>
33
+					<view class="batch-modal-body">
34
+						<!-- 签到类型选择 -->
35
+						<view class="form-item">
36
+							<view class="form-label">选择类型:</view>
37
+							<radio-group class="radio-group" bindchange="bindBatchTypeChange" style="width:80%">
38
+								<radio class="radio-item" value="0" checked="{{batchSignType === 0}}">批量上班签到</radio>
39
+								<radio class="radio-item" value="1" checked="{{batchSignType === 1}}">批量下班签到</radio>
40
+							</radio-group>
41
+						</view>
42
+						<!-- 部门选择 -->
43
+						<view class="form-item">
44
+							<view class="form-label">选择部门:</view>
45
+							<picker value="{{selectedDepartmentIndex}}" range="{{departmentNames}}" style="width:80%" bindchange="bindDepartmentChange">
46
+								{{departmentNames.length > 0 && selectedDepartmentIndex !== '' ? departmentNames[selectedDepartmentIndex] : '请选择部门'}}
47
+							</picker>
48
+						</view>
49
+						<!-- 人员选择 -->
50
+						<view class="form-item">
51
+							<text class="form-label">人员选择</text>
52
+							<view wx:if="{{userList.length}}" class="form-value">
53
+								<view class="checkbox-group-wrapper">
54
+									<checkbox-group bindchange="bindUserCheckboxChange">
55
+										<view wx:for="{{userList}}" wx:key="item.fExpandint1" class="checkbox-item">
56
+											 <!-- checked="{{selectedUserId.indexOf(parseInt(item.fExpandint1)) !== -1}}" -->
57
+											<checkbox value="{{item.fExpandint1}}" />
58
+											<text>{{item.fRealname}}</text>
59
+										</view>
60
+									</checkbox-group>
61
+								</view>
62
+								<view class="selected-count">已选择{{selectedUserId.length}}人</view>
63
+							</view>
64
+							<view wx:else class="form-value placeholder">请选择部门</view>
65
+						</view>
66
+					</view>
67
+					<view class="batch-modal-footer">
68
+						<button class="cancel-btn" bindtap="hideBatchModal">取消</button>
69
+						<button class="confirm-btn" bindtap="confirmBatchSign" disabled="{{!selectedUserId || selectedUserId.length === 0}}">确认{{batchModalButtonText}}</button>
70
+					</view>
71
+				</view>
72
+			</view>
24 73
 		</view>
25 74
 
26 75
 	</view>

+ 196 - 16
wxproject/packageA/pages/onDuty/onDuty.wxss

@@ -15,7 +15,10 @@ page {
15 15
   background: -webkit-linear-gradient(top, #3573EA, white);
16 16
   background: -moz-linear-gradient(top, #3573EA, white);
17 17
 }
18
-
18
+.mapSize {
19
+  width: 100%;
20
+  height: 60vh;
21
+}
19 22
 .t_red {
20 23
   color: red;
21 24
 }
@@ -38,13 +41,41 @@ page {
38 41
 
39 42
 .signIn .btn {
40 43
   width: 300rpx;
41
-  /* padding: 5rpx 0; */
44
+  padding: 5rpx 0;
42 45
   background-color: #3573EA;
43 46
   color: #fff;
44 47
   text-align: center;
45 48
   border-radius: 10rpx;
46 49
 }
47 50
 
51
+/* 地图容器样式 */
52
+.map_box {
53
+  position: relative;
54
+  width: 100%;
55
+  height: 60vh;
56
+}
57
+
58
+/* 打卡记录样式 */
59
+.jilu {
60
+  position: absolute;
61
+  top: 20rpx;
62
+  right: 0rpx;
63
+  padding: 10rpx 20rpx;
64
+  background-color: rgba(255, 255, 255, 0.8);
65
+  color: #3573EA;
66
+  font-size: 28rpx;
67
+  border-radius: 20rpx;
68
+  z-index: 10;
69
+  box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
70
+  cursor: pointer;
71
+  transition: all 0.3s ease;
72
+}
73
+
74
+.jilu:active {
75
+  background-color: rgba(53, 115, 234, 0.2);
76
+  transform: scale(0.95);
77
+}
78
+
48 79
 .time {
49 80
   padding: 16rpx 20rpx;
50 81
   background-color: wheat;
@@ -204,22 +235,171 @@ page {
204 235
   display: block;
205 236
   width: 120rpx;
206 237
 }
207
-.map_box {
208
-  position: relative;
238
+/* 批量操作弹出层样式 */
239
+.batch-modal {
240
+  position: fixed;
241
+  top: 0;
242
+  left: 0;
243
+  width: 100%;
244
+  height: 100%;
245
+  background-color: rgba(0, 0, 0, 0.5);
246
+  display: flex;
247
+  justify-content: center;
248
+  align-items: center;
249
+  z-index: 1000;
209 250
 }
210
-.mapSize{
211
-  height: 60vh;
251
+
252
+.batch-modal-content {
253
+  width: 90%;
254
+  max-width: 500px;
255
+  background-color: #fff;
256
+  border-radius: 10px;
257
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
258
+  overflow: hidden;
259
+}
260
+
261
+.batch-modal-header {
262
+  display: flex;
263
+  justify-content: space-between;
264
+  align-items: center;
265
+  padding: 15px 20px;
266
+  border-bottom: 1px solid #e8e8e8;
267
+}
268
+
269
+.batch-modal-title {
270
+  font-size: 16px;
271
+  font-weight: bold;
272
+  color: #333;
273
+}
274
+
275
+.batch-modal-close {
276
+  font-size: 24px;
277
+  color: #999;
278
+  cursor: pointer;
279
+  width: 24px;
280
+  height: 24px;
281
+  display: flex;
282
+  justify-content: center;
283
+  align-items: center;
284
+}
285
+
286
+.batch-modal-close:hover {
287
+  color: #666;
288
+}
289
+
290
+.batch-modal-body {
291
+  padding: 20px;
292
+}
293
+
294
+.form-item {
295
+  margin-bottom: 20px;
296
+  display: flex;
297
+  flex-direction: column;
298
+}
299
+
300
+.form-label {
301
+  font-size: 14px;
302
+  color: #666;
303
+  margin-bottom: 8px;
304
+}
305
+
306
+.batch-modal-body picker {
307
+  height: 40px;
308
+  border: 1px solid #e8e8e8;
309
+  border-radius: 5px;
310
+  padding: 0 10px;
311
+  box-sizing: border-box;
312
+  background-color: #f8f8f8;
313
+  font-size: 14px;
314
+  display: flex;
315
+  align-items: center;
316
+}
317
+
318
+/* Checkbox多选样式 */
319
+.checkbox-group-wrapper {
320
+  border: 1px solid #ddd;
321
+  border-radius: 4px;
322
+  padding: 15px;
323
+  background-color: #fff;
324
+  max-height: 200px;
325
+  overflow-y: auto;
326
+}
327
+
328
+.checkbox-item {
329
+  display: flex;
330
+  align-items: center;
331
+  margin-bottom: 10px;
332
+  padding: 5px 0;
333
+}
334
+
335
+.checkbox-item:last-child {
336
+  margin-bottom: 0;
337
+}
338
+
339
+.checkbox-item checkbox {
340
+  margin-right: 10px;
341
+  transform: scale(1.2);
342
+}
343
+
344
+.checkbox-item text {
345
+  font-size: 16px;
346
+  color: #333;
347
+}
348
+
349
+.selected-count {
350
+  margin-top: 10px;
351
+  font-size: 14px;
352
+  color: #666;
353
+  text-align: right;
354
+}
355
+
356
+.batch-modal-footer {
357
+  display: flex;
358
+  justify-content: space-between;
359
+  padding: 15px 20px;
360
+  border-top: 1px solid #e8e8e8;
361
+}
362
+
363
+.cancel-btn {
364
+  flex: 1;
365
+  height: 40px;
366
+  border-radius: 5px;
367
+  font-size: 14px;
368
+  background-color: #f8f8f8;
369
+  color: #666;
370
+  border: 1px solid #e8e8e8;
371
+  margin-right: 10px;
372
+}
373
+
374
+.confirm-btn {
375
+  flex: 1;
376
+  height: 40px;
377
+  border-radius: 5px;
378
+  font-size: 14px;
379
+  background-color: #1a73e8;
380
+  color: #fff;
381
+  border: none;
382
+}
383
+
384
+.confirm-btn[disabled] {
385
+  background-color: #ccc;
386
+  color: #fff;
387
+}
388
+
389
+/* 地图和签到按钮样式 */
390
+.map-container {
212 391
   width: 100%;
392
+  height: 300px;
213 393
 }
214
-.jilu {
215
-  width: auto;
216
-  color: #ffffff;
217
-  position: absolute;
218
-  right: 20rpx;
219
-  top: 0;
220
-  z-index: 10000;
221
-  background-color: #1E90FF;
394
+
395
+.location-info {
396
+  padding: 10px;
397
+  background-color: #f8f8f8;
398
+  border-bottom: 1px solid #e8e8e8;
222 399
 }
223
-.btn_all {
224
-  background: #cccccc;
400
+
401
+.sign-buttons {
402
+  display: flex;
403
+  justify-content: space-around;
404
+  padding: 20px 10px;
225 405
 }