|
@@ -1,23 +1,252 @@
|
|
|
<template>
|
|
|
<div class="container">
|
|
|
- <DateRangePicker v-model="queryParams.dateRange" @change="initData" />
|
|
|
+ <van-field
|
|
|
+ v-model="dateRangeText"
|
|
|
+ class="common-search"
|
|
|
+ :left-icon="searchImg"
|
|
|
+ :right-icon="closeImg"
|
|
|
+ placeholder="请选择时间段"
|
|
|
+ readonly
|
|
|
+ @click="show = true"
|
|
|
+ @click-right-icon.stop="handleClear"
|
|
|
+ />
|
|
|
+ <van-list
|
|
|
+ v-model:loading="loading"
|
|
|
+ v-model:error="error"
|
|
|
+ error-text="请求失败,点击重新加载"
|
|
|
+ :finished="finished"
|
|
|
+ finished-text="没有更多记录了"
|
|
|
+ class="list"
|
|
|
+ @load="getList">
|
|
|
+ <div v-for="(item, index) in dataList" :key="index" class="card">
|
|
|
+ <div class="line1">
|
|
|
+ <div class="data-box1">
|
|
|
+ <div class="data-item">
|
|
|
+ <i class="icon1" />
|
|
|
+ <div class="text1">点名开始时间:{{ item.time1 }}</div>
|
|
|
+ </div>
|
|
|
+ <div class="data-item">
|
|
|
+ <i class="icon2" />
|
|
|
+ <div class="text1">点名结束时间:{{item.time2}}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="btn" @click="handleToDetails(item.id)">查看详情</div>
|
|
|
+ </div>
|
|
|
+ <div class="line2">
|
|
|
+ <div class="data-item1">
|
|
|
+ <div class="text1">点名时长</div>
|
|
|
+ <div class="text2">{{ item.time3 }}</div>
|
|
|
+ </div>
|
|
|
+ <div class="data-item2">
|
|
|
+ <div class="text1">已应答</div>
|
|
|
+ <div class="text2">{{ item.data1 }}</div>
|
|
|
+ </div>
|
|
|
+ <div class="data-item3">
|
|
|
+ <div class="text1">未应答</div>
|
|
|
+ <div class="text2">{{ item.data2 }}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </van-list>
|
|
|
+ <van-calendar v-model:show="show" type="range" @confirm="onConfirm" />
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script lang="ts" setup name="rollCallRecord">
|
|
|
-import DateRangePicker from "@/components/DateRangePicker/index.vue";
|
|
|
-import { reactive } from "vue";
|
|
|
+import {onMounted, reactive, ref} from "vue";
|
|
|
+import closeImg from "@/assets/images/close.png";
|
|
|
+import searchImg from "@/assets/images/search.png";
|
|
|
+import {parseTime} from "@/utils/ruoyi";
|
|
|
+import {useRouter} from "vue-router";
|
|
|
|
|
|
+const router = useRouter();
|
|
|
const queryParams = reactive({
|
|
|
page: 0,
|
|
|
page_size: 15,
|
|
|
- dateRange: ''
|
|
|
+ dateRange: []
|
|
|
});
|
|
|
-const initData = () => {
|
|
|
-debugger
|
|
|
+const total = ref(0);
|
|
|
+const loading = ref(false);
|
|
|
+const error = ref(false);
|
|
|
+const finished = ref(false);
|
|
|
+let dataList = ref([]);
|
|
|
+let dateRangeText = ref('');
|
|
|
+let show = ref(false);
|
|
|
+let timer;
|
|
|
+const onConfirm = (data) => {
|
|
|
+ show.value = false;
|
|
|
+ const startDate = parseTime(data[0], '{y}-{m}-{d}');
|
|
|
+ const endDate = parseTime(data[1], '{y}-{m}-{d}');
|
|
|
+ queryParams.dateRange = [startDate, endDate];
|
|
|
+ dateRangeText.value = startDate + '至' + endDate;
|
|
|
+ getList();
|
|
|
}
|
|
|
+const handleClear = () => {
|
|
|
+ queryParams.dateRange = [];
|
|
|
+ dateRangeText.value = '';
|
|
|
+ getList();
|
|
|
+}
|
|
|
+const calculateDuration = (startTimeStr) => {
|
|
|
+ // 将起始时间字符串转换为日期对象
|
|
|
+ const startTime = new Date(startTimeStr);
|
|
|
+
|
|
|
+ // 获取当前时间作为结束时间
|
|
|
+ const endTime = new Date();
|
|
|
+
|
|
|
+ // 确保起始时间在结束时间之前(可选,用于验证输入)
|
|
|
+ if (startTime > endTime) {
|
|
|
+ return '';
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算时间差(以毫秒为单位)
|
|
|
+ let timeDifference = endTime - startTime;
|
|
|
+
|
|
|
+ // 将毫秒转换为秒、分钟和小时
|
|
|
+ const seconds = Math.floor((timeDifference / 1000) % 60);
|
|
|
+ const minutes = Math.floor((timeDifference / (1000 * 60)) % 60);
|
|
|
+ const hours = Math.floor((timeDifference / (1000 * 60 * 60)));
|
|
|
+
|
|
|
+ // 格式化时间为 HH:MM:SS
|
|
|
+ const formattedDuration = `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`;
|
|
|
+
|
|
|
+ // 返回格式化的持续时间
|
|
|
+ return formattedDuration;
|
|
|
+};
|
|
|
+const updateTime = () => {
|
|
|
+ dataList.value.forEach((item) => {
|
|
|
+ item.time3 = calculateDuration(item.time1);
|
|
|
+ })
|
|
|
+}
|
|
|
+const getList = () => {
|
|
|
+ dataList.value = [
|
|
|
+ { id: 1, time1: '2024-10-22 11:10:08', time2: '2024-10-22 12:00:00', time3: '00:01:06', data1: 23, data2: 12 },
|
|
|
+ { id: 2, time1: '2024-10-22 11:10:08', time2: '2024-10-22 12:00:00', time3: '00:01:06', data1: 23, data2: 12 },
|
|
|
+ { id: 3, time1: '2024-10-22 11:10:08', time2: '2024-10-23 12:00:00', time3: '00:01:06', data1: 23, data2: 12 },
|
|
|
+ { id: 4, time1: '2024-10-22 11:10:08', time2: '2024-10-23 12:00:00', time3: '00:01:06', data1: 23, data2: 12 },
|
|
|
+ { id: 5, time1: '2024-10-22 11:10:08', time2: '2024-10-23 12:00:00', time3: '00:01:06', data1: 23, data2: 12 },
|
|
|
+ { id: 6, time1: '2024-10-22 11:10:08', time2: '2024-10-23 12:00:00', time3: '00:01:06', data1: 23, data2: 12 },
|
|
|
+ { id: 7, time1: '2024-10-22 11:10:08', time2: '2024-10-23 12:00:00', time3: '00:01:06', data1: 23, data2: 12 }
|
|
|
+ ]
|
|
|
+ loading.value = false;
|
|
|
+ finished.value = true;
|
|
|
+ total.value = 7;
|
|
|
+ if (!timer) {
|
|
|
+ timer = setInterval(updateTime, 1000)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const handleToDetails = (id) => {
|
|
|
+ router.push({
|
|
|
+ name: 'RollCallDetails2',
|
|
|
+ query: { id }
|
|
|
+ });
|
|
|
+}
|
|
|
+onMounted(() => {
|
|
|
+ getList();
|
|
|
+})
|
|
|
</script>
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
-
|
|
|
+.list {
|
|
|
+ margin-top: 9px;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ .card {
|
|
|
+ width: 343px;
|
|
|
+ height: 150px;
|
|
|
+ background: url("@/assets/images/onlineRollCall/card2.png") no-repeat;
|
|
|
+ background-size: 100% 100%;
|
|
|
+ margin-top: 16px;
|
|
|
+ padding: 16px 5px;
|
|
|
+ &:first-child {
|
|
|
+ margin-top: 0;
|
|
|
+ }
|
|
|
+ .line1 {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: flex-start;
|
|
|
+ .data-item{
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ .icon1 {
|
|
|
+ width: 16px;
|
|
|
+ height: 17px;
|
|
|
+ background: url("@/assets/images/onlineRollCall/icon6.png") no-repeat;
|
|
|
+ background-size: 100% 100%;
|
|
|
+ margin-right: 3px;
|
|
|
+ }
|
|
|
+ .icon2 {
|
|
|
+ width: 17px;
|
|
|
+ height: 17px;
|
|
|
+ background: url("@/assets/images/onlineRollCall/icon7.png") no-repeat;
|
|
|
+ background-size: 100% 100%;
|
|
|
+ margin-right: 3px;
|
|
|
+ }
|
|
|
+ .text1 {
|
|
|
+ font-size: 14px;
|
|
|
+ line-height: 26px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .btn {
|
|
|
+ background: #2C81FF;
|
|
|
+ border-radius: 2px;
|
|
|
+ font-size: 14px;
|
|
|
+ color: #FFFFFF;
|
|
|
+ padding: 0 12px;
|
|
|
+ height: 24px;
|
|
|
+ line-height: 24px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .line2 {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ margin: 23px;
|
|
|
+ .data-item1, .data-item2, .data-item3 {
|
|
|
+ font-weight: bold;
|
|
|
+ line-height: 26px;
|
|
|
+ .text1 {
|
|
|
+ font-size: 14px;
|
|
|
+ }
|
|
|
+ .text2 {
|
|
|
+ font-size: 18px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .data-item1 {
|
|
|
+ color: #2C81FF;
|
|
|
+ }
|
|
|
+ .data-item2 {
|
|
|
+ color: #40C75F;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ }
|
|
|
+ .data-item3 {
|
|
|
+ color: #FF2F3C;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+.common-search {
|
|
|
+ //:deep(.van-field__left-icon) {
|
|
|
+ // .van-icon__image {
|
|
|
+ // width: 12px;
|
|
|
+ // height: 12px;
|
|
|
+ // }
|
|
|
+ //}
|
|
|
+ :deep(.van-field__right-icon) {
|
|
|
+ width: 30px;
|
|
|
+ height: 30px;
|
|
|
+ padding: 0;
|
|
|
+ .van-icon__image {
|
|
|
+ width: 30px;
|
|
|
+ height: 30px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
</style>
|