checkIn.vue 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. <template>
  2. <view class="attendance-page">
  3. <view class="header">
  4. <text class="title">我的考勤记录</text>
  5. <picker mode="selector" :range="timeRanges" @change="onTimeRangeChange">
  6. <view class="picker">
  7. {{ selectedTimeRange }}
  8. </view>
  9. </picker>
  10. </view>
  11. <!-- 数据统计区域 -->
  12. <view class="statistics" :class="{'show': chartShow}">
  13. <view class="statistic-card">
  14. <view class="statistic-title">考勤统计</view>
  15. <view class="statistic-item">
  16. <text class="label">出勤:</text>
  17. <text class="value">{{ attendanceCount }}</text>
  18. </view>
  19. <view class="statistic-item">
  20. <text class="label">请假:</text>
  21. <text class="value">{{ leaveCount }}</text>
  22. </view>
  23. <view class="statistic-item">
  24. <text class="label">出差:</text>
  25. <text class="value">{{ businessTripCount }}</text>
  26. </view>
  27. <view class="statistic-item">
  28. <text class="label">打卡:</text>
  29. <text class="value">{{ clockInCount }}</text>
  30. </view>
  31. <view class="statistic-item">
  32. <text class="label">补卡:</text>
  33. <text class="value">{{ makeUpCount }}</text>
  34. </view>
  35. </view>
  36. </view>
  37. <!-- 今日签到信息区域 -->
  38. <view class="todayCheckIn" :class="{'show': shouldShow}">
  39. <view class="check-in-container">
  40. <text class="title2">今日签到信息</text>
  41. <view class="info-row">
  42. <text class="value">日期:</text>
  43. <text class="label">{{todayData.day}}</text>
  44. </view>
  45. <view class="info-row">
  46. <text class="value">晨签时间:</text>
  47. <text class="label">{{todayData.startTime || '未打卡'}} </text>
  48. </view>
  49. <view class="info-row">
  50. <text class="value">晚签时间:</text>
  51. <text class="label">{{todayData.endTime || '未打卡'}} </text>
  52. </view>
  53. <view class="info-row">
  54. <text class="value">状态:</text>
  55. <text class="label">{{todayData.status}}</text>
  56. </view>
  57. </view>
  58. </view>
  59. <!-- 图表区域 -->
  60. <view class="chart-container" :class="{'show': chartShow}">
  61. <view class="">
  62. <text class="chart-title">考勤趋势图</text>
  63. <view class="charts-box">
  64. <qiun-data-charts :type="chartsType" :opts="opts" :chartData="chartData" />
  65. </view>
  66. </view>
  67. </view>
  68. </view>
  69. </template>
  70. <script setup>
  71. import {
  72. ref,
  73. onMounted,
  74. reactive
  75. } from 'vue';
  76. // 创建一个响应式对象记录今天的考勤数据
  77. const todayData = reactive({
  78. day: '2024-11-11', // 当前日期
  79. startTime: '08:59:59', // 晨签到时间
  80. endTime: '', // 晚签到时间
  81. status: '待补卡' // 签到状态
  82. })
  83. // 控制今日签到信息和图表显示的状态
  84. const shouldShow = ref(true) // 今日签到信息是否显示
  85. const chartShow = ref(false) // 图表是否显示
  86. // 图表类型的响应式引用
  87. const chartsType = ref('')
  88. // 图表要填充的数据
  89. const chartData = ref({})
  90. // 图表配置选项
  91. const opts = ref({
  92. color: ["#1890FF", "#91CB74", "#FAC858", "#EE6666", "#73C0DE", "#3CA272", "#FC8452", "#9A60B4", "#ea7ccc"], // 设定图表颜色
  93. padding: [15, 15, 0, 5],
  94. enableScroll: false, // 禁用滚动
  95. legend: {}, // 图例设置
  96. xAxis: {
  97. disableGrid: true // 禁用网格线
  98. },
  99. yAxis: {
  100. data: [{
  101. min: 0 // y轴最小值
  102. }]
  103. },
  104. extra: {
  105. column: {
  106. type: "group",
  107. width: 30,
  108. activeBgColor: "#000000",
  109. activeBgOpacity: 0.08
  110. }
  111. }
  112. });
  113. // 时间范围选择数组
  114. const timeRanges = ref(['日', '周', '月']);
  115. const selectedTimeRange = ref('日'); // 初始化选择的时间范围为“日”
  116. // 考勤相关统计数据
  117. const attendanceCount = ref(0); // 出勤次数
  118. const leaveCount = ref(0); // 请假次数
  119. const businessTripCount = ref(0); // 出差次数
  120. const clockInCount = ref(0); // 打卡次数
  121. const makeUpCount = ref(0); // 补卡次数
  122. const currentData = ref({
  123. attendance: 0, // 出勤
  124. leave: 0, // 请假
  125. businessTrip: 0, // 出差
  126. clockIn: 0, // 打卡
  127. makeUp: 0, // 补卡
  128. res: {}, // 记录详细数据
  129. });
  130. onMounted(() => {
  131. fetchAttendanceData();
  132. });
  133. // 时间范围切换事件
  134. function onTimeRangeChange(event) {
  135. const selectedIndex = event.detail.value;
  136. selectedTimeRange.value = timeRanges.value[selectedIndex]; // 设置选择的时间范围(日周月)
  137. fetchAttendanceData(); // 获取考勤数据
  138. // 根据选择的时间范围更新展示内容
  139. switch (selectedTimeRange.value) {
  140. case '日':
  141. showDay(); // 显示今日考勤
  142. break;
  143. case '周':
  144. chartsType.value = 'pie'; // 设置图表类型为饼图
  145. showWeekAndMonth(); // 显示周考勤
  146. break;
  147. case '月':
  148. chartsType.value = 'column'; // 设置图表类型为柱状图
  149. showWeekAndMonth(); // 显示月考勤
  150. break;
  151. }
  152. };
  153. // 显示今日考勤数据
  154. function showDay() {
  155. shouldShow.value = true; // 显示今日签到信息
  156. chartShow.value = false; // 隐藏图表
  157. }
  158. // 显示周或者月考勤
  159. function showWeekAndMonth() {
  160. shouldShow.value = false; // 隐藏今日签到信息
  161. chartShow.value = true; // 显示图表
  162. getData(); // 获取图表数据
  163. }
  164. // 获取考勤数据的函数
  165. function fetchAttendanceData() {
  166. // 模拟考勤数据
  167. const mockData = {
  168. 日: {
  169. attendance: 5,
  170. leave: 0,
  171. businessTrip: 1,
  172. clockIn: 5,
  173. makeUp: 0,
  174. res: {}
  175. },
  176. 周: {
  177. attendance: 30,
  178. leave: 2,
  179. businessTrip: 1,
  180. clockIn: 32,
  181. makeUp: 1,
  182. res: {
  183. series: [{
  184. data: [{
  185. "name": "出勤",
  186. "value": 50,
  187. "labelText": "出勤:50次"
  188. }, {
  189. "name": "请假",
  190. "value": 30
  191. }, {
  192. "name": "出差",
  193. "value": 20
  194. }, {
  195. "name": "打卡",
  196. "value": 18,
  197. "labelText": "打卡:18次"
  198. }, {
  199. "name": "补卡",
  200. "value": 8
  201. }]
  202. }],
  203. }
  204. },
  205. 月: {
  206. attendance: 100,
  207. leave: 5,
  208. businessTrip: 2,
  209. clockIn: 105,
  210. makeUp: 3,
  211. res: {
  212. categories: ["第1周", "第2周", "第3周", "第4周"],
  213. series: [{
  214. name: "出勤",
  215. data: [35, 8, 25, 37]
  216. },
  217. {
  218. name: "出差",
  219. data: [70, 40, 65, 100]
  220. },
  221. {
  222. name: "请假",
  223. data: [100, 80, 95, 10]
  224. }]
  225. },
  226. }
  227. };
  228. // 获取当前选择时间范围的考勤数据
  229. const currentData1 = mockData[selectedTimeRange.value];
  230. currentData.value = currentData1; // 更新当前数据
  231. // 更新统计数据
  232. if (currentData1) {
  233. attendanceCount.value = currentData1.attendance;
  234. leaveCount.value = currentData1.leave;
  235. businessTripCount.value = currentData1.businessTrip;
  236. clockInCount.value = currentData1.clockIn;
  237. makeUpCount.value = currentData1.makeUp;
  238. } else {
  239. console.error('没有找到相关的考勤数据:', selectedTimeRange.value); // 错误处理
  240. }
  241. }
  242. // 获取图表数据
  243. function getData() {
  244. let res = currentData.value.res; // 获取当前数据的额外信息
  245. chartData.value = JSON.parse(JSON.stringify(res)); // 更新图表数据
  246. }
  247. // 计算数组和的辅助函数 (未使用)
  248. function countArr(arr){
  249. const sum=0;
  250. for (var i = 0; i < arr.length; i++) {
  251. sum+=arr[i];
  252. }
  253. return sum;
  254. }
  255. </script>
  256. <style scoped>
  257. .attendance-page {
  258. padding: 20px;
  259. background-color: #f9f9f9;
  260. }
  261. .header {
  262. display: flex;
  263. justify-content: space-between;
  264. align-items: center;
  265. margin-bottom: 20px;
  266. }
  267. .title {
  268. font-size: 24px;
  269. font-weight: bold;
  270. color: #333;
  271. }
  272. .picker {
  273. border: 1px solid #3498db;
  274. border-radius: 5px;
  275. padding: 10px;
  276. background-color: #ecf6fc;
  277. color: #3498db;
  278. }
  279. .statistics {
  280. display: flex;
  281. justify-content: center;
  282. }
  283. .statistic-card {
  284. background: white;
  285. border-radius: 10px;
  286. padding: 20px;
  287. box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
  288. /* width: 100%; */
  289. max-width: 600px;
  290. }
  291. .statistic-title {
  292. text-align: center;
  293. font-size: 20px;
  294. font-weight: bold;
  295. color: #333;
  296. }
  297. .statistic-item {
  298. display: flex;
  299. justify-content: space-between;
  300. margin-bottom: 10px;
  301. border-bottom: 1px solid #eee;
  302. padding-bottom: 10px;
  303. }
  304. .label {
  305. font-size: 18px;
  306. color: #666;
  307. }
  308. .value {
  309. font-size: 16px;
  310. font-weight: bold;
  311. color: #333;
  312. }
  313. .chart-container {
  314. margin-top: 20px;
  315. }
  316. .chart-title {
  317. font-size: 20px;
  318. font-weight: bold;
  319. text-align: center;
  320. margin-bottom: 10px;
  321. color: #333;
  322. }
  323. .charts-box {
  324. width: 100%;
  325. height: 300px;
  326. }
  327. .todayCheckIn {
  328. background-color: #ffffff;
  329. padding: 20px;
  330. box-sizing: border-box;
  331. }
  332. .check-in-container {
  333. width: 100%;
  334. height: 100%;
  335. display: flex;
  336. flex-direction: column;
  337. align-items: center;
  338. justify-content: center;
  339. }
  340. .title2 {
  341. font-size: 22px;
  342. font-weight: bold;
  343. margin-bottom: 20px;
  344. }
  345. .info-row {
  346. width: 100%;
  347. display: flex;
  348. justify-content: space-between;
  349. margin-bottom: 10px;
  350. }
  351. .todayCheckIn,
  352. .chart-container,
  353. .statistics {
  354. /* 默认隐藏 */
  355. display: none;
  356. }
  357. /* 当需要显示时,可以添加一个额外的类 */
  358. .todayCheckIn.show,
  359. .chart-container.show,
  360. .statistics.show {
  361. display: block;
  362. }
  363. </style>