850 lines
23 KiB
Vue
850 lines
23 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"
|
||
:class="{ 'submit-btn-disabled': isSubmitting }"
|
||
:disabled="isSubmitting"
|
||
@click="submit"
|
||
>
|
||
{{ isSubmitting ? '提交中...' : '提交巡查' }}
|
||
</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 isSubmitting = ref(false);
|
||
|
||
// 加载巡查项目
|
||
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 (isSubmitting.value) {
|
||
uni.showToast({
|
||
title: "正在提交中,请勿重复点击",
|
||
icon: "none",
|
||
duration: 1500,
|
||
});
|
||
return;
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
// 设置提交状态
|
||
isSubmitting.value = true;
|
||
|
||
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不为空
|
||
pbJsId: xkkc.value.pbJsId || xkkc.value.id, // 使用传递的pbJsId或课程记录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",
|
||
});
|
||
} finally {
|
||
// 重置提交状态
|
||
isSubmitting.value = false;
|
||
}
|
||
};
|
||
|
||
// 获取图片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;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.submit-btn-disabled {
|
||
background-color: #d9d9d9 !important;
|
||
color: #999 !important;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
.cor-primary {
|
||
color: #4080ff;
|
||
}
|
||
|
||
.cor-warning {
|
||
color: #ff9900;
|
||
}
|
||
|
||
.cor-danger {
|
||
color: #ff4d4f;
|
||
}
|
||
|
||
.cor-666 {
|
||
color: #666;
|
||
}
|
||
|
||
.cor-999 {
|
||
color: #999;
|
||
}
|
||
</style>
|
||
|
||
|