816 lines
22 KiB
Vue
816 lines
22 KiB
Vue
|
|
<template>
|
|||
|
|
<BasicLayout>
|
|||
|
|
<!-- 待巡查内容 -->
|
|||
|
|
<view class="pending-inspection">
|
|||
|
|
<!-- 课程信息卡片 -->
|
|||
|
|
<view class="course-card mx-15 my-15 bg-white white-bg-color r-md p-15">
|
|||
|
|
<view class="flex-row items-center mb-15">
|
|||
|
|
<view class="course-icon flex-center mr-10">
|
|||
|
|
<u-icon name="calendar" color="#4080ff" size="20"></u-icon>
|
|||
|
|
</view>
|
|||
|
|
<text class="font-16 font-bold">{{ xkkc.kcmc }}</text>
|
|||
|
|
<text class="font-14 cor-999 ml-10"
|
|||
|
|
>{{ todayInfo.date }} ({{ todayInfo.weekName }})</text
|
|||
|
|
>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 课业辅导信息 -->
|
|||
|
|
<view class="course-time-info">
|
|||
|
|
<view class="time-item">
|
|||
|
|
<view class="time-label">年级:</view>
|
|||
|
|
<view class="time-value">{{ xkkc.gradeName || '暂无' }}</view>
|
|||
|
|
</view>
|
|||
|
|
<view class="time-item" v-if="xkkc.bjmc">
|
|||
|
|
<view class="time-label">班级:</view>
|
|||
|
|
<view class="time-value">{{ xkkc.bjmc || '暂无' }}</view>
|
|||
|
|
</view>
|
|||
|
|
<view class="time-item">
|
|||
|
|
<view class="time-label">巡查类型:</view>
|
|||
|
|
<view class="time-value">{{ xkkc.xclx === 'B' ? '课业辅导巡查' : '课程巡查' }}</view>
|
|||
|
|
</view>
|
|||
|
|
<view class="time-item">
|
|||
|
|
<view class="time-label">排班标题:</view>
|
|||
|
|
<view class="time-value">{{ xkkc.xcbt || '暂无' }}</view>
|
|||
|
|
</view>
|
|||
|
|
<view class="time-item">
|
|||
|
|
<view class="time-label">学期:</view>
|
|||
|
|
<view class="time-value">{{ xkkc.xqmc || '暂无' }}</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 巡查时间状态 -->
|
|||
|
|
<view class="inspection-status" v-if="!canInspect">
|
|||
|
|
<u-icon name="clock" color="#ff9900" size="16"></u-icon>
|
|||
|
|
<text class="status-text">{{ inspectionStatusText }}</text>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<view v-if="canInspect">
|
|||
|
|
<!-- 巡查项目 -->
|
|||
|
|
<view class="section mx-15 mb-15">
|
|||
|
|
<view class="section-title-bar">
|
|||
|
|
<view class="decorator"></view>
|
|||
|
|
<text class="title-text">巡查项目</text>
|
|||
|
|
</view>
|
|||
|
|
<view class="check-card bg-white r-md p-15">
|
|||
|
|
<template v-if="checkItems && checkItems.length > 0">
|
|||
|
|
<view class="check-list">
|
|||
|
|
<view
|
|||
|
|
v-for="(item, index) in checkItems"
|
|||
|
|
:key="item.id"
|
|||
|
|
class="check-item"
|
|||
|
|
>
|
|||
|
|
<view class="item-info flex-1">
|
|||
|
|
<!-- 项目名称单独一行 -->
|
|||
|
|
<text class="item-text"
|
|||
|
|
>{{ index + 1 }}、{{ item.xcMc }}</text
|
|||
|
|
>
|
|||
|
|
<!-- 分值和结果同一行 -->
|
|||
|
|
<view class="item-score-result">
|
|||
|
|
<text class="item-deduction mr-20">
|
|||
|
|
分值:{{ item.xmFz }}分
|
|||
|
|
</text>
|
|||
|
|
<view class="item-result">
|
|||
|
|
<radio-group
|
|||
|
|
:name="'result_' + item.id"
|
|||
|
|
class="item-radio-group"
|
|||
|
|
@change="onCheckItemChange($event, item)"
|
|||
|
|
>
|
|||
|
|
<label class="item-radio-label mr-10">
|
|||
|
|
<radio
|
|||
|
|
:value="'A'"
|
|||
|
|
:checked="item.checked === true"
|
|||
|
|
color="#4080ff"
|
|||
|
|
class="item-radio"
|
|||
|
|
/>
|
|||
|
|
<text class="ml-2">有</text>
|
|||
|
|
</label>
|
|||
|
|
<label class="item-radio-label">
|
|||
|
|
<radio
|
|||
|
|
:value="'B'"
|
|||
|
|
:checked="item.checked === false"
|
|||
|
|
color="#4080ff"
|
|||
|
|
class="item-radio"
|
|||
|
|
/>
|
|||
|
|
<text class="ml-2">无</text>
|
|||
|
|
</label>
|
|||
|
|
</radio-group>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</template>
|
|||
|
|
<template v-else>
|
|||
|
|
<view
|
|||
|
|
class="no-check-items"
|
|||
|
|
style="text-align: center; color: #999; padding: 20px 0"
|
|||
|
|
>
|
|||
|
|
暂无巡查项目
|
|||
|
|
</view>
|
|||
|
|
</template>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 拍照上传 -->
|
|||
|
|
<view class="section mx-15 mb-15">
|
|||
|
|
<view class="section-title-bar">
|
|||
|
|
<view class="decorator"></view>
|
|||
|
|
<text class="title-text">拍照上传</text>
|
|||
|
|
</view>
|
|||
|
|
<view class="upload-card bg-white r-md p-15">
|
|||
|
|
<view class="upload-section">
|
|||
|
|
<view class="upload-list">
|
|||
|
|
<view
|
|||
|
|
v-for="(image, index) in imageList"
|
|||
|
|
:key="index"
|
|||
|
|
class="upload-item"
|
|||
|
|
>
|
|||
|
|
<image
|
|||
|
|
:src="image.url ? imagUrl(image.url) : image.tempPath"
|
|||
|
|
mode="aspectFill"
|
|||
|
|
class="upload-image"
|
|||
|
|
@click="previewImage(index)"
|
|||
|
|
/>
|
|||
|
|
<view class="upload-delete" @click="deleteImage(index)">
|
|||
|
|
<text class="delete-icon">×</text>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
<view
|
|||
|
|
v-if="imageList.length < 5"
|
|||
|
|
class="upload-add"
|
|||
|
|
@click="chooseImage"
|
|||
|
|
>
|
|||
|
|
<text class="add-icon">+</text>
|
|||
|
|
<text class="add-text">添加图片</text>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 拍视频上传 -->
|
|||
|
|
<view class="section mx-15 mb-30">
|
|||
|
|
<view class="section-title-bar">
|
|||
|
|
<view class="decorator"></view>
|
|||
|
|
<text class="title-text">拍视频上传</text>
|
|||
|
|
</view>
|
|||
|
|
<view class="upload-card bg-white r-md p-15">
|
|||
|
|
<view class="upload-section">
|
|||
|
|
<view class="upload-list">
|
|||
|
|
<view
|
|||
|
|
v-for="(video, index) in videoList"
|
|||
|
|
:key="index"
|
|||
|
|
class="upload-item"
|
|||
|
|
>
|
|||
|
|
<video
|
|||
|
|
:src="video.url ? video.url : video.tempPath"
|
|||
|
|
class="upload-video"
|
|||
|
|
: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 class="video-play-icon">
|
|||
|
|
<u-icon name="play-right-fill" color="#fff" size="20"></u-icon>
|
|||
|
|
</view>
|
|||
|
|
<view class="upload-delete" @click="deleteVideo(index)">
|
|||
|
|
<text class="delete-icon">×</text>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
<view
|
|||
|
|
v-if="videoList.length < 3"
|
|||
|
|
class="upload-add"
|
|||
|
|
@click="chooseVideo"
|
|||
|
|
>
|
|||
|
|
<text class="add-icon">+</text>
|
|||
|
|
<text class="add-text">添加视频</text>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<template #bottom>
|
|||
|
|
<view
|
|||
|
|
v-if="canInspect"
|
|||
|
|
class="submit-btn-wrap py-10 px-20 bg-white"
|
|||
|
|
>
|
|||
|
|
<button class="submit-btn" @click="submit">提交巡查</button>
|
|||
|
|
</view>
|
|||
|
|
</template>
|
|||
|
|
</BasicLayout>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script setup lang="ts">
|
|||
|
|
import { xcXmFindByXcLxApi } from "@/api/base/xcXmApi";
|
|||
|
|
import { kyXcSaveApi } from "@/api/base/kyXcApi";
|
|||
|
|
import { attachmentUpload } from "@/api/system/upload";
|
|||
|
|
import BasicLayout from "@/components/BasicLayout/Layout.vue";
|
|||
|
|
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";
|
|||
|
|
import { showLoading, hideLoading, showToast } from "@/utils/uniapp";
|
|||
|
|
|
|||
|
|
const { getJs } = useUserStore();
|
|||
|
|
const { getData } = useDataStore();
|
|||
|
|
|
|||
|
|
const js = computed(() => getJs);
|
|||
|
|
const xkkc = computed(() => getData);
|
|||
|
|
|
|||
|
|
const now = dayjs();
|
|||
|
|
let wDay = now.day();
|
|||
|
|
if (wDay === 0) {
|
|||
|
|
wDay = 7;
|
|||
|
|
}
|
|||
|
|
const wdNameList = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"];
|
|||
|
|
const todayInfo = ref({
|
|||
|
|
date: now.format("YYYY-MM-DD"),
|
|||
|
|
weekName: wdNameList[wDay - 1],
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 巡查项目
|
|||
|
|
const checkItems = ref<any[]>([]);
|
|||
|
|
|
|||
|
|
// 图片列表 - 修改为包含临时路径和服务器路径的对象
|
|||
|
|
interface ImageItem {
|
|||
|
|
tempPath?: string; // 临时路径(用于预览)
|
|||
|
|
url?: string; // 服务器路径(上传成功后)
|
|||
|
|
name?: string; // 文件名
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 视频列表 - 修改为包含临时路径和服务器路径的对象
|
|||
|
|
interface VideoItem {
|
|||
|
|
tempPath?: string; // 临时路径(用于预览)
|
|||
|
|
url?: string; // 服务器路径(上传成功后)
|
|||
|
|
name?: string; // 文件名
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const imageList = ref<ImageItem[]>([]);
|
|||
|
|
const videoList = ref<VideoItem[]>([]);
|
|||
|
|
|
|||
|
|
// 巡查状态相关
|
|||
|
|
const inspectionStatusText = ref("");
|
|||
|
|
const canInspect = ref(true); // 是否可以巡查
|
|||
|
|
|
|||
|
|
// 加载巡查项目
|
|||
|
|
const loadCheckItems = async () => {
|
|||
|
|
try {
|
|||
|
|
const res = await xcXmFindByXcLxApi("课业辅导巡查");
|
|||
|
|
|
|||
|
|
if (res && res.resultCode === 1 && res.result && res.result.length > 0) {
|
|||
|
|
checkItems.value = res.result.map((item: any) => {
|
|||
|
|
return {
|
|||
|
|
id: item.id,
|
|||
|
|
xcMc: item.xcMc,
|
|||
|
|
xmFz: item.xmFz,
|
|||
|
|
xcLx: item.xcLx,
|
|||
|
|
checked: false,
|
|||
|
|
};
|
|||
|
|
});
|
|||
|
|
} else {
|
|||
|
|
checkItems.value = [];
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error("加载巡查项目失败:", error);
|
|||
|
|
checkItems.value = [];
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const onCheckItemChange = (e: any, item: any) => {
|
|||
|
|
item.checked = e.detail.value === "A";
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 选择图片
|
|||
|
|
const chooseImage = () => {
|
|||
|
|
uni.chooseImage({
|
|||
|
|
count: 5 - imageList.value.length,
|
|||
|
|
sizeType: ['compressed'],
|
|||
|
|
sourceType: ['album', 'camera'],
|
|||
|
|
success: async (res) => {
|
|||
|
|
// 添加临时图片到列表
|
|||
|
|
const tempFilePaths = res.tempFilePaths as string[];
|
|||
|
|
const newImages = tempFilePaths.map((path: string) => ({
|
|||
|
|
tempPath: path,
|
|||
|
|
name: path.split('/').pop() || 'image.jpg'
|
|||
|
|
}));
|
|||
|
|
imageList.value = [...imageList.value, ...newImages];
|
|||
|
|
// 自动上传图片
|
|||
|
|
await uploadImages(newImages);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 选择视频
|
|||
|
|
const chooseVideo = () => {
|
|||
|
|
uni.chooseVideo({
|
|||
|
|
sourceType: ['album', 'camera'],
|
|||
|
|
maxDuration: 60,
|
|||
|
|
camera: 'back',
|
|||
|
|
success: async (res) => {
|
|||
|
|
// 添加临时视频到列表
|
|||
|
|
const tempFilePath = res.tempFilePath;
|
|||
|
|
const newVideo = {
|
|||
|
|
tempPath: tempFilePath,
|
|||
|
|
name: tempFilePath.split('/').pop() || 'video.mp4'
|
|||
|
|
};
|
|||
|
|
videoList.value = [...videoList.value, newVideo];
|
|||
|
|
// 自动上传视频
|
|||
|
|
await uploadVideos([newVideo]);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 上传图片到服务器
|
|||
|
|
const uploadImages = async (images: ImageItem[]) => {
|
|||
|
|
try {
|
|||
|
|
showLoading('上传图片中...');
|
|||
|
|
|
|||
|
|
for (let i = 0; i < images.length; i++) {
|
|||
|
|
const image = images[i];
|
|||
|
|
if (image.tempPath) {
|
|||
|
|
try {
|
|||
|
|
// 调用上传接口
|
|||
|
|
const uploadResult: any = await attachmentUpload(image.tempPath as any);
|
|||
|
|
|
|||
|
|
if (uploadResult && uploadResult.resultCode === 1 && uploadResult.result && uploadResult.result.length > 0) {
|
|||
|
|
// 保存服务器返回的文件路径
|
|||
|
|
const serverPath = uploadResult.result[0].filePath;
|
|||
|
|
|
|||
|
|
// 更新图片对象,保存服务器路径
|
|||
|
|
const index = imageList.value.findIndex(img => img.tempPath === image.tempPath);
|
|||
|
|
if (index !== -1) {
|
|||
|
|
imageList.value[index].url = serverPath;
|
|||
|
|
// 可以删除临时路径以节省内存
|
|||
|
|
delete imageList.value[index].tempPath;
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
throw new Error('上传响应格式异常');
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('图片上传失败:', error);
|
|||
|
|
showToast({ title: `${image.name || '图片'}上传失败`, icon: 'none' });
|
|||
|
|
|
|||
|
|
// 从列表中移除上传失败的图片
|
|||
|
|
const index = imageList.value.findIndex(img => img.tempPath === image.tempPath);
|
|||
|
|
if (index !== -1) {
|
|||
|
|
imageList.value.splice(index, 1);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
hideLoading();
|
|||
|
|
showToast({ title: '图片上传完成', icon: 'success' });
|
|||
|
|
} catch (error) {
|
|||
|
|
hideLoading();
|
|||
|
|
console.error('批量上传图片失败:', error);
|
|||
|
|
showToast({ title: '图片上传失败,请重试', icon: 'none' });
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 上传视频到服务器
|
|||
|
|
const uploadVideos = async (videos: VideoItem[]) => {
|
|||
|
|
try {
|
|||
|
|
showLoading('上传视频中...');
|
|||
|
|
|
|||
|
|
for (let i = 0; i < videos.length; i++) {
|
|||
|
|
const video = videos[i];
|
|||
|
|
if (video.tempPath) {
|
|||
|
|
try {
|
|||
|
|
// 调用上传接口
|
|||
|
|
const uploadResult: any = await attachmentUpload(video.tempPath as any);
|
|||
|
|
|
|||
|
|
if (uploadResult && uploadResult.resultCode === 1 && uploadResult.result && uploadResult.result.length > 0) {
|
|||
|
|
// 保存服务器返回的文件路径
|
|||
|
|
const serverPath = uploadResult.result[0].filePath;
|
|||
|
|
|
|||
|
|
// 更新视频对象,保存服务器路径
|
|||
|
|
const index = videoList.value.findIndex(v => v.tempPath === video.tempPath);
|
|||
|
|
if (index !== -1) {
|
|||
|
|
videoList.value[index].url = serverPath;
|
|||
|
|
// 可以删除临时路径以节省内存
|
|||
|
|
delete videoList.value[index].tempPath;
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
throw new Error('上传响应格式异常');
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('视频上传失败:', error);
|
|||
|
|
showToast({ title: `${video.name || '视频'}上传失败`, icon: 'none' });
|
|||
|
|
|
|||
|
|
// 从列表中移除上传失败的视频
|
|||
|
|
const index = videoList.value.findIndex(v => v.tempPath === video.tempPath);
|
|||
|
|
if (index !== -1) {
|
|||
|
|
videoList.value.splice(index, 1);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
hideLoading();
|
|||
|
|
showToast({ title: '视频上传完成', icon: 'success' });
|
|||
|
|
} catch (error) {
|
|||
|
|
hideLoading();
|
|||
|
|
console.error('批量上传视频失败:', error);
|
|||
|
|
showToast({ title: '视频上传失败,请重试', icon: 'none' });
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 预览图片
|
|||
|
|
const previewImage = (index: number) => {
|
|||
|
|
const urls = imageList.value.map(img =>
|
|||
|
|
img.url ? imagUrl(img.url) : img.tempPath
|
|||
|
|
).filter((url): url is string => !!url);
|
|||
|
|
|
|||
|
|
uni.previewImage({
|
|||
|
|
urls: urls,
|
|||
|
|
current: index
|
|||
|
|
});
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 删除图片
|
|||
|
|
const deleteImage = (index: number) => {
|
|||
|
|
imageList.value.splice(index, 1);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 删除视频
|
|||
|
|
const deleteVideo = (index: number) => {
|
|||
|
|
videoList.value.splice(index, 1);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 检查巡查时间 - 课业辅导巡查不需要时间验证
|
|||
|
|
const checkInspectionTime = () => {
|
|||
|
|
// 课业辅导巡查直接允许巡查,不需要时间验证
|
|||
|
|
canInspect.value = true;
|
|||
|
|
inspectionStatusText.value = "可以巡查";
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 提交数据
|
|||
|
|
const submit = async () => {
|
|||
|
|
if (!canInspect.value) {
|
|||
|
|
uni.showToast({
|
|||
|
|
title: inspectionStatusText.value,
|
|||
|
|
icon: "none",
|
|||
|
|
duration: 2000,
|
|||
|
|
});
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 验证巡查项目是否已选择
|
|||
|
|
const hasCheckedItems = checkItems.value.some(item => item.checked !== undefined);
|
|||
|
|
if (!hasCheckedItems) {
|
|||
|
|
uni.showToast({
|
|||
|
|
title: "请至少选择一个巡查项目",
|
|||
|
|
icon: "none",
|
|||
|
|
duration: 2000,
|
|||
|
|
});
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// 构建巡查项目列表
|
|||
|
|
const xkXcXmList = checkItems.value.map((item: any) => {
|
|||
|
|
const newItem = {
|
|||
|
|
...item,
|
|||
|
|
xcXmId: item.id,
|
|||
|
|
xcJg: item.checked ? "A" : "B",
|
|||
|
|
xkXcId: "", // 这个会在后端设置
|
|||
|
|
};
|
|||
|
|
newItem.id = "";
|
|||
|
|
return newItem;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const submitData = {
|
|||
|
|
jsId: js.value.id,
|
|||
|
|
jsxm: js.value.xm || js.value.jsxm, // 添加教师姓名
|
|||
|
|
njId: xkkc.value.njId || '', // 确保年级ID不为空
|
|||
|
|
njmcId: xkkc.value.njmcId || '', // 确保年级名称ID不为空
|
|||
|
|
bjId: xkkc.value.bjId || '', // 确保班级ID不为空
|
|||
|
|
xctime: now.format("YYYY-MM-DD HH:mm:ss"),
|
|||
|
|
zp: getImageUrls(),
|
|||
|
|
sp: getVideoUrls(),
|
|||
|
|
kyXcXmList: xkXcXmList, // 添加巡查项目列表
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const res = await kyXcSaveApi(submitData);
|
|||
|
|
|
|||
|
|
if (res && res.resultCode === 1) {
|
|||
|
|
uni.showToast({
|
|||
|
|
title: "提交成功",
|
|||
|
|
icon: "success",
|
|||
|
|
});
|
|||
|
|
setTimeout(() => {
|
|||
|
|
uni.navigateBack();
|
|||
|
|
}, 1500);
|
|||
|
|
} else {
|
|||
|
|
uni.showToast({
|
|||
|
|
title: "提交失败",
|
|||
|
|
icon: "none",
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('提交巡查失败:', error);
|
|||
|
|
uni.showToast({
|
|||
|
|
title: "提交失败",
|
|||
|
|
icon: "none",
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 获取图片URL列表 - 转换为逗号分隔的字符串
|
|||
|
|
const getImageUrls = () => {
|
|||
|
|
const urls = imageList.value
|
|||
|
|
.filter(img => img.url) // 只返回有url的图片
|
|||
|
|
.map(img => img.url)
|
|||
|
|
.filter((url): url is string => !!url); // 过滤掉undefined和null
|
|||
|
|
|
|||
|
|
// 返回逗号分隔的字符串,如果没有图片则返回空字符串
|
|||
|
|
return urls.length > 0 ? urls.join(',') : '';
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 获取视频URL列表 - 转换为逗号分隔的字符串
|
|||
|
|
const getVideoUrls = () => {
|
|||
|
|
const urls = videoList.value
|
|||
|
|
.filter(video => video.url) // 只返回有url的视频
|
|||
|
|
.map(video => video.url)
|
|||
|
|
.filter((url): url is string => !!url); // 过滤掉undefined和null
|
|||
|
|
|
|||
|
|
// 返回逗号分隔的字符串,如果没有视频则返回空字符串
|
|||
|
|
return urls.length > 0 ? urls.join(',') : '';
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 页面加载时获取状态选项
|
|||
|
|
onMounted(async () => {
|
|||
|
|
await loadCheckItems(); // 加载巡查项目
|
|||
|
|
checkInspectionTime();
|
|||
|
|
});
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style scoped lang="scss">
|
|||
|
|
.container {
|
|||
|
|
min-height: 100vh;
|
|||
|
|
background-color: #f5f5f5;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.bg-white {
|
|||
|
|
background-color: white;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.pending-inspection {
|
|||
|
|
padding-top: 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.course-card {
|
|||
|
|
border-radius: 8px;
|
|||
|
|
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.course-icon {
|
|||
|
|
width: 30px;
|
|||
|
|
height: 30px;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
background-color: rgba(64, 128, 255, 0.1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.course-time-info {
|
|||
|
|
margin-top: 15px;
|
|||
|
|
padding: 10px 15px;
|
|||
|
|
background-color: #f9f9f9;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
border: 1px solid #eee;
|
|||
|
|
|
|||
|
|
.time-item {
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
margin-bottom: 5px;
|
|||
|
|
|
|||
|
|
.time-label {
|
|||
|
|
font-size: 14px;
|
|||
|
|
color: #666;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.time-value {
|
|||
|
|
font-size: 14px;
|
|||
|
|
font-weight: bold;
|
|||
|
|
color: #333;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.inspection-status {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
margin-top: 15px;
|
|||
|
|
padding: 10px 15px;
|
|||
|
|
background-color: #fffbe6;
|
|||
|
|
border: 1px solid #ffe58f;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
color: #faad14;
|
|||
|
|
font-size: 14px;
|
|||
|
|
|
|||
|
|
.status-text {
|
|||
|
|
margin-left: 5px;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.section {
|
|||
|
|
.section-title-bar {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
margin-bottom: 10px;
|
|||
|
|
|
|||
|
|
.decorator {
|
|||
|
|
width: 4px;
|
|||
|
|
height: 16px;
|
|||
|
|
background-color: #4080ff;
|
|||
|
|
margin-right: 8px;
|
|||
|
|
border-radius: 2px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.title-text {
|
|||
|
|
font-size: 16px;
|
|||
|
|
font-weight: bold;
|
|||
|
|
color: #333;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.check-card {
|
|||
|
|
background-color: white;
|
|||
|
|
|
|||
|
|
.check-item {
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
align-items: center;
|
|||
|
|
padding: 12px 0;
|
|||
|
|
border-bottom: 1px solid #eee;
|
|||
|
|
|
|||
|
|
.item-score-result {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
&:first-child {
|
|||
|
|
padding-top: 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
&:last-child {
|
|||
|
|
border-bottom: none;
|
|||
|
|
padding-bottom: 0;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.item-info {
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.item-text {
|
|||
|
|
font-size: 14px;
|
|||
|
|
color: #333;
|
|||
|
|
margin-bottom: 4px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.item-deduction {
|
|||
|
|
font-size: 12px;
|
|||
|
|
color: #999;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.upload-card {
|
|||
|
|
background-color: white;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.upload-section {
|
|||
|
|
.upload-list {
|
|||
|
|
display: flex;
|
|||
|
|
flex-wrap: wrap;
|
|||
|
|
gap: 8px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.upload-item {
|
|||
|
|
position: relative;
|
|||
|
|
width: 80px;
|
|||
|
|
height: 80px;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
overflow: hidden;
|
|||
|
|
border: 1px solid #e9ecef;
|
|||
|
|
|
|||
|
|
.upload-image {
|
|||
|
|
width: 100%;
|
|||
|
|
height: 100%;
|
|||
|
|
object-fit: cover;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.upload-video {
|
|||
|
|
width: 100%;
|
|||
|
|
height: 100%;
|
|||
|
|
object-fit: cover;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.video-play-icon {
|
|||
|
|
position: absolute;
|
|||
|
|
top: 50%;
|
|||
|
|
left: 50%;
|
|||
|
|
transform: translate(-50%, -50%);
|
|||
|
|
width: 32px;
|
|||
|
|
height: 32px;
|
|||
|
|
background-color: rgba(0, 0, 0, 0.6);
|
|||
|
|
border-radius: 50%;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.upload-delete {
|
|||
|
|
position: absolute;
|
|||
|
|
top: 4px;
|
|||
|
|
right: 4px;
|
|||
|
|
width: 20px;
|
|||
|
|
height: 20px;
|
|||
|
|
background-color: rgba(0, 0, 0, 0.6);
|
|||
|
|
border-radius: 50%;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
|
|||
|
|
.delete-icon {
|
|||
|
|
color: #ffffff;
|
|||
|
|
font-size: 14px;
|
|||
|
|
font-weight: bold;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.upload-add {
|
|||
|
|
width: 80px;
|
|||
|
|
height: 80px;
|
|||
|
|
border: 2px dashed #e9ecef;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
background-color: #f8f9fa;
|
|||
|
|
cursor: pointer;
|
|||
|
|
|
|||
|
|
.add-icon {
|
|||
|
|
font-size: 24px;
|
|||
|
|
color: #6c757d;
|
|||
|
|
margin-bottom: 4px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.add-text {
|
|||
|
|
font-size: 12px;
|
|||
|
|
color: #6c757d;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.submit-btn {
|
|||
|
|
background-color: #4080ff;
|
|||
|
|
color: #fff;
|
|||
|
|
height: 44px;
|
|||
|
|
border-radius: 22px;
|
|||
|
|
font-size: 16px;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.cor-primary {
|
|||
|
|
color: #4080ff;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.cor-warning {
|
|||
|
|
color: #ff9900;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.cor-danger {
|
|||
|
|
color: #ff4d4f;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.cor-666 {
|
|||
|
|
color: #666;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.cor-999 {
|
|||
|
|
color: #999;
|
|||
|
|
}
|
|||
|
|
</style>
|
|||
|
|
|
|||
|
|
|