nursing_unit_vue/src/utils/midiaManage/MediaResourcePicker.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>