完成中转仓管理

This commit is contained in:
xuqiuyun
2025-12-08 15:24:43 +08:00
parent e968fcf52a
commit 620975c04d
981 changed files with 154245 additions and 83 deletions

View File

@@ -0,0 +1,975 @@
<template>
<el-dialog
v-model="data.dialogVisible"
:title="data.isDetail ? '进仓详情' : (data.editId ? '编辑进仓记录' : '新增进仓记录')"
:before-close="handleClose"
width="900px"
:close-on-click-modal="false"
>
<el-form ref="formDataRef" :model="ruleForm" :rules="rules" label-width="120px" :disabled="data.isDetail">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="中转仓" prop="warehouseId">
<el-select
v-model="ruleForm.warehouseId"
placeholder="请选择中转仓"
clearable
filterable
style="width: 100%"
@change="handleWarehouseChange"
>
<el-option
v-for="item in warehouseList"
:key="item.id"
:label="item.warehouseName"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="运送清单" prop="deliveryId">
<el-select
v-model="ruleForm.deliveryId"
placeholder="请选择运送清单"
clearable
filterable
style="width: 100%"
@change="handleDeliveryChange"
>
<el-option
v-for="item in deliveryList"
:key="item.id"
:label="`${item.deliveryNumber || '--'} - ${item.licensePlate || '--'}`"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="订单" prop="orderId">
<el-select
v-model="ruleForm.orderId"
placeholder="请选择订单(可多选)"
multiple
clearable
filterable
style="width: 100%"
>
<el-option
v-for="item in orderList"
:key="item.id"
:label="`订单${item.id} - 单价: ${item.firmPrice || '--'}元/斤`"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="进仓时间" prop="inTime">
<el-date-picker
v-model="ruleForm.inTime"
type="datetime"
placeholder="请选择进仓时间"
style="width: 100%"
value-format="YYYY-MM-DD HH:mm:ss"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="来源地" prop="sourceLocation">
<el-input
v-model="ruleForm.sourceLocation"
placeholder="请输入来源地"
maxlength="255"
style="width: calc(100% - 100px); margin-right: 10px;"
/>
<el-button type="primary" @click="openSourceLocationMap">选择位置</el-button>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="来源地经度">
<el-input v-model="ruleForm.sourceLon" placeholder="经度" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="来源地纬度">
<el-input v-model="ruleForm.sourceLat" placeholder="纬度" disabled />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="牛只数量(头)" prop="cattleCount">
<el-input-number
v-model="ruleForm.cattleCount"
:min="1"
:max="9999"
placeholder="请输入牛只数量"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="重量(公斤)" prop="weight">
<el-input-number
v-model="ruleForm.weight"
:min="0"
:precision="2"
placeholder="请输入重量"
style="width: 100%"
>
<template #append>kg</template>
</el-input-number>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="状态" prop="status">
<el-select v-model="ruleForm.status" placeholder="请选择状态" style="width: 100%">
<el-option label="待进仓" :value="1" />
<el-option label="已进仓" :value="2" />
<el-option label="已出仓" :value="3" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="照片">
<div style="display: flex; flex-direction: column; gap: 10px;">
<!-- 拖拽上传区域 -->
<el-upload
drag
action="#"
:auto-upload="false"
:before-upload="beforePhotoUpload"
:limit="9"
accept="image/*"
:on-change="handlePhotoChange"
:show-file-list="false"
style="width: 100%"
>
<el-icon class="el-icon--upload"><UploadFilled /></el-icon>
<div class="el-upload__text">将图片文件拖到此处<em>点击上传</em></div>
<template #tip>
<div class="el-upload__tip">支持 jpg/png/gif 格式单个文件不超过 10MB最多上传 9 </div>
</template>
</el-upload>
<!-- 图片预览列表 -->
<div v-if="photoFileList.length > 0" class="photo-preview-list">
<div
v-for="(file, index) in photoFileList"
:key="index"
class="photo-preview-item"
>
<el-image
:src="file.url || file.response?.data?.url || ''"
fit="cover"
class="photo-preview-image"
:preview-src-list="photoFileList.map(f => f.url || f.response?.data?.url || '').filter(Boolean)"
:initial-index="index"
/>
<el-button
type="danger"
:icon="Delete"
circle
size="small"
class="photo-delete-btn"
@click="handlePhotoRemove(file)"
/>
</div>
</div>
</div>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="视频">
<el-upload
drag
action="#"
:auto-upload="false"
:on-change="handleVideoChange"
:on-remove="handleVideoRemove"
:before-upload="beforeVideoUpload"
:limit="3"
accept="video/*"
:show-file-list="false"
style="width: 100%"
>
<el-icon class="el-icon--upload"><UploadFilled /></el-icon>
<div class="el-upload__text">将视频文件拖到此处,或<em>点击上传</em></div>
<template #tip>
<div class="el-upload__tip">支持 mp4/avi/rmvb/mkv 格式,单个文件不超过 100MB最多上传 3 个</div>
</template>
</el-upload>
<!-- 视频预览列表 -->
<div v-if="videoFileList.length > 0" class="video-preview-list">
<div
v-for="(file, index) in videoFileList"
:key="index"
class="video-preview-item"
>
<video
:src="file.url || file.response?.data?.url || ''"
controls
class="video-preview-player"
></video>
<div class="video-name">{{ file.name || '视频' }}</div>
<el-button
type="danger"
:icon="Delete"
circle
size="small"
class="video-delete-btn"
@click="handleVideoRemove(file)"
/>
</div>
</div>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="备注">
<el-input
v-model="ruleForm.remark"
type="textarea"
:rows="3"
placeholder="请输入备注"
maxlength="500"
show-word-limit
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button v-if="!data.isDetail" :loading="data.saveLoading" type="primary" @click="onClickSave">
保存
</el-button>
<el-button @click="handleClose">{{ data.isDetail ? '关闭' : '取消' }}</el-button>
</span>
</template>
</el-dialog>
<!-- 来源地地址选择地图 -->
<el-dialog v-model="showSourceLocationMap" title="选择来源地地址" width="900px">
<baidu-map
class="map"
:center="ruleForm.sourceLon && ruleForm.sourceLat ? {lng: parseFloat(ruleForm.sourceLon), lat: parseFloat(ruleForm.sourceLat)} : {lng: 116.404, lat: 39.915}"
:zoom="15"
:scroll-wheel-zoom="true"
@click="handleSourceLocationClick"
style="height: 500px"
>
<bm-marker
v-if="ruleForm.sourceLon && ruleForm.sourceLat"
:position="{lng: parseFloat(ruleForm.sourceLon), lat: parseFloat(ruleForm.sourceLat)}"
:dragging="true"
@dragging="handleSourceMarkerDrag"
/>
<bm-map-type :map-types="['BMAP_NORMAL_MAP', 'BMAP_HYBRID_MAP']"></bm-map-type>
</baidu-map>
<template #footer>
<span class="dialog-footer">
<el-button @click="showSourceLocationMap = false">取消</el-button>
<el-button type="primary" @click="showSourceLocationMap = false">确定</el-button>
</span>
</template>
</el-dialog>
<!-- 图片预览 -->
<el-dialog v-model="showImageViewer" title="图片预览" width="800px">
<el-image :src="imageViewerUrl" style="width: 100%" fit="contain" />
</el-dialog>
</template>
<script setup>
import { ref, reactive, watch } from 'vue';
import { ElMessage } from 'element-plus';
import { Plus, UploadFilled, Delete } from '@element-plus/icons-vue';
import { BaiduMap, BmMapType, BmMarker } from 'vue-baidu-map-3x';
import { warehouseInAdd, warehouseInEdit, warehouseInDetail } from '@/api/warehouseIn.js';
import { warehouseAll } from '@/api/warehouse.js';
import { orderPageQuery, shippingList } from '@/api/shipping.js';
const emits = defineEmits(['success']);
const formDataRef = ref(null);
const showSourceLocationMap = ref(false);
const showImageViewer = ref(false);
const imageViewerUrl = ref('');
const photoFileList = ref([]);
const videoFileList = ref([]);
const warehouseList = ref([]);
const orderList = ref([]);
const deliveryList = ref([]);
const data = reactive({
dialogVisible: false,
saveLoading: false,
editId: null,
isDetail: false,
});
const ruleForm = reactive({
id: null,
warehouseId: null,
orderId: [],
deliveryId: null,
sourceLocation: '',
sourceLon: '',
sourceLat: '',
cattleCount: null,
weight: null,
inTime: '',
photos: '',
videos: '',
remark: '',
status: 1,
});
const rules = reactive({
warehouseId: [
{ required: true, message: '请选择中转仓', trigger: 'change' },
],
deliveryId: [
{ required: true, message: '请选择运送清单', trigger: 'change' },
],
cattleCount: [
{ required: true, message: '请输入牛只数量', trigger: 'blur' },
{ type: 'number', min: 1, message: '牛只数量必须大于0', trigger: 'blur' },
],
inTime: [
{ required: true, message: '请选择进仓时间', trigger: 'change' },
],
});
// 加载中转仓列表
const loadWarehouseList = async () => {
try {
const res = await warehouseAll();
if (res.code === 200) {
warehouseList.value = res.data || [];
}
} catch (error) {
console.error('加载中转仓列表失败', error);
}
};
// 加载订单列表
const loadOrderList = async () => {
try {
const res = await orderPageQuery({ pageNum: 1, pageSize: 1000 });
if (res.code === 200) {
const responseData = res.data || res;
if (responseData && typeof responseData === 'object' && 'rows' in responseData) {
orderList.value = responseData.rows || [];
} else if (responseData && responseData.data && 'rows' in responseData.data) {
orderList.value = responseData.data.rows || [];
} else {
orderList.value = [];
}
}
} catch (error) {
console.error('加载订单列表失败', error);
}
};
// 加载运送清单列表
const loadDeliveryList = async () => {
try {
const res = await shippingList({ pageNum: 1, pageSize: 1000 });
if (res.code === 200) {
const responseData = res.data || res;
if (responseData && typeof responseData === 'object' && 'rows' in responseData) {
deliveryList.value = responseData.rows || [];
} else if (responseData && responseData.data && 'rows' in responseData.data) {
deliveryList.value = responseData.data.rows || [];
} else {
deliveryList.value = [];
}
}
} catch (error) {
console.error('加载运送清单列表失败', error);
}
};
const handleClose = () => {
if (formDataRef.value) {
formDataRef.value.resetFields();
}
Object.assign(ruleForm, {
id: null,
warehouseId: null,
orderId: [],
deliveryId: null,
sourceLocation: '',
sourceLon: '',
sourceLat: '',
cattleCount: null,
weight: null,
inTime: '',
photos: '',
videos: '',
remark: '',
status: 1,
});
photoFileList.value = [];
videoFileList.value = [];
data.editId = null;
data.isDetail = false;
data.dialogVisible = false;
};
const onClickSave = () => {
if (!formDataRef.value) {
return;
}
formDataRef.value.validate((valid) => {
if (!valid) {
return false;
}
data.saveLoading = true;
// 处理订单ID多个订单用逗号分隔
const orderIdStr = ruleForm.orderId && ruleForm.orderId.length > 0
? ruleForm.orderId.join(',')
: null;
// 处理照片和视频URL多个用逗号分隔
const photosStr = photoFileList.value.map(file => file.url || file.response?.data?.url || '').filter(url => url).join(',');
const videosStr = videoFileList.value.map(file => file.url || file.response?.data?.url || '').filter(url => url).join(',');
const params = {
warehouseId: ruleForm.warehouseId,
orderId: orderIdStr,
deliveryId: ruleForm.deliveryId,
sourceLocation: ruleForm.sourceLocation,
sourceLon: ruleForm.sourceLon,
sourceLat: ruleForm.sourceLat,
cattleCount: ruleForm.cattleCount,
weight: ruleForm.weight,
inTime: ruleForm.inTime,
photos: photosStr,
videos: videosStr,
remark: ruleForm.remark,
};
if (data.editId) {
// 编辑
params.id = data.editId;
params.status = ruleForm.status;
warehouseInEdit(params)
.then((res) => {
if (res.code === 200) {
ElMessage.success('编辑成功');
handleClose();
emits('success');
} else {
ElMessage.error(res.msg || '编辑失败');
}
})
.catch((error) => {
ElMessage.error('编辑失败:' + (error.message || '未知错误'));
})
.finally(() => {
data.saveLoading = false;
});
} else {
// 新增
warehouseInAdd(params)
.then((res) => {
if (res.code === 200) {
ElMessage.success('新增成功');
handleClose();
emits('success');
} else {
ElMessage.error(res.msg || '新增失败');
}
})
.catch((error) => {
ElMessage.error('新增失败:' + (error.message || '未知错误'));
})
.finally(() => {
data.saveLoading = false;
});
}
});
};
const onShowDialog = (row, isDetail = false) => {
data.isDetail = isDetail || false;
data.editId = null;
if (row) {
data.editId = row.id;
// 如果是详情模式,先获取详情数据
if (isDetail) {
warehouseInDetail(row.id)
.then((res) => {
if (res.code === 200 && res.data) {
const detailData = res.data;
Object.assign(ruleForm, {
id: detailData.id,
warehouseId: detailData.warehouseId,
orderId: detailData.orderId ? detailData.orderId.split(',').map(id => parseInt(id)) : [],
deliveryId: detailData.deliveryId,
sourceLocation: detailData.sourceLocation || '',
sourceLon: detailData.sourceLon || '',
sourceLat: detailData.sourceLat || '',
cattleCount: detailData.cattleCount,
weight: detailData.weight,
inTime: detailData.inTime || '',
photos: detailData.photos || '',
videos: detailData.videos || '',
remark: detailData.remark || '',
status: detailData.status !== undefined ? detailData.status : 1,
});
// 处理照片和视频文件列表
if (detailData.photos) {
photoFileList.value = detailData.photos.split(',').map(url => ({ url, name: 'photo' }));
}
if (detailData.videos) {
videoFileList.value = detailData.videos.split(',').map(url => ({ url, name: 'video' }));
}
}
})
.catch((error) => {
ElMessage.error('获取详情失败:' + (error.message || '未知错误'));
});
} else {
// 编辑模式,直接使用传入的数据
Object.assign(ruleForm, {
id: row.id,
warehouseId: row.warehouseId,
orderId: row.orderId ? row.orderId.split(',').map(id => parseInt(id)) : [],
deliveryId: row.deliveryId,
sourceLocation: row.sourceLocation || '',
sourceLon: row.sourceLon || '',
sourceLat: row.sourceLat || '',
cattleCount: row.cattleCount,
weight: row.weight,
inTime: row.inTime || '',
photos: row.photos || '',
videos: row.videos || '',
remark: row.remark || '',
status: row.status !== undefined ? row.status : 1,
});
// 处理照片和视频文件列表
if (row.photos) {
photoFileList.value = row.photos.split(',').map(url => ({ url, name: 'photo' }));
}
if (row.videos) {
videoFileList.value = row.videos.split(',').map(url => ({ url, name: 'video' }));
}
}
} else {
// 新增模式,重置表单
Object.assign(ruleForm, {
id: null,
warehouseId: null,
orderId: [],
deliveryId: null,
sourceLocation: '',
sourceLon: '',
sourceLat: '',
cattleCount: null,
weight: null,
inTime: '',
photos: '',
videos: '',
remark: '',
status: 1,
});
photoFileList.value = [];
videoFileList.value = [];
}
data.dialogVisible = true;
};
// 中转仓选择变化
const handleWarehouseChange = (warehouseId) => {
// 可以在这里添加逻辑
};
// 运送清单选择变化
const handleDeliveryChange = (deliveryId) => {
if (!deliveryId) {
// 清空相关字段
ruleForm.sourceLocation = '';
ruleForm.sourceLon = '';
ruleForm.sourceLat = '';
ruleForm.cattleCount = null;
ruleForm.weight = null;
return;
}
// 从 deliveryList 中找到对应的运送清单
const selectedDelivery = deliveryList.value.find(item => item.id === deliveryId);
if (selectedDelivery) {
// 自动填充来源地信息
if (selectedDelivery.startLocation) {
ruleForm.sourceLocation = selectedDelivery.startLocation;
}
if (selectedDelivery.startLon) {
ruleForm.sourceLon = selectedDelivery.startLon;
}
if (selectedDelivery.startLat) {
ruleForm.sourceLat = selectedDelivery.startLat;
}
// 自动填充牛只数量
if (selectedDelivery.ratedQuantity) {
ruleForm.cattleCount = selectedDelivery.ratedQuantity;
}
// 自动计算重量entruckWeight - emptyWeight
if (selectedDelivery.entruckWeight && selectedDelivery.emptyWeight) {
const entruckWeight = parseFloat(selectedDelivery.entruckWeight) || 0;
const emptyWeight = parseFloat(selectedDelivery.emptyWeight) || 0;
const calculatedWeight = entruckWeight - emptyWeight;
if (calculatedWeight > 0) {
ruleForm.weight = parseFloat(calculatedWeight.toFixed(2));
}
} else if (selectedDelivery.entruckWeight) {
// 如果只有装车重量,也可以填充
ruleForm.weight = parseFloat(selectedDelivery.entruckWeight) || null;
}
}
};
// 打开来源地地图选择地址
const openSourceLocationMap = () => {
if (ruleForm.sourceLocation && ruleForm.sourceLocation.trim()) {
showSourceLocationMap.value = true;
setTimeout(() => {
if (window.BMap && window.BMap.Geocoder) {
const geocoder = new window.BMap.Geocoder();
geocoder.getPoint(ruleForm.sourceLocation, (point) => {
if (point) {
ruleForm.sourceLon = point.lng;
ruleForm.sourceLat = point.lat;
ElMessage.success('已定位到该地址');
} else {
ElMessage.warning('未找到该地址,请在地图上手动选择');
}
});
}
}, 500);
} else {
showSourceLocationMap.value = true;
}
};
// 地图点击事件
const handleSourceLocationClick = (e) => {
ruleForm.sourceLon = e.point.lng;
ruleForm.sourceLat = e.point.lat;
if (window.BMap && window.BMap.Geocoder) {
const geocoder = new window.BMap.Geocoder();
geocoder.getLocation(e.point, (res) => {
if (res) {
ruleForm.sourceLocation = res.address;
ElMessage.success('已设置来源地地址');
}
});
}
};
// 标记拖拽事件
const handleSourceMarkerDrag = (e) => {
ruleForm.sourceLon = e.point.lng;
ruleForm.sourceLat = e.point.lat;
if (window.BMap && window.BMap.Geocoder) {
const geocoder = new window.BMap.Geocoder();
geocoder.getLocation(e.point, (res) => {
if (res) {
ruleForm.sourceLocation = res.address;
}
});
}
};
// 图片预览(保留用于兼容)
const handlePictureCardPreview = (file) => {
imageViewerUrl.value = file.url || file.response?.data?.url || '';
showImageViewer.value = true;
};
// 照片文件变化处理(拖拽上传时触发)
const handlePhotoChange = (file, fileList) => {
// 验证文件
if (!beforePhotoUpload(file)) {
return;
}
// 手动上传文件
uploadPhotoFile(file);
};
// 手动上传照片文件
const uploadPhotoFile = async (file) => {
const formData = new FormData();
formData.append('file', file.raw || file);
try {
// 获取 token
let token = '';
const userStore = localStorage.getItem('userStore');
if (userStore) {
const us = JSON.parse(userStore);
token = us.token || '';
}
const response = await fetch('/api/common/upload', {
method: 'POST',
headers: {
'Authorization': token,
},
body: formData,
});
const result = await response.json();
if (result.code === 200) {
const photoUrl = result.data?.url || result.data;
photoFileList.value.push({
url: photoUrl,
name: file.name,
uid: file.uid || Date.now(),
});
ElMessage.success('照片上传成功');
} else {
ElMessage.error(result.msg || '照片上传失败');
}
} catch (error) {
console.error('照片上传失败:', error);
ElMessage.error('照片上传失败');
}
};
// 删除照片
const handlePhotoRemove = (file) => {
const index = photoFileList.value.findIndex(item => item.uid === file.uid || item.url === file.url);
if (index > -1) {
photoFileList.value.splice(index, 1);
}
};
// 视频文件变化处理(拖拽上传时触发)
const handleVideoChange = (file, fileList) => {
// 验证文件
if (!beforeVideoUpload(file)) {
return;
}
// 手动上传文件
uploadVideoFile(file);
};
// 手动上传视频文件
const uploadVideoFile = async (file) => {
const formData = new FormData();
formData.append('file', file.raw || file);
try {
// 获取 token
let token = '';
const userStore = localStorage.getItem('userStore');
if (userStore) {
const us = JSON.parse(userStore);
token = us.token || '';
}
const response = await fetch('/api/common/upload', {
method: 'POST',
headers: {
'Authorization': token,
},
body: formData,
});
const result = await response.json();
if (result.code === 200) {
const videoUrl = result.data?.url || result.data;
videoFileList.value.push({
url: videoUrl,
name: file.name,
uid: file.uid || Date.now(),
});
ElMessage.success('视频上传成功');
} else {
ElMessage.error(result.msg || '视频上传失败');
}
} catch (error) {
console.error('视频上传失败:', error);
ElMessage.error('视频上传失败');
}
};
// 删除视频
const handleVideoRemove = (file) => {
const index = videoFileList.value.findIndex(item => item.uid === file.uid || item.url === file.url);
if (index > -1) {
videoFileList.value.splice(index, 1);
}
};
// 上传前验证照片
const beforePhotoUpload = (file) => {
const isImage = file.type.startsWith('image/');
const isLt10M = file.size / 1024 / 1024 < 10;
if (!isImage) {
ElMessage.error('只能上传图片文件!');
return false;
}
if (!isLt10M) {
ElMessage.error('图片大小不能超过 10MB!');
return false;
}
// 检查数量限制
if (photoFileList.value.length >= 9) {
ElMessage.error('最多只能上传 9 张照片!');
return false;
}
return true; // 允许继续处理
};
// 上传前验证视频
const beforeVideoUpload = (file) => {
const isVideo = file.type.startsWith('video/');
const isLt100M = file.size / 1024 / 1024 < 100;
if (!isVideo) {
ElMessage.error('只能上传视频文件!');
return false;
}
if (!isLt100M) {
ElMessage.error('视频大小不能超过 100MB!');
return false;
}
// 检查数量限制
if (videoFileList.value.length >= 3) {
ElMessage.error('最多只能上传 3 个视频!');
return false;
}
return true; // 允许继续处理
};
// 监听对话框打开,加载数据
watch(() => data.dialogVisible, (newVal) => {
if (newVal) {
loadWarehouseList();
loadOrderList();
loadDeliveryList();
}
});
// 暴露方法给父组件调用
defineExpose({
onShowDialog,
});
</script>
<style scoped lang="scss">
.map {
width: 100%;
height: 500px;
}
/* 照片预览列表样式 */
.photo-preview-list {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 10px;
}
.photo-preview-item {
position: relative;
width: 120px;
height: 120px;
border: 1px solid #dcdfe6;
border-radius: 6px;
overflow: hidden;
}
.photo-preview-image {
width: 100%;
height: 100%;
}
.photo-delete-btn {
position: absolute;
top: 5px;
right: 5px;
z-index: 10;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
/* 视频预览列表样式 */
.video-preview-list {
display: flex;
flex-direction: column;
gap: 15px;
margin-top: 15px;
}
.video-preview-item {
position: relative;
border: 1px solid #dcdfe6;
border-radius: 6px;
padding: 10px;
background-color: #f5f7fa;
}
.video-preview-player {
width: 100%;
max-height: 300px;
border-radius: 4px;
}
.video-name {
margin-top: 8px;
font-size: 12px;
color: #606266;
text-align: center;
}
.video-delete-btn {
position: absolute;
top: 15px;
right: 15px;
z-index: 10;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
</style>