344 lines
8.7 KiB
Vue
344 lines
8.7 KiB
Vue
|
<template>
|
||
|
<a-modal :visible="visible" width="70vw" :title="title" :footer="null" @cancel="handleCancel">
|
||
|
<div class="resource-picker-container">
|
||
|
<!-- 搜索区域 -->
|
||
|
<div class="search-area">
|
||
|
<a-form layout="inline" :model="searchParams" :label-col="{ span: 8 }" :wrapper-col="{ span: 16 }">
|
||
|
<a-form-item label="资源名称">
|
||
|
<a-input v-model:value="searchParams.name" placeholder="请输入资源名称" />
|
||
|
</a-form-item>
|
||
|
<a-form-item label="文件类型" v-if="!fixedFileType">
|
||
|
<j-dict-select-tag v-model:value="searchParams.fileType" dictCode="file_type" placeholder="请选择文件类型"
|
||
|
allowClear />
|
||
|
</a-form-item>
|
||
|
<a-button type="primary" @click="handleSearch">查询</a-button>
|
||
|
<a-button style="margin-left: 8px" @click="resetSearch">重置</a-button>
|
||
|
</a-form>
|
||
|
</div>
|
||
|
|
||
|
<!-- 表格区域 -->
|
||
|
<div class="table-wrapper" :style="{ height: 'calc(50vh - 100px)' }">
|
||
|
<a-table :columns="columns" :data-source="dataSource" :pagination="pagination" :loading="loading" rowKey="id"
|
||
|
@change="handleTableChange" :row-selection="rowSelection" :scroll="{ y: '100%' }" :customRow="customRow"
|
||
|
size="middle">
|
||
|
<template #bodyCell="{ column, record }">
|
||
|
<template v-if="column.key === 'filePath'">
|
||
|
<div>
|
||
|
<PreviewComponent :izNetUrl="record.izNetUrl" :file-type="record.fileType" :file-path="record.filePath"
|
||
|
:file-name="record.name" />
|
||
|
</div>
|
||
|
</template>
|
||
|
<template v-if="column.key === 'action'">
|
||
|
<a-button type="link" @click.stop="handleViewDetail(record)">详情</a-button>
|
||
|
</template>
|
||
|
</template>
|
||
|
</a-table>
|
||
|
</div>
|
||
|
|
||
|
<!-- 底部操作按钮 -->
|
||
|
<div class="footer-area">
|
||
|
<a-button @click="handleCancel">取消</a-button>
|
||
|
<a-button type="primary" @click="handleConfirm" :disabled="!selectedRowKeys.length" style="margin-left: 8px">
|
||
|
确定
|
||
|
</a-button>
|
||
|
</div>
|
||
|
</div>
|
||
|
|
||
|
<!-- 详情弹窗 -->
|
||
|
<a-modal v-model:visible="detailVisible" :title="detailTitle" :width="800" :footer="null">
|
||
|
<MediaDetail :record="currentRecord" />
|
||
|
</a-modal>
|
||
|
</a-modal>
|
||
|
</template>
|
||
|
|
||
|
<script lang="ts" setup>
|
||
|
import { ref, reactive, defineProps, defineEmits, watch, computed, onMounted } from 'vue';
|
||
|
import { list } from './MediaManagePicker.api';
|
||
|
import PreviewComponent from './PreviewPickerComponent.vue';
|
||
|
import MediaDetail from './MediaDetail.vue';
|
||
|
import JDictSelectTag from '/@/components/Form/src/jeecg/components/JDictSelectTag.vue';
|
||
|
import { message } from 'ant-design-vue';
|
||
|
|
||
|
const props = defineProps({
|
||
|
visible: {
|
||
|
type: Boolean,
|
||
|
default: false
|
||
|
},
|
||
|
title: {
|
||
|
type: String,
|
||
|
default: '选择媒体资源'
|
||
|
},
|
||
|
multiple: {
|
||
|
type: Boolean,
|
||
|
default: false
|
||
|
},
|
||
|
maxSelect: {
|
||
|
type: Number,
|
||
|
default: 1
|
||
|
},
|
||
|
// 新增固定文件类型属性
|
||
|
fileType: {
|
||
|
type: String,
|
||
|
default: ''
|
||
|
}
|
||
|
});
|
||
|
|
||
|
const emit = defineEmits(['update:visible', 'confirm']);
|
||
|
|
||
|
const visible = ref(props.visible);
|
||
|
const loading = ref(false);
|
||
|
const dataSource = ref([]);
|
||
|
const selectedRowKeys = ref<string[]>([]);
|
||
|
const selectedRows = ref<any[]>([]);
|
||
|
const detailVisible = ref(false);
|
||
|
const currentRecord = reactive({});
|
||
|
const detailTitle = ref('资源详情');
|
||
|
// 计算属性:是否固定了文件类型
|
||
|
const fixedFileType = computed(() => {
|
||
|
if (!!props.fileType) {
|
||
|
if (props.fileType == 'any') {
|
||
|
return ''
|
||
|
} else if (props.fileType == 'img') {
|
||
|
return 'image'
|
||
|
} else {
|
||
|
return props.fileType
|
||
|
}
|
||
|
} else {
|
||
|
return ''
|
||
|
}
|
||
|
|
||
|
});
|
||
|
|
||
|
const searchParams = reactive({
|
||
|
name: '',
|
||
|
fileType: props.fileType || '' // 初始化时使用传入的fileType
|
||
|
});
|
||
|
|
||
|
const pagination = reactive({
|
||
|
current: 1,
|
||
|
pageSize: 10,
|
||
|
total: 0,
|
||
|
showSizeChanger: true,
|
||
|
showQuickJumper: true,
|
||
|
pageSizeOptions: ['10', '20', '30', '50'],
|
||
|
showTotal: total => `共 ${total} 条`
|
||
|
});
|
||
|
|
||
|
const columns = [
|
||
|
{
|
||
|
title: '资源名称',
|
||
|
dataIndex: 'name',
|
||
|
align: 'center',
|
||
|
key: 'name',
|
||
|
width: '25%'
|
||
|
},
|
||
|
{
|
||
|
title: '文件类型',
|
||
|
dataIndex: 'fileType_dictText',
|
||
|
align: 'center',
|
||
|
key: 'fileType',
|
||
|
width: '15%'
|
||
|
},
|
||
|
{
|
||
|
title: '预览',
|
||
|
align: 'center',
|
||
|
key: 'filePath',
|
||
|
width: '50%'
|
||
|
},
|
||
|
{
|
||
|
title: '操作',
|
||
|
align: 'center',
|
||
|
key: 'action',
|
||
|
width: '10%'
|
||
|
}
|
||
|
];
|
||
|
|
||
|
// 自定义行属性
|
||
|
const customRow = (record) => {
|
||
|
return {
|
||
|
onClick: () => {
|
||
|
handleRowClick(record);
|
||
|
}
|
||
|
};
|
||
|
};
|
||
|
|
||
|
// 计算属性:行选择配置
|
||
|
const rowSelection = computed(() => ({
|
||
|
selectedRowKeys: selectedRowKeys.value,
|
||
|
onChange: onSelectChange,
|
||
|
type: props.maxSelect === 1 ? 'radio' : 'checkbox',
|
||
|
getCheckboxProps: (record) => ({
|
||
|
disabled: props.maxSelect !== -1 && selectedRowKeys.value.length >= props.maxSelect && !selectedRowKeys.value.includes(record.id)
|
||
|
})
|
||
|
}));
|
||
|
|
||
|
// 监听visible变化
|
||
|
watch(() => props.visible, (val) => {
|
||
|
visible.value = val;
|
||
|
if (val) {
|
||
|
fetchData();
|
||
|
}
|
||
|
});
|
||
|
|
||
|
// 监听fileType变化
|
||
|
watch(() => props.fileType, (newVal) => {
|
||
|
searchParams.fileType = newVal || '';
|
||
|
if (visible.value) {
|
||
|
fetchData();
|
||
|
}
|
||
|
});
|
||
|
|
||
|
// 获取数据
|
||
|
const fetchData = async () => {
|
||
|
try {
|
||
|
loading.value = true;
|
||
|
const params = {
|
||
|
...searchParams,
|
||
|
pageNo: pagination.current,
|
||
|
pageSize: pagination.pageSize
|
||
|
};
|
||
|
// 如果设置了固定文件类型,强制使用该类型查询
|
||
|
if (fixedFileType.value) {
|
||
|
params.fileType = props.fileType;
|
||
|
}
|
||
|
const res = await list(params);
|
||
|
dataSource.value = res.records || [];
|
||
|
pagination.total = res.total || 0;
|
||
|
} catch (error) {
|
||
|
console.error('获取数据失败:', error);
|
||
|
} finally {
|
||
|
loading.value = false;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// 表格变化
|
||
|
const handleTableChange = (pag, filters, sorter) => {
|
||
|
pagination.current = pag.current;
|
||
|
pagination.pageSize = pag.pageSize;
|
||
|
fetchData();
|
||
|
};
|
||
|
|
||
|
// 搜索
|
||
|
const handleSearch = () => {
|
||
|
pagination.current = 1;
|
||
|
fetchData();
|
||
|
};
|
||
|
|
||
|
// 重置搜索
|
||
|
const resetSearch = () => {
|
||
|
searchParams.name = '';
|
||
|
// 重置时,如果设置了固定文件类型,保持该类型不变
|
||
|
if (!fixedFileType.value) {
|
||
|
searchParams.fileType = '';
|
||
|
}
|
||
|
handleSearch();
|
||
|
};
|
||
|
|
||
|
// 选择变化
|
||
|
const onSelectChange = (selectedKeys: string[], selectedRowsInfo: any[]) => {
|
||
|
if (props.maxSelect === 1) {
|
||
|
// 单选模式,只保留最新选择
|
||
|
selectedRowKeys.value = selectedKeys.slice(-1);
|
||
|
selectedRows.value = selectedRowsInfo.slice(-1);
|
||
|
} else if (props.maxSelect !== -1 && selectedKeys.length > props.maxSelect) {
|
||
|
// 多选模式,超过限制数量时提示但不阻止
|
||
|
message.warning(`最多只能选择${props.maxSelect}项`);
|
||
|
} else {
|
||
|
selectedRowKeys.value = selectedKeys;
|
||
|
selectedRows.value = selectedRowsInfo;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// 点击行选择
|
||
|
const handleRowClick = (record) => {
|
||
|
const selectedIndex = selectedRowKeys.value.indexOf(record.id);
|
||
|
|
||
|
if (props.maxSelect === 1) {
|
||
|
// 单选模式,直接替换选中项
|
||
|
selectedRowKeys.value = [record.id];
|
||
|
selectedRows.value = [record];
|
||
|
} else {
|
||
|
if (selectedIndex >= 0) {
|
||
|
// 如果已选中,则取消选择
|
||
|
selectedRowKeys.value.splice(selectedIndex, 1);
|
||
|
selectedRows.value.splice(selectedIndex, 1);
|
||
|
} else {
|
||
|
// 如果未选中,则添加选择
|
||
|
if (props.maxSelect === -1 || selectedRowKeys.value.length < props.maxSelect) {
|
||
|
selectedRowKeys.value.push(record.id);
|
||
|
selectedRows.value.push(record);
|
||
|
} else {
|
||
|
message.warning(`最多只能选择${props.maxSelect}项`);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// 查看详情
|
||
|
const handleViewDetail = (record) => {
|
||
|
Object.assign(currentRecord, record);
|
||
|
detailTitle.value = `资源详情 - ${record.name}`;
|
||
|
detailVisible.value = true;
|
||
|
};
|
||
|
|
||
|
// 确认选择
|
||
|
const handleConfirm = () => {
|
||
|
emit('confirm', (props.maxSelect != 1) ? selectedRows.value : selectedRows.value[0]);
|
||
|
handleCancel();
|
||
|
};
|
||
|
|
||
|
// 取消
|
||
|
const handleCancel = () => {
|
||
|
selectedRowKeys.value = [];
|
||
|
selectedRows.value = [];
|
||
|
emit('update:visible', false);
|
||
|
};
|
||
|
|
||
|
|
||
|
defineExpose({
|
||
|
fetchData
|
||
|
});
|
||
|
</script>
|
||
|
|
||
|
<style scoped>
|
||
|
.resource-picker-container {
|
||
|
padding: 10px;
|
||
|
display: flex;
|
||
|
flex-direction: column;
|
||
|
height: 100%;
|
||
|
}
|
||
|
|
||
|
.search-area {
|
||
|
margin-bottom: 16px;
|
||
|
}
|
||
|
|
||
|
.table-wrapper {
|
||
|
flex: 1;
|
||
|
overflow: hidden;
|
||
|
margin-bottom: 16px;
|
||
|
}
|
||
|
|
||
|
.footer-area {
|
||
|
text-align: right;
|
||
|
padding-top: 16px;
|
||
|
border-top: 1px solid #f0f0f0;
|
||
|
}
|
||
|
|
||
|
.preview-wrapper {
|
||
|
max-width: 300px;
|
||
|
max-height: 150px;
|
||
|
overflow: hidden;
|
||
|
display: flex;
|
||
|
justify-content: center;
|
||
|
align-items: center;
|
||
|
}
|
||
|
|
||
|
/* 添加行点击效果 */
|
||
|
:deep(.ant-table-row) {
|
||
|
cursor: pointer;
|
||
|
}
|
||
|
|
||
|
:deep(.ant-table-row:hover) {
|
||
|
background-color: #f5f5f5;
|
||
|
}
|
||
|
</style>
|