| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238 |
- <template>
- <div>
- <el-button type="primary" @click="dialogVisible = true">选择文件</el-button>
- <div v-if="selectedFiles.length > 0" class="selected-files-list">
- <el-tag v-for="(file, idx) in selectedFiles" :key="file.id" closable @close="removeFile(idx)" style="margin: 2px">
- {{ file.name }}
- </el-tag>
- </div>
- <el-dialog title="选择媒资文件" v-model="dialogVisible" width="900px" append-to-body @close="restoreSelection">
- <el-form :inline="true" :model="queryParams" class="mb-2">
- <el-form-item label="名称">
- <el-input v-model="queryParams.originalName" placeholder="文件名" clearable style="width: 180px"
- @keyup.enter="getFileList" />
- </el-form-item>
- <el-form-item label="类型">
- <el-select v-model="queryParams.type" clearable placeholder="全部" style="width: 120px">
- <el-option label="全部" :value="''" />
- <el-option label="图片" :value="1" />
- <el-option label="视频" :value="2" />
- <el-option label="音频" :value="3" />
- </el-select>
- </el-form-item>
- <el-form-item>
- <el-button type="primary" @click="getFileList">搜索</el-button>
- </el-form-item>
- </el-form>
- <el-table v-loading="dialogLoading" ref="fileTable" :data="fileList" reserve-selection row-key="id"
- @selection-change="handleSelectionFile" @select="handleSelect" @select-all="handleSelectAll">
- <el-table-column type="selection" width="55" header-align="center" :selectable="isSelectableRow" />
- <el-table-column label="类型" header-align="left" prop="type" width="80">
- <template #default="scope">
- <dict-tag :options="smsb_source_type" :value="scope.row.type" />
- </template>
- </el-table-column>
- <el-table-column label="原名" header-align="left" prop="originalName" width="150" :show-overflow-tooltip="true" />
- <el-table-column label="大小" header-align="left" prop="size" />
- <el-table-column label="时长" header-align="left" prop="duration" />
- <el-table-column label="截图" header-align="left" prop="screenshot">
- <template #default="scope">
- <image-preview :src="scope.row.screenshot" style="width: 40px; height: 40px; cursor: pointer" />
- </template>
- </el-table-column>
- </el-table>
- <pagination v-show="fileTotal > 0" :total="fileTotal" v-model:page="queryParams.pageNum"
- v-model:limit="queryParams.pageSize" @pagination="getFileList" />
- <template #footer>
- <el-button @click="dialogVisible = false">取消</el-button>
- <el-button type="primary" @click="confirmSelect">确定</el-button>
- </template>
- </el-dialog>
- </div>
- </template>
- <script setup lang="ts">
- const { proxy } = getCurrentInstance() as ComponentInternalInstance;
- const { smsb_source_type } = toRefs<any>(proxy?.useDict('smsb_source_type'));
- import { ref, watch, defineProps, defineEmits, nextTick } from 'vue';
- import { listMinioData } from '@/api/smsb/source/minioData';
- import type { MinioDataVO, MinioDataQuery } from '@/api/smsb/source/minioData_type';
- const props = defineProps<{ modelValue: string; single?: boolean; onlyImage?: boolean }>();
- const emit = defineEmits(['update:modelValue']);
- const dialogVisible = ref(false);
- const dialogLoading = ref(false);
- const fileList = ref<MinioDataVO[]>([]);
- const fileTotal = ref(0);
- const selectedFiles = ref<any[]>([]);
- // 控制哪些行可选
- function isSelectableRow(row: any) {
- if (props.onlyImage) return row.type === 1;
- return true;
- }
- const fileTable = ref();
- const queryParams = ref<MinioDataQuery>({
- pageNum: 1,
- pageSize: 10,
- originalName: '',
- type: ''
- });
- // 初始化时,如果有值,反序列化
- watch(
- () => props.modelValue,
- (val) => {
- if (val) {
- try {
- selectedFiles.value = JSON.parse(val);
- } catch {
- selectedFiles.value = [];
- }
- } else {
- selectedFiles.value = [];
- }
- },
- { immediate: true }
- );
- // 查询文件资源列表
- const getFileList = async () => {
- dialogLoading.value = true;
- try {
- const res = await listMinioData(queryParams.value);
- console.log('[getFileList] raw:', res);
- console.log('[getFileList] rows:', res.rows);
- const mapped = (res.rows || []).map((item: any, idx: number) => {
- const sizeNum = Number(item.size);
- const sizeStr = !isNaN(sizeNum) && sizeNum > 0 ? (sizeNum / 1024).toFixed(3) + 'MB' : '0MB';
- if (isNaN(sizeNum)) {
- console.warn(`[getFileList] row[${idx}] 非法size:`, item.size, item);
- }
- return {
- ...item,
- size: sizeStr
- };
- });
- fileList.value = mapped;
- console.log('[getFileList] mapped fileList:', mapped);
- console.log('[getFileList] fileList.value:', fileList.value, 'isArray:', Array.isArray(fileList.value), 'length:', fileList.value.length);
- fileTotal.value = res.total || 0;
- await nextTick();
- restoreSelection();
- } finally {
- dialogLoading.value = false;
- }
- };
- // 多选框选中文件数据
- function handleSelectionFile(selection: MinioDataVO[]) {
- console.log('[handleSelectionFile] selection:', selection);
- if (props.single && props.onlyImage) {
- // 单选且仅图片
- if (selection.length > 0) {
- const img = selection.find((item) => item.type === 1);
- if (img) {
- selectedFiles.value = [
- {
- id: img.id,
- name: img.originalName,
- type: img.type,
- duration: 10,
- order: 1,
- url: img.fileUrl,
- md5: img.md5
- }
- ];
- } else {
- selectedFiles.value = [];
- }
- } else {
- selectedFiles.value = [];
- }
- } else if (props.single) {
- // 单选任意类型
- if (selection.length > 0) {
- selectedFiles.value = [
- {
- id: selection[0].id,
- name: selection[0].originalName,
- type: selection[0].type,
- duration: selection[0].duration,
- order: 1,
- url: selection[0].fileUrl,
- md5: selection[0].md5
- }
- ];
- } else {
- selectedFiles.value = [];
- }
- } else {
- // 多选多类型
- selectedFiles.value = selection.map((item, idx) => ({
- id: item.id,
- name: item.originalName,
- type: item.type,
- duration: item.duration,
- order: idx + 1,
- url: item.fileUrl,
- md5: item.md5
- }));
- }
- }
- // 取消单个选中
- function handleSelect(selection: MinioDataVO[], row: MinioDataVO) {
- if (!selection.some((item) => String(item.id) === String(row.id))) {
- selectedFiles.value = selectedFiles.value.filter((f) => String(f.id) !== String(row.id));
- selectedFiles.value = selectedFiles.value.map((f, idx) => ({ ...f, order: idx + 1 }));
- }
- }
- // 取消全选
- function handleSelectAll(selection: MinioDataVO[]) {
- const currentPageIds = new Set(fileList.value.map((item) => String(item.id)));
- const selectedIds = new Set(selection.map((item) => String(item.id)));
- selectedFiles.value = selectedFiles.value.filter((f) => !currentPageIds.has(String(f.id)) || selectedIds.has(String(f.id)));
- selectedFiles.value = selectedFiles.value.map((f, idx) => ({ ...f, order: idx + 1 }));
- }
- function removeFile(idx: number) {
- selectedFiles.value.splice(idx, 1);
- emitChange();
- }
- function confirmSelect() {
- if (props.single && props.onlyImage) {
- if (selectedFiles.value.length === 0 || selectedFiles.value[0].type !== 1) {
- ElMessage.error('只能选择图片文件作为背景');
- return;
- }
- }
- dialogVisible.value = false;
- emitChange();
- }
- function emitChange() {
- emit('update:modelValue', JSON.stringify(selectedFiles.value));
- }
- // 弹窗关闭时还原选中状态
- function restoreSelection() {
- nextTick(() => {
- if (!fileTable.value) return;
- const selectedIds = new Set(selectedFiles.value.map((f) => String(f.id)));
- fileList.value.forEach((row) => {
- fileTable.value.toggleRowSelection(row, selectedIds.has(String(row.id)));
- });
- });
- }
- // 弹窗首次打开时自动加载
- watch(dialogVisible, (val) => {
- if (val) getFileList();
- });
- </script>
- <style scoped>
- .selected-files-list {
- margin: 8px 0;
- }
- </style>
|