2025-11-10 16:41:31 +08:00

433 lines
13 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<BasicLayout>
<!-- 巡查记录列表 -->
<view class="inspection-list">
<BasicListLayout
@register="registerInspection"
style="position: absolute"
>
<template v-slot="{ data, index }">
<view class="inspection-record bg-white r-md p-15 mb-15 timeline-item">
<!-- 时间轴连接线 -->
<view class="timeline-line" v-if="index < 10"></view>
<!-- 时间轴节点 -->
<view class="timeline-dot">
<view class="dot-inner"></view>
</view>
<view class="record-header">
<view class="record-time">
<u-icon name="clock" color="#4080ff" size="16"></u-icon>
<text class="time-text">{{ formatTime(data.xctime) }}</text>
</view>
<view class="record-status">
<text class="status-text">已巡查</text>
</view>
</view>
<view class="record-content">
<view class="content-item">
<text class="item-label">巡查教师</text>
<text class="item-value">{{ data.jsxm }}</text>
<text class="item-label" style="margin-left: 20px;">代课教师</text>
<text class="item-value">{{ data.dkjsxm || '无' }}</text>
</view>
<view class="content-item">
<text class="item-label">点名时间</text>
<text class="item-value">{{ data.dmTime ? formatTime(data.dmTime) : '无' }}</text>
</view>
<view class="content-item flex-col">
<text class="item-label" style="flex: 0 0 25px"
>巡查项目</text
>
<view class="item-value" style="width: 100%">
<template
v-if="data.xkXcXmList && data.xkXcXmList.length > 0"
>
<view
v-for="(xm, idx) in data.xkXcXmList"
:key="xm.xcXmId"
style="margin-bottom: 4px"
>
<view>
<view style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 4px;">
<view style="display: flex; align-items: center; flex: 1;">
<text style="margin-right: 8px;">{{ idx + 1 }}{{ xm.xcMc }}</text>
<view style="display: flex; align-items: center;">
<u-icon
:name="xm.xcJg === 'A' ? 'close-circle-fill' : 'checkmark-circle-fill'"
:color="xm.xcJg === 'A' ? '#f56c6c' : '#67c23a'"
size="18"
></u-icon>
</view>
</view>
<view style="font-size: 12px; color: #666;">
<text v-if="xm.xcJg === 'A'" style="color: #f56c6c;">
</text>
<text v-else style="color: #67c23a;">
</text>
</view>
</view>
</view>
</view>
</template>
<template v-else> 无巡查项目 </template>
</view>
</view>
<view
class="content-item"
v-if="data.zp && data.zp.length > 0"
>
<text class="item-label">巡查图片</text>
<view
class="item-value"
style="display: flex; flex-wrap: wrap; gap: 8px"
>
<image
v-for="(img, imgIdx) in getImageArray(data.zp)"
:key="imgIdx"
:src="imagUrl(img)"
mode="aspectFill"
style="
width: 60px;
height: 60px;
border-radius: 4px;
border: 1px solid #eee;
cursor: pointer;
"
@click="handlePreviewImage(img, getImageArray(data.zp))"
/>
</view>
</view>
<view
class="content-item"
v-if="data.sp && data.sp.length > 0"
>
<text class="item-label">巡查视频</text>
<view
class="item-value"
style="display: flex; flex-wrap: wrap; gap: 8px"
>
<view
v-for="(video, vIdx) in getVideoArray(data.sp)"
:key="vIdx"
style="
width: 80px;
height: 60px;
position: relative;
border-radius: 4px;
overflow: hidden;
border: 1px solid #eee;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
background: #000;
"
@click="handlePreviewVideo(getVideoArray(data.sp), vIdx)"
>
<video
:src="video"
style="width: 100%; height: 100%; object-fit: cover"
:controls="false"
:show-center-play-btn="false"
:show-play-btn="false"
:show-fullscreen-btn="false"
:show-progress="false"
:show-mute-btn="false"
:enable-progress-gesture="false"
:enable-play-gesture="false"
:loop="false"
:muted="true"
:poster="''"
></video>
<view
style="
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
"
>
<u-icon
name="play-right-fill"
color="#fff"
size="28"
></u-icon>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
</BasicListLayout>
</view>
</BasicLayout>
</template>
<script setup lang="ts">
import { xkXcFindPageApi } from "@/api/base/xkXcApi";
import BasicLayout from "@/components/BasicLayout/Layout.vue";
import { useLayout } from "@/components/BasicListLayout/hooks/useLayout";
import { useDataStore } from "@/store/modules/data";
import { useUserStore } from "@/store/modules/user";
import { computed, onMounted, ref } from "vue";
import dayjs from "dayjs";
import { imagUrl } from "@/utils";
const { getJs } = useUserStore();
const { getData } = useDataStore();
const js = computed(() => getJs);
const xkkc = computed(() => getData);
// 巡查记录列表参数
let inspectionParams = ref({
rows: 10,
xkkcId: xkkc.value.id,
});
// 添加调试信息
console.log('巡查记录查询参数:', inspectionParams.value);
// 巡查记录列表
const [registerInspection, { reload }] = useLayout({
api: xkXcFindPageApi,
componentProps: {},
param: inspectionParams.value,
});
// 图片预览
const handlePreviewImage = (img: string, images: string[]) => {
// 兼容uni-app的图片预览API
const processedImages = images.map(image => imagUrl(image));
uni.previewImage({
current: imagUrl(img),
urls: processedImages,
});
};
// 视频预览
const handlePreviewVideo = (videos: string[], index: number) => {
// 兼容uni-app的视频预览API
// uni.previewMedia 仅在H5/小程序/APP支持
uni.previewMedia({
current: index,
sources: videos.map((url) => ({
url: imagUrl(url),
type: "video",
})),
});
};
// 格式化时间
const formatTime = (timestamp: string) => {
const date = dayjs(timestamp);
const weekDays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
const weekDay = weekDays[date.day()];
return `${weekDay} ${date.format("YYYY-MM-DD HH:mm")}`;
};
// 将逗号分隔的字符串转换为数组
const getImageArray = (str: string) => {
if (!str) return [];
return str.split(",").map((item) => item.trim());
};
// 将逗号分隔的字符串转换为数组
const getVideoArray = (str: string) => {
if (!str) return [];
return str.split(",").map((item) => item.trim());
};
// 页面加载时重新加载巡查记录
onMounted(() => {
reload();
});
</script>
<style scoped lang="scss">
.inspection-list {
position: relative;
height: calc(100vh - 50px);
padding-left: 12px; // 为时间轴留出空间
.inspection-record {
position: relative;
margin-left: 0; // 去掉左边距
background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%);
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
border: 1px solid #e8f4fd;
transition: all 0.3s ease;
overflow: hidden;
&:hover {
transform: translateY(-2px);
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
border-color: #4080ff;
}
// 时间轴连接线
.timeline-line {
position: absolute;
left: -12px;
top: 0;
width: 2px;
height: 100%;
background: linear-gradient(180deg, #4080ff 0%, #e8f4fd 100%);
z-index: 1;
}
// 时间轴节点
.timeline-dot {
position: absolute;
left: -18px;
top: 20px;
width: 12px;
height: 12px;
background: #4080ff;
border-radius: 50%;
z-index: 2;
box-shadow: 0 0 0 4px #ffffff, 0 0 0 6px #e8f4fd;
.dot-inner {
width: 100%;
height: 100%;
background: linear-gradient(135deg, #4080ff 0%, #66b3ff 100%);
border-radius: 50%;
animation: pulse 2s infinite;
}
}
.record-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
padding: 12px 8px;
background: linear-gradient(135deg, #f0f7ff 0%, #e8f4fd 100%);
border-radius: 8px 8px 0 0;
border-bottom: 1px solid #d1e7ff;
.record-time {
display: flex;
align-items: center;
font-size: 15px;
color: #2c5aa0;
font-weight: 600;
.time-text {
margin-left: 8px;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
}
.record-status {
padding: 6px 12px;
border-radius: 20px;
background: linear-gradient(135deg, #4080ff 0%, #66b3ff 100%);
color: #ffffff;
font-size: 12px;
font-weight: 600;
box-shadow: 0 2px 8px rgba(64, 128, 255, 0.3);
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
}
.record-content {
padding: 0 8px 12px 8px;
.content-item {
display: flex;
margin-bottom: 6px;
font-size: 14px;
color: #333;
padding: 6px 8px;
background: #fafbfc;
border-radius: 6px;
border-left: 3px solid #e8f4fd;
transition: all 0.2s ease;
&:hover {
background: #f0f7ff;
border-left-color: #4080ff;
}
.item-label {
font-weight: 600;
flex: 0 0 70px;
color: #2c5aa0;
}
.item-value {
color: #4a5568;
}
}
}
}
// 第一个时间轴节点特殊样式
.inspection-record:first-child {
.timeline-dot {
background: linear-gradient(135deg, #67c23a 0%, #85ce61 100%);
box-shadow: 0 0 0 4px #ffffff, 0 0 0 6px #f0f9ff;
}
}
// 最后一个时间轴节点
.inspection-record:last-child {
.timeline-line {
display: none;
}
}
// 动画效果
@keyframes pulse {
0% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(1.1);
opacity: 0.8;
}
100% {
transform: scale(1);
opacity: 1;
}
}
// 渐入动画
.timeline-item {
animation: fadeInUp 0.6s ease-out;
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
::v-deep .zp-loading-fixed {
position: absolute;
}
::v-deep .d-load-main {
position: absolute;
}
}
</style>