512 lines
13 KiB
Vue
512 lines
13 KiB
Vue
<template>
|
||
<div class="app-container">
|
||
<!-- 搜索区域 -->
|
||
<div class="filter-container">
|
||
<el-card shadow="never">
|
||
<el-form :inline="true" :model="queryParams" class="demo-form-inline">
|
||
<el-form-item label="标题">
|
||
<el-input
|
||
v-model="queryParams.title"
|
||
placeholder="请输入标题"
|
||
clearable
|
||
style="width: 200px"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="部门">
|
||
<el-select
|
||
v-model="queryParams.deptId"
|
||
placeholder="请选择部门"
|
||
clearable
|
||
style="width: 200px"
|
||
>
|
||
<el-option
|
||
v-for="dept in deptOptions"
|
||
:key="dept.id"
|
||
:label="dept.name"
|
||
:value="dept.id"
|
||
/>
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="涉事人">
|
||
<el-input
|
||
v-model="queryParams.personName"
|
||
placeholder="请输入涉事人"
|
||
clearable
|
||
style="width: 200px"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="事项">
|
||
<el-select
|
||
v-model="queryParams.reasonId"
|
||
placeholder="请选择事项"
|
||
clearable
|
||
style="width: 200px"
|
||
>
|
||
<el-option
|
||
v-for="item in reportTypeOptions"
|
||
:key="item.id"
|
||
:label="item.name"
|
||
:value="item.id"
|
||
/>
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="填报时间">
|
||
<el-date-picker
|
||
v-model="createTimeRange"
|
||
type="daterange"
|
||
range-separator="至"
|
||
start-placeholder="开始日期"
|
||
end-placeholder="结束日期"
|
||
format="YYYY-MM-DD"
|
||
value-format="YYYY-MM-DD"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item>
|
||
<el-button type="primary" @click="handleQuery" :loading="loading">
|
||
查询
|
||
</el-button>
|
||
<el-button @click="resetQuery">重置</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
</el-card>
|
||
</div>
|
||
|
||
<!-- 表格区域 -->
|
||
<div class="table-container">
|
||
<el-card shadow="never">
|
||
<!-- 表格操作区域 -->
|
||
<div class="table-header">
|
||
<el-button
|
||
@click="handleExport"
|
||
:loading="exportLoading"
|
||
type="success"
|
||
plain
|
||
>
|
||
<el-icon class="mr5"><Download /></el-icon>
|
||
导出
|
||
</el-button>
|
||
</div>
|
||
|
||
<!-- 表格 -->
|
||
<el-table
|
||
v-loading="loading"
|
||
:data="reportList"
|
||
:header-cell-style="{ background: '#f5f7fa', color: '#606266' }"
|
||
stripe
|
||
style="width: 100%"
|
||
>
|
||
<el-table-column
|
||
label="标题"
|
||
prop="title"
|
||
min-width="200"
|
||
align="center"
|
||
show-overflow-tooltip
|
||
/>
|
||
<el-table-column
|
||
label="部门"
|
||
prop="deptName"
|
||
min-width="120"
|
||
align="center"
|
||
/>
|
||
<el-table-column
|
||
label="涉事人"
|
||
prop="personName"
|
||
min-width="120"
|
||
align="center"
|
||
/>
|
||
<el-table-column label="事项" min-width="120" align="center">
|
||
<template #default="{ row }">
|
||
{{ row.reportTypeName || row.reason?.name || '-' }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="分值" min-width="80" align="center">
|
||
<template #default="scope">
|
||
{{ scope.row.userIntegralRecords?.[0]?.score || '-' }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column
|
||
label="备注"
|
||
prop="remark"
|
||
min-width="250"
|
||
align="center"
|
||
show-overflow-tooltip
|
||
>
|
||
<template #default="scope">
|
||
{{ scope.row.remark || '-' }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="填报时间" min-width="160" align="center">
|
||
<template #default="scope">
|
||
{{ formatDisplayTime(scope.row.fillingTime) }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="状态" min-width="100" align="center">
|
||
<template #default="scope">
|
||
<el-tag :type="getStatusTagType(scope.row.status)">
|
||
{{ getStatusText(scope.row.status) }}
|
||
</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column
|
||
label="操作"
|
||
min-width="120"
|
||
align="center"
|
||
fixed="right"
|
||
>
|
||
<template #default="scope">
|
||
<span class="table-opera-item" @click="handleView(scope.row)" :title="'查看'">
|
||
<el-icon class="table-opera-icon"><View /></el-icon>
|
||
</span>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
|
||
<!-- 空状态 -->
|
||
<template v-if="!loading && reportList.length === 0">
|
||
<el-empty description="暂无公司通报数据" :image-size="200" />
|
||
</template>
|
||
|
||
<pagination
|
||
v-show="total > 0"
|
||
:total="total"
|
||
v-model:page="queryParams.pageNo"
|
||
v-model:limit="queryParams.pageSize"
|
||
@pagination="getList"
|
||
/>
|
||
</el-card>
|
||
</div>
|
||
|
||
<!-- 查看详情组件 -->
|
||
<DetailComponent
|
||
v-model:visible="detailDialogVisible"
|
||
:report-id="selectedReportId"
|
||
@refresh="getList"
|
||
/>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { onMounted, reactive, ref, computed, watch } from 'vue'
|
||
import { ElMessage } from 'element-plus'
|
||
import { View, Download } from '@element-plus/icons-vue'
|
||
import { useRoute } from 'vue-router'
|
||
import Pagination from '@/components/Pagination/index.vue'
|
||
import DetailComponent from '../common/detail.vue'
|
||
import { useDeptStore } from '@/store/modules/dept'
|
||
import { useRewardsPunishReasonStore } from '@/store/modules/rewardsPunishReason'
|
||
import { enumsType } from '@/store/static'
|
||
import { getMyReportPage, exportMyReport } from '@/api/submit/myReporting'
|
||
import type { MyReportDTO, QueryParams } from '@/api/submit/myReporting/types'
|
||
|
||
// Store
|
||
const deptStore = useDeptStore()
|
||
const rewardsPunishReasonStore = useRewardsPunishReasonStore()
|
||
|
||
// Router
|
||
const route = useRoute()
|
||
|
||
// 计算属性
|
||
const deptOptions = computed(() => deptStore.getDeptList)
|
||
const reportTypeOptions = computed(() => rewardsPunishReasonStore.getRewardsPunishReasonList)
|
||
|
||
// 响应式数据
|
||
const reportList = ref<MyReportDTO[]>([])
|
||
const total = ref<number>(0)
|
||
const loading = ref<boolean>(true)
|
||
const exportLoading = ref<boolean>(false)
|
||
|
||
const createTimeRange = ref<string[] | null>(null)
|
||
const detailDialogVisible = ref<boolean>(false)
|
||
const selectedReportId = ref<string>('')
|
||
|
||
// 查询参数
|
||
const queryParams = reactive<QueryParams>({
|
||
pageNo: 1,
|
||
pageSize: 10,
|
||
title: '',
|
||
deptId: '',
|
||
personName: '',
|
||
reasonId: '',
|
||
status: '',
|
||
startTime: undefined,
|
||
endTime: undefined,
|
||
isOwnReport: false,
|
||
isOwnApprove: false
|
||
})
|
||
|
||
// 工具函数
|
||
const getStatusTagType = (status: string | number) => {
|
||
const typeMap: Record<string | number, string> = {
|
||
'STAGING': 'warning', 1: 'warning',
|
||
'AUDIT': 'primary', 2: 'primary',
|
||
'APPROVED': 'success', 3: 'success',
|
||
'REVIEW_FAILED': 'danger', 4: 'danger'
|
||
}
|
||
return typeMap[status] || 'info'
|
||
}
|
||
|
||
const getStatusText = (status: string | number) => {
|
||
const statusItem = enumsType.reportStatus.find(item => item.value === status)
|
||
return statusItem ? statusItem.text : '未知状态'
|
||
}
|
||
|
||
const formatDateTime = (dateTime: string | undefined) => {
|
||
if (!dateTime) return ''
|
||
const date = new Date(dateTime)
|
||
return date.toLocaleString('zh-CN', {
|
||
year: 'numeric',
|
||
month: '2-digit',
|
||
day: '2-digit',
|
||
hour: '2-digit',
|
||
minute: '2-digit',
|
||
second: '2-digit'
|
||
})
|
||
}
|
||
|
||
const formatDisplayTime = (time: string | undefined) => {
|
||
return time ? formatDateTime(time) : '-'
|
||
}
|
||
|
||
// 初始化
|
||
onMounted(async () => {
|
||
await initData()
|
||
})
|
||
|
||
// 初始化数据
|
||
const initData = async () => {
|
||
try {
|
||
// 并行加载数据
|
||
await Promise.all([
|
||
!deptStore.getIsSetDept ? deptStore.setDeptInfoAction() : Promise.resolve(),
|
||
!rewardsPunishReasonStore.getIsSetRewardsPunishReason ? rewardsPunishReasonStore.setRewardsPunishReasonAction() : Promise.resolve()
|
||
])
|
||
await getList()
|
||
} catch (error) {
|
||
console.error('初始化数据失败:', error)
|
||
ElMessage.error('初始化数据失败')
|
||
}
|
||
}
|
||
|
||
// 获取列表
|
||
const getList = async () => {
|
||
loading.value = true
|
||
queryParams.status = 'APPROVED'
|
||
try {
|
||
const response = await getMyReportPage(queryParams)
|
||
reportList.value = response.list || []
|
||
total.value = response.total || 0
|
||
} catch (error) {
|
||
console.error('获取公司通报列表失败:', error)
|
||
ElMessage.error('获取公司通报列表失败')
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
// 查询
|
||
const handleQuery = () => {
|
||
queryParams.pageNo = 1
|
||
|
||
// 处理填报时间范围
|
||
if (createTimeRange.value && createTimeRange.value.length === 2) {
|
||
queryParams.startTime = createTimeRange.value[0] + 'T00:00:00'
|
||
queryParams.endTime = createTimeRange.value[1] + 'T23:59:59'
|
||
} else {
|
||
queryParams.startTime = undefined
|
||
queryParams.endTime = undefined
|
||
}
|
||
|
||
getList()
|
||
}
|
||
|
||
// 重置查询
|
||
const resetQuery = () => {
|
||
Object.assign(queryParams, {
|
||
pageNo: 1,
|
||
pageSize: 10,
|
||
title: '',
|
||
deptId: '',
|
||
personName: '',
|
||
reasonId: '',
|
||
status: '',
|
||
startTime: undefined,
|
||
endTime: undefined,
|
||
isOwnReport: false,
|
||
isOwnApprove: false
|
||
})
|
||
|
||
createTimeRange.value = null
|
||
getList()
|
||
}
|
||
|
||
// 查看详情
|
||
const handleView = (row: MyReportDTO) => {
|
||
selectedReportId.value = row.id
|
||
detailDialogVisible.value = true
|
||
}
|
||
|
||
// 监听路由变化,当路由发生变化时关闭抽屉
|
||
watch(
|
||
[() => route.path, () => route.fullPath],
|
||
() => {
|
||
detailDialogVisible.value = false
|
||
},
|
||
{ immediate: true }
|
||
)
|
||
|
||
// 导出
|
||
const handleExport = async () => {
|
||
exportLoading.value = true
|
||
try {
|
||
const response = await exportMyReport(queryParams)
|
||
|
||
// 处理文件下载
|
||
if (response instanceof Blob) {
|
||
// 创建Blob URL
|
||
const blob = new Blob([response], {
|
||
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8'
|
||
})
|
||
const url = window.URL.createObjectURL(blob)
|
||
|
||
// 创建下载链接
|
||
const link = document.createElement('a')
|
||
link.href = url
|
||
|
||
// 从响应头获取文件名,或者使用默认文件名
|
||
const filename = getFilenameFromResponse(response) || `公司通报数据.xlsx`
|
||
link.download = filename
|
||
|
||
// 触发下载
|
||
document.body.appendChild(link)
|
||
link.click()
|
||
|
||
// 清理
|
||
document.body.removeChild(link)
|
||
window.URL.revokeObjectURL(url)
|
||
|
||
ElMessage.success('导出成功')
|
||
} else {
|
||
ElMessage.success('导出请求已发送')
|
||
}
|
||
} catch (error) {
|
||
console.error('导出失败:', error)
|
||
ElMessage.error('导出失败')
|
||
} finally {
|
||
exportLoading.value = false
|
||
}
|
||
}
|
||
|
||
// 从响应中获取文件名
|
||
const getFilenameFromResponse = (response: any) => {
|
||
try {
|
||
// 如果是Response对象,从headers中获取
|
||
if (response.headers) {
|
||
const contentDisposition = response.headers.get('content-disposition')
|
||
if (contentDisposition) {
|
||
const filenameMatch = contentDisposition.match(/filename="?(.+?)\"?/i)
|
||
if (filenameMatch && filenameMatch[1]) {
|
||
return decodeURIComponent(filenameMatch[1])
|
||
}
|
||
}
|
||
}
|
||
} catch (e) {
|
||
console.warn('获取文件名失败:', e)
|
||
}
|
||
return null
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.app-container {
|
||
padding: 20px;
|
||
}
|
||
|
||
.filter-container {
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.table-container {
|
||
margin-top: 20px;
|
||
}
|
||
|
||
.table-header {
|
||
margin-bottom: 15px;
|
||
display: flex;
|
||
justify-content: flex-start;
|
||
align-items: center;
|
||
}
|
||
|
||
:deep(.el-card) {
|
||
border: 1px solid #e4e7ed;
|
||
border-radius: 4px;
|
||
}
|
||
|
||
:deep(.el-card__body) {
|
||
padding: 20px;
|
||
}
|
||
|
||
:deep(.el-table) {
|
||
border: 1px solid #e4e7ed;
|
||
border-radius: 4px;
|
||
}
|
||
|
||
:deep(.el-table th) {
|
||
background-color: #f5f7fa;
|
||
font-weight: 600;
|
||
}
|
||
|
||
:deep(.el-table--striped .el-table__body tr.el-table__row--striped td) {
|
||
background-color: #fafafa;
|
||
}
|
||
|
||
:deep(.el-form-item__label) {
|
||
font-weight: 500;
|
||
color: #606266;
|
||
}
|
||
|
||
/* 操作列样式 */
|
||
.table-opera-item {
|
||
cursor: pointer;
|
||
color: #409eff;
|
||
transition: color 0.3s;
|
||
display: inline-block;
|
||
}
|
||
|
||
.table-opera-item:hover {
|
||
color: #66b1ff;
|
||
}
|
||
|
||
.table-opera-icon {
|
||
font-size: 18px;
|
||
vertical-align: middle;
|
||
}
|
||
|
||
.mr5 {
|
||
margin-right: 5px;
|
||
}
|
||
|
||
.ml10 {
|
||
margin-left: 10px;
|
||
}
|
||
|
||
.delete-icon {
|
||
color: #eb333d;
|
||
}
|
||
|
||
.delete-icon:hover {
|
||
color: #f56c6c;
|
||
}
|
||
|
||
.loading-icon {
|
||
animation: rotate 1s linear infinite;
|
||
}
|
||
|
||
@keyframes rotate {
|
||
from {
|
||
transform: rotate(0deg);
|
||
}
|
||
to {
|
||
transform: rotate(360deg);
|
||
}
|
||
}
|
||
</style> |