Files
school-developer/src/views/stuwork/rewarddorm/index.vue
2026-01-29 16:38:09 +08:00

449 lines
14 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="modern-page-container">
<div class="page-wrapper">
<!-- æ<EFBFBD>œç´¢è¡¨å<EFBFBD>å<EFBFBD>¡ç -->
<el-card v-show="showSearch" class="search-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon class="title-icon"><Search /></el-icon>
ç­éæ<EFBFBD>¡ä»?
</span>
</div>
</template>
<el-form :model="state.queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList" class="search-form">
<el-form-item label="学年" prop="schoolYear">
<el-select
v-model="state.queryForm.schoolYear"
placeholder="请选择学年"
clearable
filterable
style="width: 200px">
<el-option
v-for="item in schoolYearList"
:key="item.year"
:label="item.year"
:value="item.year">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="学期" prop="schoolTerm">
<el-select
v-model="state.queryForm.schoolTerm"
placeholder="请选择学期"
clearable
style="width: 200px">
<el-option
v-for="item in schoolTermList"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="宿èˆ<C3A8>å<EFBFBD>? prop="roomNo">
<el-input
v-model="state.queryForm.roomNo"
placeholder="请输入宿èˆ<C3A8>å<EFBFBD>·"
clearable
style="width: 200px" />
</el-form-item>
<el-form-item label="å¥é¡¹å<C2B9><C3A5>ç§°" prop="ruleName">
<el-input
v-model="state.queryForm.ruleName"
placeholder="请输入å¥é¡¹å<C2B9><C3A5>ç§?
clearable
style="width: 200px" />
</el-form-item>
<el-form-item>
<el-button type="primary" plain icon="Search" @click="getDataList">查询</el-button>
<el-button icon="Refresh" @click="handleReset">é<EFBFBD>ç½®</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- å容å<EFBFBD>¡ç -->
<el-card class="content-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon class="title-icon"><Document /></el-icon>
ææ˜Žå®¿èˆ<EFBFBD>åˆè¡¨
</span>
<div class="header-actions">
<el-button
icon="Plus"
type="primary"
@click="formDialogRef.openDialog()">
æ°å¢ž
</el-button>
<el-button
icon="Upload"
type="success"
class="ml10"
@click="handleImport">
导å¥
</el-button>
<right-toolbar
v-model:showSearch="showSearch"
class="ml10"
@queryTable="getDataList">
<TableColumnControl
ref="columnControlRef"
:columns="tableColumns"
v-model="visibleColumns"
trigger-type="default"
trigger-circle
@change="handleColumnChange"
@order-change="handleColumnOrderChange"
>
<template #trigger>
<el-tooltip class="item" effect="dark" content="列设� placement="top">
<el-button circle style="margin-left: 0;">
<el-icon><Menu /></el-icon>
</el-button>
</el-tooltip>
</template>
</TableColumnControl>
</right-toolbar>
</div>
</div>
</template>
<!-- 表格 -->
<el-table
:data="state.dataList"
v-loading="state.loading"
stripe
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle"
class="modern-table">
<el-table-column type="index" label="åº<C3A5>å<EFBFBD>·" width="70" align="center">
<template #header>
<el-icon><List /></el-icon>
</template>
<template #default="{ $index }">
{{ $index + 1 + ((state.pagination?.current || 1) - 1) * (state.pagination?.size || 10) }}
</template>
</el-table-column>
<template v-for="col in visibleColumnsSorted" :key="col.prop || col.label">
<el-table-column
v-if="checkColumnVisible(col.prop || '') && col.prop !== 'æ“<C3A6>作'"
:prop="col.prop"
:label="col.label"
:width="col.width"
:min-width="col.minWidth"
:show-overflow-tooltip="col.showOverflowTooltip !== false"
:align="col.align || 'center'">
<template #header>
<el-icon v-if="col.icon"><component :is="col.icon" /></el-icon>
<span :style="{ marginLeft: col.icon ? '4px' : '0' }">{{ col.label }}</span>
</template>
<template #default="scope" v-if="col.prop === 'schoolTerm'">
<el-tag size="small" type="primary" effect="plain">
{{ formatSchoolTerm(scope.row.schoolTerm) }}
</el-tag>
</template>
<template #default="scope" v-else-if="col.prop === 'ruleName'">
<el-tag v-if="scope.row.ruleName" size="small" type="warning" effect="light">
{{ scope.row.ruleName }}
</el-tag>
<span v-else>-</span>
</template>
</el-table-column>
</template>
<el-table-column label="æ“<C3A6>作" width="150" align="center" fixed="right">
<template #header>
<el-icon><Setting /></el-icon>
<span style="margin-left: 4px">æ<EFBFBD>作</span>
</template>
<template #default="scope">
<el-button
icon="Edit"
link
type="primary"
@click="handleEdit(scope.row)">
ç¼è¾
</el-button>
<el-button
icon="Delete"
link
type="danger"
@click="handleDelete(scope.row)">
删é¤
</el-button>
</template>
</el-table-column>
</el-table>
<!-- åˆé¡µ -->
<div class="pagination-wrapper">
<pagination
@size-change="sizeChangeHandle"
@current-change="currentChangeHandle"
v-bind="state.pagination" />
</div>
</el-card>
</div>
<!-- æ°å¢ž/ç¼è¾è¡¨å<EFBFBD>å¼¹çª -->
<form-dialog ref="formDialogRef" @refresh="getDataList" />
<!-- 导å¥å¼¹çª -->
<el-dialog
title="å¯¼å…¥æ‡æ˜Žå®¿èˆ<C3A8>"
v-model="importDialogVisible"
:width="500"
:close-on-click-modal="false"
draggable>
<div style="margin-bottom: 15px;">
<el-button
icon="Download"
type="success"
@click="handleDownloadTemplate">
ä¸è½½æ¨¡æ<EFBFBD>¿
</el-button>
</div>
<el-upload
ref="uploadRef"
:auto-upload="false"
:on-change="handleFileChange"
:limit="1"
accept=".xlsx,.xls"
drag>
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
<div class="el-upload__text">
å°æä»æåˆ°æ­¤å¤ï¼Œæˆ?em>ç¹å»ä¸Šä¼ </em>
</div>
<template #tip>
<div class="el-upload__tip">
å<EFBFBD>ªèƒ½ä¸Šä¼  xlsx/xls æä»
</div>
</template>
</el-upload>
<template #footer>
<span class="dialog-footer">
<el-button @click="importDialogVisible = false">å<EFBFBD>?æ?/el-button>
<el-button type="primary" @click="handleImportSubmit" :disabled="!importFile || importLoading">ç¡?è®?/el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts" name="RewardDorm">
import { reactive, ref, onMounted, computed } from 'vue'
import { useRoute } from 'vue-router'
import { BasicTableProps, useTable } from "/@/hooks/table";
import { fetchList, delObj, importExcel } from "/@/api/stuwork/rewarddorm";
import { queryAllSchoolYear } from "/@/api/basic/basicyear";
import { getDicts } from "/@/api/admin/dict";
import { useMessage, useMessageBox } from "/@/hooks/message";
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
import { UploadFilled, List, Calendar, Clock, House, Trophy, EditPen, Setting, Menu, Search, Document } from '@element-plus/icons-vue'
import { useTableColumnControl } from '/@/hooks/tableColumnControl'
import FormDialog from './form.vue'
// 定义å<E280B0>˜é‡<C3A9>内容
const route = useRoute()
const formDialogRef = ref()
const uploadRef = ref()
const searchFormRef = ref()
const columnControlRef = ref()
const showSearch = ref(true)
const schoolYearList = ref<any[]>([])
const schoolTermList = ref<any[]>([])
const importDialogVisible = ref(false)
const importFile = ref<File | null>(null)
const importLoading = ref(false)
// 表格列é…<C3A9>ç½?
const tableColumns = [
{ prop: 'schoolYear', label: '学年', icon: Calendar },
{ prop: 'schoolTerm', label: '学期', icon: Clock },
{ prop: 'roomNo', label: '宿èˆ<C3A8>å<EFBFBD>?, icon: House },
{ prop: 'ruleName', label: 'å¥é¡¹å<EFBFBD><EFBFBD>ç§°', icon: Trophy, minWidth: 150 },
{ prop: 'remarks', label: '夿³¨', icon: EditPen, minWidth: 200 }
]
// 使用表格列控制hook
const {
visibleColumns,
visibleColumnsSorted,
checkColumnVisible,
handleColumnChange,
handleColumnOrderChange
} = useTableColumnControl(tableColumns, route.path)
// 表格样å¼<C3A5>
const tableStyle = {
cellStyle: { padding: '8px 0' },
headerCellStyle: { background: '#f5f7fa', color: '#606266', fontWeight: 'bold' }
}
// é…<C3A9>ç½® useTable
const state: BasicTableProps = reactive<BasicTableProps>({
queryForm: {
schoolYear: '',
schoolTerm: '',
roomNo: '',
ruleName: ''
},
pageList: fetchList,
props: {
item: 'records',
totalCount: 'total'
},
createdIsNeed: true // 页é<C2B5>¢åŠ è½½æ—¶è‡ªåŠ¨èŽ·å<C2B7>æ•°æ<C2B0>?
})
// table hook
const {
getDataList,
currentChangeHandle,
sizeChangeHandle,
tableStyle: _tableStyle
} = useTable(state)
// æ ¼å¼<C3A5>åŒå­¦æœ?
const formatSchoolTerm = (value: string | number) => {
if (value === null || value === undefined || value === '') {
return '-'
}
const dictItem = schoolTermList.value.find(item => item.value == value)
return dictItem ? dictItem.label : value
}
// é‡<C3A9>ç½®
const handleReset = () => {
searchFormRef.value?.resetFields()
state.queryForm.schoolYear = ''
state.queryForm.schoolTerm = ''
state.queryForm.roomNo = ''
state.queryForm.ruleName = ''
getDataList()
}
// 导入
const handleImport = () => {
importDialogVisible.value = true
importFile.value = null
uploadRef.value?.clearFiles()
}
// ä¸è½½æ¨¡æ<C2A1>¿
const handleDownloadTemplate = async () => {
try {
const fileName = 'ææ˜Žå®¿èˆ<EFBFBD>坼奿¨¡æ<EFBFBD>¿.xlsx'
// 使用动æ€<C3A6>导入获å<C2B7>æ‡ä»¶URL,从 views/stuwork/rewarddorm åˆ?assets/file 的相对路å¾?
const fileUrl = new URL(`../../../assets/file/${fileName}`, import.meta.url).href
const response = await fetch(fileUrl)
if (!response.ok) {
throw new Error('æä»ä¸è½½å¤±è´¥')
}
const blob = await response.blob()
const url = window.URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = fileName
document.body.appendChild(link)
link.click()
window.URL.revokeObjectURL(url)
document.body.removeChild(link)
useMessage().success('模æ<EFBFBD>¿ä¸è½½æˆ<EFBFBD>功')
} catch (error) {
useMessage().error('模æ<EFBFBD>¿ä¸è½½å¤±è´¥ï¼Œè¯·æ£æŸ¥æ¨¡æ<EFBFBD>¿æä»æ˜¯å<EFBFBD>¦å­˜åœ?)
}
}
// æ‡ä»¶å<C2B6>˜åŒ
const handleFileChange = (file: any) => {
importFile.value = file.raw
}
// æ<><C3A6>交导入
const handleImportSubmit = async () => {
if (!importFile.value) {
useMessage().warning('请选æ©è¦<C3A8>导入的æ‡ä»¶')
return
}
importLoading.value = true
try {
await importExcel(importFile.value)
useMessage().success('导入æˆ<C3A6>功')
importDialogVisible.value = false
importFile.value = null
uploadRef.value?.clearFiles()
getDataList()
} catch (err: any) {
useMessage().error(err.msg || '导入失败')
} finally {
importLoading.value = false
}
}
// 编辑
const handleEdit = (row: any) => {
formDialogRef.value?.openDialog('edit', row)
}
// 删除
const handleDelete = async (row: any) => {
const { confirm } = useMessageBox()
try {
await confirm('确定è¦<C3A8>åˆ é™¤è¯¥æ‡æ˜Žå®¿èˆ<C3A8>记录å<E280A2>—?')
await delObj([row.id])
useMessage().success(ˆ é™¤æˆ<C3A6>功')
getDataList()
} catch (err: any) {
if (err !== 'cancel') {
useMessage().error(err.msg || '删除失败')
}
}
}
// 获å<C2B7>学年列表
const getSchoolYearList = async () => {
try {
const res = await queryAllSchoolYear()
if (res.data && Array.isArray(res.data)) {
schoolYearList.value = res.data
} else {
schoolYearList.value = []
}
} catch (err) {
schoolYearList.value = []
}
}
// 获å<C2B7>学期字典
const getSchoolTermDict = async () => {
try {
const res = await getDicts('school_term')
if (res.data && Array.isArray(res.data)) {
schoolTermList.value = res.data.map((item: any) => ({
label: item.label || item.dictLabel || item.name,
value: item.value || item.dictValue || item.code
}))
} else {
schoolTermList.value = []
}
} catch (err) {
schoolTermList.value = []
}
}
// åˆ<C3A5>å§åŒ?
onMounted(() => {
getSchoolYearList()
getSchoolTermDict()
})
</script>
<style scoped lang="scss">
@import '/@/assets/styles/modern-page.scss';
</style>