表格修改

This commit is contained in:
lzm
2026-04-28 18:53:58 +08:00
parent 38c634f8de
commit 7fff38fb32
4 changed files with 304 additions and 172 deletions

View File

@@ -2,7 +2,7 @@ import request from '@/config/axios'
export interface ProjectOverviewExportReqVO { export interface ProjectOverviewExportReqVO {
year?: number year?: number
specialtyCode?: string officeId?: number
sortType?: string sortType?: string
} }

View File

@@ -6,17 +6,24 @@ export const useRenderMenuTitle = () => {
const renderMenuTitle = (meta: RouteMeta) => { const renderMenuTitle = (meta: RouteMeta) => {
const { t } = useI18n() const { t } = useI18n()
const { title = 'Please set title', icon } = meta const { title = 'Please set title', icon } = meta
const text = t(title as string)
return icon ? ( return icon ? (
<> <>
<Icon icon={meta.icon}></Icon> <Icon icon={meta.icon}></Icon>
<span class="v-menu__title overflow-hidden overflow-ellipsis whitespace-nowrap"> <span
{t(title as string)} class="v-menu__title overflow-hidden overflow-ellipsis whitespace-nowrap"
title={text}
>
{text}
</span> </span>
</> </>
) : ( ) : (
<span class="v-menu__title overflow-hidden overflow-ellipsis whitespace-nowrap"> <span
{t(title as string)} class="v-menu__title overflow-hidden overflow-ellipsis whitespace-nowrap"
title={text}
>
{text}
</span> </span>
) )
} }

View File

@@ -1,7 +1,7 @@
:root { :root {
--login-bg-color: #293146; --login-bg-color: #293146;
--left-menu-max-width: 200px; --left-menu-max-width: 224px;
--left-menu-min-width: 64px; --left-menu-min-width: 64px;

View File

@@ -1,75 +1,8 @@
<template> <template>
<ContentWrap>
<div class="flex items-center justify-between gap-16px">
<div>
<div class="text-16px font-600">产值汇总报表中心</div>
<div class="mt-4px text-13px text-[var(--el-text-color-secondary)]">
页面9独立承载 7.1.5 项目总览表7.1.6 员工个人考核产值汇总
</div>
</div>
</div>
</ContentWrap>
<ContentWrap> <ContentWrap>
<el-tabs v-model="activeTab"> <el-tabs v-model="activeTab">
<el-tab-pane label="项目总览表" name="projectOverview"> <el-tab-pane label="员工产值汇总" name="employeeSummary">
<el-form :inline="true" :model="projectOverviewForm" label-width="88px"> <el-form :inline="true" :model="employeeSummaryForm" label-width="88px">
<el-form-item label="年度">
<el-date-picker
v-model="projectOverviewYearValue"
class="!w-180px"
clearable
placeholder="请选择年度"
type="year"
value-format="YYYY"
/>
</el-form-item>
<el-form-item label="专业">
<el-select
v-model="projectOverviewForm.specialtyCode"
class="!w-220px"
clearable
placeholder="请选择专业"
>
<el-option
v-for="item in REPORT_SPECIALTY_OPTIONS"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="排序方式">
<el-select
v-model="projectOverviewForm.sortType"
class="!w-220px"
placeholder="请选择排序方式"
>
<el-option
v-for="item in PROJECT_OVERVIEW_SORT_OPTIONS"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button
v-hasPermi="['tjt:report-summary:export']"
:loading="projectOverviewExportLoading"
plain
type="primary"
@click="handleExportProjectOverview"
>
<Icon class="mr-5px" icon="ep:download" />
导出项目总览表
</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="员工个人考核产值汇总" name="employeeSummary">
<el-form :inline="true" :model="employeeSummaryForm" label-width="100px">
<el-form-item label="年度"> <el-form-item label="年度">
<el-date-picker <el-date-picker
v-model="employeeSummaryYearValue" v-model="employeeSummaryYearValue"
@@ -80,13 +13,57 @@
value-format="YYYY" value-format="YYYY"
/> />
</el-form-item> </el-form-item>
<!-- <el-form-item>
<el-button @click="getEmployeeBudgetList">
<Icon class="mr-5px" icon="ep:refresh" />
刷新预览
</el-button>
</el-form-item> -->
<el-form-item>
<el-button
v-hasPermi="['tjt:report-summary:export']"
:loading="employeeSummaryExportLoading"
plain
type="primary"
@click="openEmployeeSummaryExportDialog"
>
<Icon class="mr-5px" icon="ep:download" />
导出员工汇总
</el-button>
</el-form-item>
</el-form>
<el-table
v-loading="employeeBudgetLoading"
:data="employeeBudgetList"
class="mt-12px"
:empty-text="employeeSummaryForm.year ? '暂无年度成本预算数据' : '请先选择年度'"
>
<el-table-column align="center" label="序号" type="index" width="70" />
<el-table-column align="center" label="员工姓名" min-width="160" prop="employeeName" />
<el-table-column align="center" label="预计年度" prop="budgetYear" width="120" />
<el-table-column align="center" label="预计发生成本" min-width="160">
<template #default="scope">
{{ formatAmount(scope.row.expectedCostAmount) }}
</template>
</el-table-column>
</el-table>
<Pagination
v-model:limit="employeeBudgetQuery.pageSize"
v-model:page="employeeBudgetQuery.pageNo"
:total="employeeBudgetTotal"
@pagination="getEmployeeBudgetList"
/>
</el-tab-pane>
<el-tab-pane label="项目产值总览" name="projectOverview">
<el-form :inline="true" :model="projectOverviewForm" label-width="88px">
<el-form-item label="专业所"> <el-form-item label="专业所">
<el-select <el-select
v-model="employeeSummaryForm.officeId" v-model="projectOverviewForm.officeId"
class="!w-220px" class="!w-220px"
clearable clearable
placeholder="请选择专业所" placeholder="请选择专业所"
@change="handleOfficeChange"
> >
<el-option <el-option
v-for="item in officeOptions" v-for="item in officeOptions"
@@ -96,116 +73,203 @@
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="员工"> <!-- <el-form-item>
<el-select <el-button @click="getProjectEmployeeList">
v-model="employeeSummaryForm.employeeId" <Icon class="mr-5px" icon="ep:refresh" />
class="!w-240px" 刷新预览
clearable </el-button>
filterable </el-form-item> -->
remote
reserve-keyword
placeholder="请输入员工姓名搜索"
:loading="employeeLoading"
:remote-method="searchEmployees"
>
<el-option
v-for="item in employeeOptions"
:key="item.id"
:label="item.officeName ? `${item.employeeName} / ${item.officeName}` : item.employeeName"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="员工状态">
<el-select
v-model="employeeSummaryForm.employeeStatus"
class="!w-180px"
clearable
placeholder="请选择员工状态"
@change="handleEmployeeStatusChange"
>
<el-option
v-for="item in EMPLOYEE_STATUS_OPTIONS"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="排序方式">
<el-select
v-model="employeeSummaryForm.sortType"
class="!w-220px"
placeholder="请选择排序方式"
>
<el-option
v-for="item in EMPLOYEE_SUMMARY_SORT_OPTIONS"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item> <el-form-item>
<el-button <el-button
v-hasPermi="['tjt:report-summary:export']" v-hasPermi="['tjt:report-summary:export']"
:loading="employeeSummaryExportLoading" :loading="projectOverviewExportLoading"
plain plain
type="primary" type="primary"
@click="handleExportEmployeeSummary" @click="openProjectOverviewExportDialog"
> >
<Icon class="mr-5px" icon="ep:download" /> <Icon class="mr-5px" icon="ep:download" />
导出员工汇总表 导出项目总览
</el-button> </el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
<el-table
v-loading="projectEmployeeLoading"
:data="projectEmployeeList"
class="mt-12px"
:empty-text="projectOverviewForm.officeId ? '该专业所下暂无员工' : '请先选择专业所'"
>
<el-table-column align="center" label="序号" type="index" width="70" />
<el-table-column align="center" label="员工姓名" min-width="160" prop="employeeName" />
<el-table-column align="center" label="性别" prop="gender" width="100" />
<el-table-column align="center" label="所属所" min-width="180" prop="officeName" />
</el-table>
<Pagination
v-model:limit="projectEmployeeQuery.pageSize"
v-model:page="projectEmployeeQuery.pageNo"
:total="projectEmployeeTotal"
@pagination="getProjectEmployeeList"
/>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
</ContentWrap> </ContentWrap>
<Dialog v-model="exportDialogVisible" :title="exportDialogTitle" width="480">
<el-form label-width="88px">
<template v-if="exportDialogType === 'employeeSummary'">
<el-form-item label="年度">
<span>{{ employeeSummaryForm.year || '-' }}</span>
</el-form-item>
<el-form-item label="导出排序">
<el-select
v-model="employeeSummaryForm.sortType"
class="!w-220px"
placeholder="请选择导出排序"
>
<el-option
v-for="item in EMPLOYEE_SUMMARY_SORT_OPTIONS"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</template>
<template v-else>
<el-form-item label="专业所">
<span>{{ currentOfficeName || '-' }}</span>
</el-form-item>
<el-form-item label="年度">
<el-date-picker
v-model="projectOverviewYearValue"
class="!w-220px"
clearable
disabled
placeholder="请选择年度"
type="year"
value-format="YYYY"
/>
</el-form-item>
<el-form-item label="导出排序">
<el-select
v-model="projectOverviewForm.sortType"
class="!w-220px"
placeholder="请选择导出排序"
>
<el-option
v-for="item in PROJECT_OVERVIEW_SORT_OPTIONS"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</template>
</el-form>
<template #footer>
<el-button @click="exportDialogVisible = false">取消</el-button>
<el-button :loading="currentExportLoading" type="primary" @click="submitExport">
确定导出
</el-button>
</template>
</Dialog>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import * as EmployeeApi from '@/api/tjt/employee' import * as EmployeeApi from '@/api/tjt/employee'
import * as EmployeeYearCostBudgetApi from '@/api/tjt/employeeYearCostBudget'
import * as OfficeApi from '@/api/tjt/office' import * as OfficeApi from '@/api/tjt/office'
import * as ReportApi from '@/api/tjt/report' import * as ReportApi from '@/api/tjt/report'
import download from '@/utils/download' import download from '@/utils/download'
import { EMPLOYEE_STATUS_OPTIONS } from '@/views/tjt/shared/planning'
import {
EMPLOYEE_SUMMARY_SORT_OPTIONS,
PROJECT_OVERVIEW_SORT_OPTIONS,
REPORT_SPECIALTY_OPTIONS
} from '@/views/tjt/shared/report'
defineOptions({ name: 'TjtReportSummary' }) defineOptions({ name: 'TjtReportSummary' })
type SelectOption = {
label: string
value: string
}
type ExportDialogType = 'employeeSummary' | 'projectOverview'
const PROJECT_OVERVIEW_SORT_OPTIONS: SelectOption[] = [
{ label: '产值从高到低', value: 'output_desc' },
{ label: '产值从低到高', value: 'output_asc' },
{ label: '项目名称升序', value: 'name_asc' }
]
const EMPLOYEE_SUMMARY_SORT_OPTIONS: SelectOption[] = [
{ label: '年度合计从高到低', value: 'annual_total_desc' },
{ label: '年度合计从低到高', value: 'annual_total_asc' },
{ label: '员工姓名升序', value: 'name_asc' }
]
const message = useMessage() const message = useMessage()
const activeTab = ref('projectOverview') const activeTab = ref('employeeSummary')
const exportDialogVisible = ref(false)
const exportDialogType = ref<ExportDialogType>('employeeSummary')
const projectOverviewExportLoading = ref(false) const projectOverviewExportLoading = ref(false)
const employeeSummaryExportLoading = ref(false) const employeeSummaryExportLoading = ref(false)
const employeeLoading = ref(false) const employeeBudgetLoading = ref(false)
const projectEmployeeLoading = ref(false)
const officeOptions = ref<OfficeApi.OfficeSimpleVO[]>([]) const officeOptions = ref<OfficeApi.OfficeSimpleVO[]>([])
const employeeOptions = ref<EmployeeApi.EmployeeSimpleVO[]>([]) const employeeBudgetList = ref<EmployeeYearCostBudgetApi.EmployeeYearCostBudgetVO[]>([])
const projectEmployeeList = ref<EmployeeApi.EmployeeVO[]>([])
const employeeBudgetTotal = ref(0)
const projectEmployeeTotal = ref(0)
const currentYear = new Date().getFullYear()
const projectOverviewForm = reactive<ReportApi.ProjectOverviewExportReqVO>({ const projectOverviewForm = reactive<ReportApi.ProjectOverviewExportReqVO>({
year: new Date().getFullYear(), year: currentYear,
specialtyCode: 'water', officeId: undefined,
sortType: 'output_desc' sortType: 'output_desc'
}) })
const employeeSummaryForm = reactive<ReportApi.EmployeeOutputSummaryExportReqVO>({ const employeeSummaryForm = reactive<ReportApi.EmployeeOutputSummaryExportReqVO>({
year: new Date().getFullYear(), year: currentYear,
officeId: undefined, officeId: undefined,
employeeId: undefined, employeeId: undefined,
employeeStatus: '在职', employeeStatus: undefined,
sortType: 'annual_total_desc' sortType: 'annual_total_desc'
}) })
const employeeBudgetQuery = reactive<EmployeeYearCostBudgetApi.EmployeeYearCostBudgetPageReqVO>({
pageNo: 1,
pageSize: 10,
employeeId: undefined,
employeeName: undefined,
budgetYear: currentYear,
enabledFlag: true
})
const projectEmployeeQuery = reactive<EmployeeApi.EmployeePageReqVO>({
pageNo: 1,
pageSize: 10,
employeeName: undefined,
officeId: undefined,
employeeStatus: undefined,
enabledFlag: true
})
const exportDialogTitle = computed(() =>
exportDialogType.value === 'employeeSummary' ? '导出员工产值汇总' : '导出项目产值总览'
)
const currentExportLoading = computed(() =>
exportDialogType.value === 'employeeSummary'
? employeeSummaryExportLoading.value
: projectOverviewExportLoading.value
)
const currentOfficeName = computed(() => {
return officeOptions.value.find((item) => item.id === projectOverviewForm.officeId)?.officeName || ''
})
const projectOverviewYearValue = computed({ const projectOverviewYearValue = computed({
get: () => (projectOverviewForm.year ? String(projectOverviewForm.year) : undefined), get: () => String(currentYear),
set: (value?: string) => { set: (_value?: string) => undefined
projectOverviewForm.year = value ? Number(value) : undefined
}
}) })
const employeeSummaryYearValue = computed({ const employeeSummaryYearValue = computed({
@@ -215,48 +279,90 @@ const employeeSummaryYearValue = computed({
} }
}) })
const formatAmount = (value?: number) => {
if (value === undefined || value === null) {
return ''
}
return Number(value).toFixed(2)
}
const getOfficeList = async () => { const getOfficeList = async () => {
officeOptions.value = await OfficeApi.getOfficeSimpleList() officeOptions.value = await OfficeApi.getOfficeSimpleList()
} }
const searchEmployees = async (keyword: string) => { const getEmployeeBudgetList = async () => {
employeeLoading.value = true if (!employeeBudgetQuery.budgetYear) {
employeeBudgetList.value = []
employeeBudgetTotal.value = 0
return
}
employeeBudgetLoading.value = true
try { try {
employeeOptions.value = await EmployeeApi.getEmployeeSimpleList({ const data = await EmployeeYearCostBudgetApi.getEmployeeYearCostBudgetPage(employeeBudgetQuery)
keyword, employeeBudgetList.value = data.list
officeId: employeeSummaryForm.officeId, employeeBudgetTotal.value = data.total
status: employeeSummaryForm.employeeStatus,
enabledFlag: true
})
} finally { } finally {
employeeLoading.value = false employeeBudgetLoading.value = false
} }
} }
const handleOfficeChange = () => { const getProjectEmployeeList = async () => {
employeeSummaryForm.employeeId = undefined if (!projectEmployeeQuery.officeId) {
searchEmployees('') projectEmployeeList.value = []
projectEmployeeTotal.value = 0
return
}
projectEmployeeLoading.value = true
try {
const data = await EmployeeApi.getEmployeePage(projectEmployeeQuery)
projectEmployeeList.value = data.list
projectEmployeeTotal.value = data.total
} finally {
projectEmployeeLoading.value = false
}
} }
const handleEmployeeStatusChange = () => { const openEmployeeSummaryExportDialog = () => {
employeeSummaryForm.employeeId = undefined if (!employeeSummaryForm.year) {
searchEmployees('')
}
const handleExportProjectOverview = async () => {
if (!projectOverviewForm.year) {
message.warning('请选择年度') message.warning('请选择年度')
return return
} }
if (!projectOverviewForm.specialtyCode) { exportDialogType.value = 'employeeSummary'
message.warning('请选择专业') exportDialogVisible.value = true
}
const openProjectOverviewExportDialog = () => {
if (!projectOverviewForm.officeId) {
message.warning('请选择专业所')
return
}
exportDialogType.value = 'projectOverview'
exportDialogVisible.value = true
}
const submitExport = async () => {
if (exportDialogType.value === 'employeeSummary') {
await handleExportEmployeeSummary()
return
}
await handleExportProjectOverview()
}
const handleExportProjectOverview = async () => {
if (!projectOverviewForm.officeId) {
message.warning('请选择专业所')
return
}
if (!projectOverviewYearValue.value) {
message.warning('请选择年度')
return return
} }
try { try {
await message.exportConfirm()
projectOverviewExportLoading.value = true projectOverviewExportLoading.value = true
projectOverviewForm.year = currentYear
const data = await ReportApi.exportProjectOverview(projectOverviewForm) const data = await ReportApi.exportProjectOverview(projectOverviewForm)
download.excel(data, '项目总览表.xlsx') download.excel(data, '项目总览表.xlsx')
exportDialogVisible.value = false
} finally { } finally {
projectOverviewExportLoading.value = false projectOverviewExportLoading.value = false
} }
@@ -268,17 +374,36 @@ const handleExportEmployeeSummary = async () => {
return return
} }
try { try {
await message.exportConfirm()
employeeSummaryExportLoading.value = true employeeSummaryExportLoading.value = true
const data = await ReportApi.exportEmployeeOutputSummary(employeeSummaryForm) const data = await ReportApi.exportEmployeeOutputSummary(employeeSummaryForm)
download.excel(data, '员工个人考核产值汇总.xlsx') download.excel(data, '员工个人考核产值汇总.xlsx')
exportDialogVisible.value = false
} finally { } finally {
employeeSummaryExportLoading.value = false employeeSummaryExportLoading.value = false
} }
} }
watch(
() => employeeSummaryForm.year,
(year) => {
employeeBudgetQuery.pageNo = 1
employeeBudgetQuery.budgetYear = year
getEmployeeBudgetList()
},
{ immediate: true }
)
watch(
() => projectOverviewForm.officeId,
(officeId) => {
projectEmployeeQuery.pageNo = 1
projectEmployeeQuery.officeId = officeId
getProjectEmployeeList()
},
{ immediate: true }
)
onMounted(async () => { onMounted(async () => {
await getOfficeList() await getOfficeList()
await searchEmployees('')
}) })
</script> </script>