| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556 |
- <template>
- <div class="p-4">
- <div class="mb-6 p-4 bg-white rounded-lg shadow-sm flex flex-wrap items-center gap-4">
- <el-form :inline="true" :model="searchForm" class="flex-grow flex flex-wrap">
- <el-form-item>
- <span slot="label" class="font-size-4.5">部门名称:</span>
- <el-input
- v-model="searchForm.departmentName"
- style="width: 240px"
- size="default"
- placeholder="请输入部门名称"
- @clear="handleSearch"
- />
- </el-form-item>
- <el-form-item>
- <el-button type="primary" :icon="Search" @click="handleSearch" size="search">查询
- </el-button>
- <el-button :icon="Refresh" @click="handleResetSearch" size="search">重置</el-button>
- </el-form-item>
- </el-form>
- <div class="flex gap-1" style="justify-content:flex-end">
- <el-button type="primary" :icon="Plus" @click="handleAdd" size="search">新增</el-button>
- <el-button
- type="danger"
- :icon="Delete"
- @click="handleBatchDelete"
- :disabled="selectedIds.length === 0"
- size="search"
- >批量删除
- </el-button
- >
- <el-button type="success" :icon="Download" @click="exportExcel" size="search">导出</el-button>
- <el-upload
- class="inline-block ml-2"
- action="/api/edepartmentEmployeeStats/importData"
- :show-file-list="false"
- :on-success="handleUploadSuccess"
- :on-error="handleUploadError"
- :before-upload="handleBeforeUpload"
- :http-request="(options) => importExcel(options.file)"
- >
- <el-button type="info" :icon="Upload" size="search">导入</el-button>
- </el-upload>
- </div>
- </div>
- <!-- Table -->
- <div class="bg-white rounded-lg shadow-sm p-4 overflow-x-auto">
- <el-table
- :data="tableData"
- style="width: 100%"
- border
- @selection-change="handleSelectionChange"
- :cell-style="{ fontSize: '14px' }"
- :header-cell-style="{ fontSize: '14px' }"
- :row-style="{ fontSize: '14px' }"
- >
- <el-table-column type="selection" width="55" fixed="left"/>
- <el-table-column prop="departmentName" label="部门名称" align="center"/>
- <el-table-column prop="domesticEmployees" label="内籍员工数量 " align="center"/>
- <el-table-column prop="educationDistribution" label="学历分布 " align="center"/>
- <el-table-column prop="femaleRatio" label="女工比例" align="center"/>
- <el-table-column prop="foreignEmployees" label="外籍员工数量" align="center"/>
- <el-table-column prop="maleRatio" label="男工比例" align="center"/>
- <el-table-column prop="totalEmployees" label="员工总数" align="center"/>
- <el-table-column prop="ageDistribution" label="年龄分布" align="center"/>
- <el-table-column label="操作" fixed="right" align="center">
- <template #default="{ row }">
- <el-button :icon="Edit" size="small" @click="handleEdit(row)">编辑</el-button>
- <el-button type="danger" :icon="Delete" size="small" @click="handleDelete(row.id)"
- >删除
- </el-button
- >
- </template>
- </el-table-column>
- </el-table>
- <!-- Pagination -->
- <div class="mt-4 flex justify-end">
- <el-pagination
- @size-change="handleSizeChange"
- @current-change="handleCurrentChange"
- :current-page="pageNum"
- :page-sizes="[10, 20, 50, 100]"
- :page-size="pageSize"
- layout="total, sizes, prev, pager, next, jumper"
- :total="total"
- />
- </div>
- </div>
- <!-- Add/Edit Dialog -->
- <el-dialog
- v-model="dialogVisible"
- :title="dialogTitle"
- width="800px"
- :close-on-click-modal="false"
- :close-on-press-escape="false"
- >
- <el-form
- :model="formData"
- ref="formRef"
- label-width="120px"
- class="grid grid-cols-1 md:grid-cols-2 gap-4"
- >
- <el-form-item
- label="部门名称"
- prop="departmentName"
- :rules="[{ required: true, message: '请输入部门名称', trigger: 'blur' }]"
- >
- <el-input v-model="formData.departmentName" placeholder="请输入部门名称"/>
- </el-form-item>
- <el-form-item
- label="内籍员工数量"
- prop="domesticEmployees"
- :rules="[{ required: true, message: '请输入内籍员工数量', trigger: 'blur' }]"
- >
- <el-input
- v-model="formData.domesticEmployees"
- placeholder="请输入内籍员工数量"
- />
- </el-form-item>
- <el-form-item
- label="学历分布"
- prop="educationDistribution"
- :rules="[{ required: true, message: '请输入学历分布', trigger: 'blur' }]"
- >
- <el-input
- v-model="formData.educationDistribution"
- placeholder="请输入学历分布"
- />
- </el-form-item>
- <el-form-item
- label="女工比例"
- prop="femaleRatio"
- :rules="[{ required: true, message: '请输入女工比例', trigger: 'blur' }]"
- >
- <el-input
- v-model="formData.femaleRatio"
- placeholder="请输入女工比例"
- />
- </el-form-item>
- <el-form-item
- label="外籍员工数量"
- prop="foreignEmployees"
- :rules="[
- { required: true, message: '请输入外籍员工数量', trigger: 'blur' },
- { type: 'number', message: '请输入数字', trigger: 'blur' },
- ]"
- >
- <el-input v-model.number="formData.foreignEmployees" placeholder="请输入外籍员工数量"/>
- </el-form-item>
- <el-form-item
- label="男工比例"
- prop="maleRatio"
- :rules="[
- { required: true, message: '请输入男工比例', trigger: 'blur' },
- { type: 'number', message: '请输入数字', trigger: 'blur' },
- ]"
- >
- <el-input v-model.number="formData.maleRatio" placeholder="请输入男工比例"/>
- </el-form-item>
- <el-form-item
- label="员工总数"
- prop="totalEmployees"
- :rules="[
- { required: true, message: '请输入员工总数', trigger: 'blur' },
- { type: 'number', message: '请输入数字', trigger: 'blur' },
- ]"
- >
- <el-input v-model.number="formData.totalEmployees" placeholder="请输入员工总数"/>
- </el-form-item>
- <el-form-item
- label="年龄分布"
- prop="ageDistribution"
- :rules="[
- { required: true, message: '请输入年龄分布', trigger: 'blur' },
- { type: 'number', message: '请输入数字', trigger: 'blur' },
- ]"
- >
- <el-input v-model.number="formData.ageDistribution" placeholder="请输入年龄分布"/>
- </el-form-item>
- </el-form>
- <template #footer>
- <span class="dialog-footer">
- <el-button @click="dialogVisible = false">取消</el-button>
- <el-button type="primary" @click="handleConfirm">确定</el-button>
- </span>
- </template>
- </el-dialog>
- </div>
- </template>
- <script setup lang="ts">
- import {onMounted, reactive, ref} from 'vue'
- import {clientDownloadExcel, clientGet, clientPost} from '@/utils/request.ts'
- import type {FormInstance, UploadProps} from 'element-plus'
- import {ElForm, ElLoading, ElMessage, ElMessageBox} from 'element-plus'
- import {Delete, Download, Edit, Plus, Refresh, Search, Upload} from '@element-plus/icons-vue'
- // 假设 BaseResponse 和 PageType 在全局或 request.ts 中已定义
- interface BaseResponse {
- code: number
- msg: string
- }
- interface PageType<T> {
- records: T[]
- total: number
- size: number
- current: number
- pages: number
- }
- interface Property {
- id: string // 主键ID
- departmentName: string // 部门名称
- domesticEmployees: string // 内籍员工数量
- educationDistribution: string // 学历分布
- femaleRatio: string // 女工比例
- foreignEmployees: string // 外籍员工数量
- maleRatio: string // 男工比例
- totalEmployees: string // 员工总数
- ageDistribution: string // 年龄分布
- }
- interface PropertyOneResponse extends BaseResponse {
- data: Property
- }
- interface PropertyListResponse extends BaseResponse {
- data: PageType<Property>
- }
- interface AddProperty {
- departmentName: string // 部门名称
- domesticEmployees: string // 内籍员工数量
- educationDistribution: string // 学历分布
- femaleRatio: string // 女工比例
- foreignEmployees: string // 外籍员工数量
- maleRatio: string // 男工比例
- totalEmployees: string // 员工总数
- ageDistribution: string // 年龄分布
- }
- interface UpdateProperty {
- id?: string
- departmentName: string // 部门名称
- domesticEmployees: string // 内籍员工数量
- educationDistribution: string // 学历分布
- femaleRatio: string // 女工比例
- foreignEmployees: string // 外籍员工数量
- maleRatio: string // 男工比例
- totalEmployees: string // 员工总数
- ageDistribution: string // 年龄分布
- }
- // 响应式状态变量
- const tableData = ref<Property[]>([])
- const total = ref(0)
- const pageSize = ref(10)
- const pageNum = ref(1)
- const searchForm = reactive({
- departmentName: ''
- })
- const dialogVisible = ref(false)
- const dialogTitle = ref('')
- const isEdit = ref(false)
- const formData = reactive<Property>({})
- const formRef = ref<FormInstance>()
- const selectedIds = ref<string[]>([])
- // 规上企业是否
- const arrearsOptions = [
- {label: '是', value: '是'},
- {label: '否', value: '否'},
- ]
- // 年度选项(动态生成最近5年)
- const currentYear = new Date().getFullYear()
- // 新增
- const add = async (data: AddProperty) => {
- const res = await clientPost<AddProperty, BaseResponse>('/edepartmentEmployeeStats/save', {
- ...data,
- })
- if (res.code !== 200) {
- ElMessage.error(res.msg)
- return false
- }
- ElMessage.success(res.msg)
- return true
- }
- // 根据id获取数据
- const getById = async (id: string) => {
- const res = await clientGet<null, PropertyOneResponse>('/edepartmentEmployeeStats/getById/' + id)
- if (res.code !== 200) {
- ElMessage.error(res.msg)
- return null
- }
- return res.data
- }
- // 分页获取数据
- const getList = async () => {
- // 构建参数对象,只包含有值的搜索条件
- const params = {
- pageNum: pageNum.value,
- pageSize: pageSize.value,
- ...(searchForm.departmentName ? {departmentName: searchForm.departmentName} : {})
- }
- const res = await clientGet<{
- pageNum: number
- pageSize: number
- departmentName?: string
- },
- PropertyListResponse>('/edepartmentEmployeeStats/findByPage', {
- params,
- })
- if (res.code !== 200) {
- ElMessage.error(res.msg)
- return
- }
- tableData.value = res.data.records
- total.value = res.data.total
- }
- // 批量删除
- const delBatch = async (ids: string[]) => {
- const res = await clientPost<string[], BaseResponse>('/edepartmentEmployeeStats/deleteBatch', ids)
- if (res.code !== 200) {
- ElMessage.error(res.msg)
- return false
- }
- ElMessage.success(res.msg)
- return true
- }
- // 修改
- const update = async (data: UpdateProperty) => {
- const res = await clientPost<UpdateProperty, BaseResponse>('/edepartmentEmployeeStats/update', {
- ...data,
- })
- if (res.code !== 200) {
- ElMessage.error(res.msg)
- return false
- }
- ElMessage.success(res.msg)
- return true
- }
- // 导出为excel
- const exportExcel = async () => {
- const loading = ElLoading.service({
- lock: true,
- text: '导出中,请稍候...',
- background: 'rgba(0, 0, 0, 0.1)',
- fullscreen: true,
- })
- try {
- // 构建参数对象,只包含有值的搜索条件
- const params = {
- ...(searchForm.departmentName ? {departmentName: searchForm.departmentName} : {})
- }
- await clientDownloadExcel('/edepartmentEmployeeStats/exportData', {
- params,
- })
- ElMessage.success('导出成功')
- } catch (error) {
- console.error('导出失败:', error)
- ElMessage.error('导出失败')
- } finally {
- loading.close()
- }
- }
- // excel导入
- const importExcel = async (file: File) => {
- const formData = new FormData()
- formData.append('file', file)
- const res = await clientPost<FormData, BaseResponse>(
- '/edepartmentEmployeeStats/importData',
- formData,
- {
- headers: {
- 'Content-Type': 'multipart/form-data',
- },
- },
- )
- if (res.code !== 200) {
- ElMessage.error(res.msg)
- return false
- }
- ElMessage.success(res.msg)
- getList()
- return true
- }
- // --- CRUD 操作和处理函数 ---
- const handleAdd = () => {
- isEdit.value = false
- dialogTitle.value = '新增用企业信息'
- // 重置表单数据
- Object.keys(formData).forEach((key) => delete formData[key as keyof Property])
- // 设置默认值为当前年度
- formData.openingDate = `${currentYear}`
- dialogVisible.value = true
- }
- const handleEdit = async (row: Property) => {
- isEdit.value = true
- dialogTitle.value = '编辑企业信息'
- const data = await getById(row.id)
- if (data) {
- Object.assign(formData, data)
- dialogVisible.value = true
- }
- }
- const handleDelete = async (id: string) => {
- ElMessageBox.confirm('确定删除此条记录吗?', '提示', {
- confirmButtonText: '确定',
- cancelButtonText: '取消',
- type: 'warning',
- })
- .then(async () => {
- const success = await delBatch([id])
- if (success) {
- getList()
- }
- })
- .catch(() => {
- ElMessage.info('已取消删除')
- })
- }
- const handleBatchDelete = () => {
- if (selectedIds.value.length === 0) {
- ElMessage.warning('请选择要删除的记录')
- return
- }
- ElMessageBox.confirm(`确定删除选中的 ${selectedIds.value.length} 条记录吗?`, '提示', {
- confirmButtonText: '确定',
- cancelButtonText: '取消',
- type: 'warning',
- })
- .then(async () => {
- const success = await delBatch(selectedIds.value)
- if (success) {
- getList()
- }
- })
- .catch(() => {
- ElMessage.info('已取消批量删除')
- })
- }
- const handleSearch = () => {
- pageNum.value = 1 // 搜索时重置到第一页
- getList()
- }
- const handleResetSearch = () => {
- searchForm.departmentName = ''
- pageNum.value = 1
- getList()
- }
- const handleConfirm = async () => {
- if (!formRef.value) return
- await formRef.value.validate(async (valid) => {
- if (valid) {
- let success = false
- const dataToSend = {...formData}
- if (isEdit.value) {
- success = await update(dataToSend)
- } else {
- success = await add(dataToSend)
- }
- if (success) {
- dialogVisible.value = false
- getList()
- }
- } else {
- ElMessage.error('请检查表单填写')
- }
- })
- }
- const handleCurrentChange = (val: number) => {
- pageNum.value = val
- getList()
- }
- const handleSizeChange = (val: number) => {
- pageSize.value = val
- pageNum.value = 1 // 页大小改变时重置到第一页
- getList()
- }
- const handleSelectionChange = (selection: Property[]) => {
- selectedIds.value = selection.map((item) => item.id)
- }
- const handleUploadSuccess: UploadProps['onSuccess'] = async (response, uploadFile) => {
- if (response && response.code === 200) {
- ElMessage.success('文件导入成功')
- getList()
- } else {
- ElMessage.error(`文件导入失败: ${response?.msg || '未知错误'}`)
- }
- }
- const handleUploadError: UploadProps['onError'] = (error) => {
- ElMessage.error(`文件导入失败: ${error.message}`)
- }
- const handleBeforeUpload: UploadProps['beforeUpload'] = (rawFile) => {
- const isExcel =
- rawFile.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' ||
- rawFile.type === 'application/vnd.ms-excel'
- if (!isExcel) {
- ElMessage.error('导入文件只能是 Excel 格式!')
- return false
- }
- return true
- }
- const init = () => {
- getList()
- };
- onMounted(() => {
- init()
- })
- </script>
- <style scoped>
- /* Add any specific styles here if needed, otherwise UnoCSS handles most */
- </style>
|