You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
341 lines
9.2 KiB
341 lines
9.2 KiB
<template>
|
|
<div class="legal-tech-container">
|
|
<!-- 搜索区域 -->
|
|
<div class="search-panel">
|
|
<el-form :model="searchForm" inline>
|
|
<el-form-item label="时间筛选:">
|
|
<el-date-picker v-model="searchForm.timeRange" type="datetimerange" range-separator="至"
|
|
start-placeholder="开始时间" end-placeholder="截止时间" class="date-picker" format="YYYY-MM-DD HH:mm"
|
|
value-format="YYYY-MM-DD HH:mm:ss" />
|
|
</el-form-item>
|
|
|
|
<el-form-item label="执法单位:">
|
|
<el-input v-model="searchForm.lawEnforcementAgency" placeholder="请输入关键字" class="search-input" clearable
|
|
@keyup.enter="handleSearch" />
|
|
</el-form-item>
|
|
|
|
<el-form-item>
|
|
<el-button type="primary" @click="handleSearch" :loading="loading">搜索</el-button>
|
|
</el-form-item>
|
|
|
|
<el-form-item>
|
|
<el-button @click="handleReset">重置</el-button>
|
|
</el-form-item>
|
|
</el-form>
|
|
|
|
</div>
|
|
<div class="action-bar">
|
|
<div class="action-left">
|
|
<el-button type="primary" @click="handleAdd">新增</el-button>
|
|
</div>
|
|
</div>
|
|
<!-- 数据表格 -->
|
|
<div class="table-container">
|
|
<el-table v-loading="loading" :data="tableData" border
|
|
:header-cell-style="{ backgroundColor: '#f5f7fa', fontWeight: 'bold' }">
|
|
<el-table-column prop="lawEnforcementAgency" label="执法单位" min-width="160" show-overflow-tooltip />
|
|
|
|
<el-table-column label="法制材料" min-width="200">
|
|
<template #default="{ row }">
|
|
<div v-if="row.legalMaterials" class="file-cell">
|
|
<el-icon class="file-icon">
|
|
<Paperclip />
|
|
</el-icon>
|
|
<span class="file-name" :title="extractFileName(row.legalMaterials)">{{
|
|
extractFileName(row.legalMaterials) }}</span>
|
|
<el-button size="small" @click="downloadFile(row.legalMaterials)">下载</el-button>
|
|
</div>
|
|
<span v-else>--</span>
|
|
</template>
|
|
</el-table-column>
|
|
|
|
<el-table-column label="技术材料" min-width="200">
|
|
<template #default="{ row }">
|
|
<div v-if="row.technicalMaterials" class="file-cell">
|
|
<el-icon class="file-icon">
|
|
<Paperclip />
|
|
</el-icon>
|
|
<span class="file-name" :title="extractFileName(row.technicalMaterials)">{{
|
|
extractFileName(row.technicalMaterials) }}</span>
|
|
<el-button size="small" @click="downloadFile(row.technicalMaterials)">下载</el-button>
|
|
</div>
|
|
<span v-else>--</span>
|
|
</template>
|
|
</el-table-column>
|
|
|
|
<el-table-column label="提交时间" width="180">
|
|
<template #default="{ row }">
|
|
{{ formatDateTime(row.createTime) }}
|
|
</template>
|
|
</el-table-column>
|
|
|
|
<el-table-column label="操作" width="100" fixed="right">
|
|
<template #default="{ row }">
|
|
<el-button size="small" type="primary" link @click="handleEdit(row)">编辑</el-button>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
</div>
|
|
|
|
<!-- 分页 -->
|
|
<div class="pagination-container">
|
|
<span class="total-text">共 {{ total }} 项数据</span>
|
|
<el-pagination v-model:current-page="currentPage" v-model:page-size="pageSize" :total="total"
|
|
:page-sizes="[10, 20, 50, 100]" layout="prev, pager, next, jumper, ->, total, sizes"
|
|
@size-change="handleSizeChange" @current-change="handleCurrentChange" />
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, reactive, onMounted, getCurrentInstance } from 'vue';
|
|
import { useRouter } from 'vue-router';
|
|
import { ElMessage } from 'element-plus';
|
|
import { Paperclip } from '@element-plus/icons-vue';
|
|
import { getLegalTechRecordsPage } from '@/api/nos/index';
|
|
import type { NosLawEnforcementFilingPageVO, LegalTechRecordsPageResponse } from '@/api/nos/type';
|
|
import { parseTime } from '@/utils/ruoyi';
|
|
|
|
const router = useRouter();
|
|
const { proxy } = getCurrentInstance() as any;
|
|
|
|
const loading = ref(false);
|
|
const tableData = ref<NosLawEnforcementFilingPageVO[]>([]);
|
|
const total = ref(0);
|
|
const currentPage = ref(1);
|
|
const pageSize = ref(10);
|
|
|
|
const searchForm = reactive({
|
|
timeRange: [] as string[],
|
|
lawEnforcementAgency: ''
|
|
});
|
|
|
|
const formatDateTime = (time?: string) => {
|
|
if (!time) return '--';
|
|
return parseTime(time, '{y}-{m}-{d} {h}:{i}') || '--';
|
|
};
|
|
|
|
const BASE_API = import.meta.env.VITE_APP_BASE_API || '';
|
|
const FILE_SERVER_PREFIX = import.meta.env.VITE_APP_FILE_SERVER_PREFIX || '';
|
|
|
|
onMounted(() => {
|
|
getList();
|
|
});
|
|
const isOssId = (val: string) => /^[0-9]+$/.test(val.trim());
|
|
|
|
const toRelativeFilePath = (val: string) => {
|
|
const raw = val.trim();
|
|
if (!raw) return '';
|
|
const prefix = FILE_SERVER_PREFIX.replace(/\/+$/, '');
|
|
let path = raw.startsWith('/') ? raw : `/${raw}`;
|
|
const normalizedPrefix = prefix.startsWith('http') ? prefix : `/${prefix}`;
|
|
while (path === normalizedPrefix || path.startsWith(`${normalizedPrefix}/`)) {
|
|
path = path.slice(normalizedPrefix.length) || '/';
|
|
if (!path.startsWith('/')) path = `/${path}`;
|
|
}
|
|
return path;
|
|
};
|
|
|
|
const buildFileUrl = (val: string) => {
|
|
if (!val) return '';
|
|
const raw = val.trim();
|
|
if (raw.startsWith('http://') || raw.startsWith('https://') || raw.startsWith('blob:') || raw.startsWith('data:')) {
|
|
return raw;
|
|
}
|
|
if (isOssId(raw)) {
|
|
return `${BASE_API}/resource/oss/download/${raw}`;
|
|
}
|
|
const relative = toRelativeFilePath(raw);
|
|
return `${FILE_SERVER_PREFIX.replace(/\/+$/, '')}${relative}`;
|
|
};
|
|
|
|
const extractFileName = (val: string) => {
|
|
if (!val) return '';
|
|
const raw = val.trim();
|
|
if (isOssId(raw)) return raw;
|
|
try {
|
|
const withoutQuery = raw.split('?')[0].split('#')[0];
|
|
const parts = withoutQuery.split('/');
|
|
return parts[parts.length - 1] || raw;
|
|
} catch {
|
|
return raw;
|
|
}
|
|
};
|
|
|
|
const downloadFile = async (file: string, fileName?: string) => {
|
|
if (!file) return;
|
|
const raw = file.trim();
|
|
|
|
if (isOssId(raw) && proxy?.$download?.oss) {
|
|
proxy.$download.oss(raw);
|
|
return;
|
|
}
|
|
|
|
const url = buildFileUrl(raw);
|
|
if (!url) {
|
|
ElMessage.error('文件下载失败');
|
|
return;
|
|
}
|
|
|
|
const link = document.createElement('a');
|
|
link.href = url;
|
|
link.target = '_blank';
|
|
if (fileName) link.download = fileName;
|
|
link.rel = 'noopener noreferrer';
|
|
document.body.appendChild(link);
|
|
link.click();
|
|
document.body.removeChild(link);
|
|
};
|
|
|
|
const buildQueryParams = () => {
|
|
return {
|
|
startTime: searchForm.timeRange?.[0] || undefined,
|
|
endTime: searchForm.timeRange?.[1] || undefined,
|
|
lawEnforcementAgency: searchForm.lawEnforcementAgency || undefined,
|
|
pageNo: currentPage.value,
|
|
pageSize: pageSize.value
|
|
};
|
|
};
|
|
|
|
const getList = async () => {
|
|
loading.value = true;
|
|
try {
|
|
const res = (await getLegalTechRecordsPage(buildQueryParams())) as unknown as LegalTechRecordsPageResponse;
|
|
tableData.value = res.rows || [];
|
|
total.value = res.total || 0;
|
|
} catch (error) {
|
|
console.error('获取列表数据失败:', error);
|
|
ElMessage.error('获取数据失败,请检查网络连接');
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
};
|
|
|
|
const handleSearch = () => {
|
|
currentPage.value = 1;
|
|
getList();
|
|
};
|
|
|
|
const handleReset = () => {
|
|
searchForm.timeRange = [];
|
|
searchForm.lawEnforcementAgency = '';
|
|
currentPage.value = 1;
|
|
getList();
|
|
};
|
|
|
|
const handleAdd = () => {
|
|
router.push({ name: 'LegalTechAdd' });
|
|
};
|
|
|
|
const handleEdit = (row: NosLawEnforcementFilingPageVO) => {
|
|
if (!row.id) {
|
|
ElMessage.warning('记录ID不存在');
|
|
return;
|
|
}
|
|
router.push({
|
|
name: 'LegalTechEdit',
|
|
params: { id: row.id }
|
|
});
|
|
};
|
|
|
|
const handleSizeChange = (val: number) => {
|
|
pageSize.value = val;
|
|
currentPage.value = 1;
|
|
getList();
|
|
};
|
|
|
|
const handleCurrentChange = (val: number) => {
|
|
currentPage.value = val;
|
|
getList();
|
|
};
|
|
|
|
</script>
|
|
|
|
<style scoped>
|
|
.legal-tech-container {
|
|
padding: 20px;
|
|
background: #f5f7fa;
|
|
min-height: calc(100vh - 100px);
|
|
}
|
|
|
|
.search-panel {
|
|
display: flex;
|
|
background: #fff;
|
|
padding: 16px 20px;
|
|
border-radius: 8px;
|
|
margin-bottom: 16px;
|
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.action-panel {
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
:deep(.el-form-item__label) {
|
|
white-space: nowrap;
|
|
margin-right: 8px;
|
|
}
|
|
|
|
.search-input {
|
|
width: 200px;
|
|
}
|
|
|
|
.date-picker {
|
|
width: 360px;
|
|
}
|
|
.action-bar {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
background: #fff;
|
|
padding: 12px 20px;
|
|
border-radius: 8px;
|
|
margin-bottom: 16px;
|
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.action-left {
|
|
display: flex;
|
|
gap: 8px;
|
|
}
|
|
|
|
.table-container {
|
|
background: #fff;
|
|
border-radius: 8px;
|
|
padding: 16px;
|
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.file-cell {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
|
|
.file-icon {
|
|
color: #909399;
|
|
}
|
|
|
|
.file-name {
|
|
flex: 1;
|
|
color: #409eff;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.pagination-container {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
background: #fff;
|
|
padding: 12px 20px;
|
|
border-radius: 8px;
|
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.total-text {
|
|
color: #909399;
|
|
}
|
|
</style>
|
|
|