merge code
This commit is contained in:
663
docs/tableHook使用方式.md
Normal file
663
docs/tableHook使用方式.md
Normal file
@@ -0,0 +1,663 @@
|
|||||||
|
# useTableColumnControl Hook 使用文档
|
||||||
|
|
||||||
|
## 一、简介
|
||||||
|
|
||||||
|
`useTableColumnControl` 是一个用于统一管理表格列显示/隐藏和排序功能的 Vue 3 Composition API Hook。它封装了列配置的加载、保存、同步等逻辑,让开发者只需几行代码即可实现完整的表格列控制功能。
|
||||||
|
|
||||||
|
### 主要功能
|
||||||
|
- ✅ 列显示/隐藏控制
|
||||||
|
- ✅ 列排序管理
|
||||||
|
- ✅ 配置自动保存到本地存储
|
||||||
|
- ✅ 配置同步到后端服务器
|
||||||
|
- ✅ 自动加载已保存的配置
|
||||||
|
- ✅ 类型安全支持
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、安装和引入
|
||||||
|
|
||||||
|
### 文件位置
|
||||||
|
```
|
||||||
|
src/hooks/tableColumn.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
### 引入方式
|
||||||
|
```typescript
|
||||||
|
import { useTableColumnControl, type TableColumn } from '/@/hooks/tableColumn'
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、基础使用
|
||||||
|
|
||||||
|
### 1. 引入公共样式(推荐)
|
||||||
|
|
||||||
|
首先在 `<style>` 中引入现代化页面布局样式:
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
|
</style>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 定义表格列配置
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { User, Calendar, Phone } from '@element-plus/icons-vue'
|
||||||
|
|
||||||
|
const tableColumns = [
|
||||||
|
{ prop: 'name', label: '姓名', icon: User },
|
||||||
|
{ prop: 'age', label: '年龄', icon: Calendar },
|
||||||
|
{ prop: 'phone', label: '电话', icon: Phone }
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 使用 Hook
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const {
|
||||||
|
visibleColumns,
|
||||||
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
|
handleColumnChange,
|
||||||
|
handleColumnOrderChange
|
||||||
|
} = useTableColumnControl(tableColumns)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 在模板中使用
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<div class="modern-page-container">
|
||||||
|
<div class="page-wrapper">
|
||||||
|
<!-- 搜索表单卡片 -->
|
||||||
|
<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>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form class="search-form">
|
||||||
|
<!-- 搜索表单项 -->
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<!-- 内容卡片 -->
|
||||||
|
<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>
|
||||||
|
数据列表
|
||||||
|
</span>
|
||||||
|
<div class="header-actions">
|
||||||
|
<!-- TableColumnControl 组件 -->
|
||||||
|
<TableColumnControl
|
||||||
|
:columns="tableColumns"
|
||||||
|
v-model="visibleColumns"
|
||||||
|
@change="handleColumnChange"
|
||||||
|
@order-change="handleColumnOrderChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 表格 -->
|
||||||
|
<el-table :data="dataList" stripe class="modern-table">
|
||||||
|
<el-table-column type="index" label="序号" width="70">
|
||||||
|
<template #default="{ $index }">
|
||||||
|
{{ $index + 1 + ((pagination?.current || 1) - 1) * (pagination?.size || 10) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<!-- 动态渲染列 -->
|
||||||
|
<template v-for="col in visibleColumnsSorted" :key="col.prop">
|
||||||
|
<el-table-column
|
||||||
|
v-if="checkColumnVisible(col.prop || '')"
|
||||||
|
:prop="col.prop"
|
||||||
|
:label="col.label"
|
||||||
|
:width="col.width"
|
||||||
|
:min-width="col.minWidth"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<el-icon v-if="col.icon">
|
||||||
|
<component :is="col.icon" />
|
||||||
|
</el-icon>
|
||||||
|
<span style="margin-left: 4px">{{ col.label }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<div class="pagination-wrapper">
|
||||||
|
<pagination v-bind="pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 四、完整示例
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<right-toolbar>
|
||||||
|
<TableColumnControl
|
||||||
|
ref="columnControlRef"
|
||||||
|
:columns="tableColumns"
|
||||||
|
v-model="visibleColumns"
|
||||||
|
trigger-type="default"
|
||||||
|
trigger-circle
|
||||||
|
@change="handleColumnChange"
|
||||||
|
@order-change="handleColumnOrderChange"
|
||||||
|
>
|
||||||
|
<template #trigger>
|
||||||
|
<el-tooltip content="列设置" placement="top">
|
||||||
|
<el-button circle>
|
||||||
|
<el-icon><Menu /></el-icon>
|
||||||
|
</el-button>
|
||||||
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
</TableColumnControl>
|
||||||
|
</right-toolbar>
|
||||||
|
|
||||||
|
<el-table :data="state.dataList" stripe>
|
||||||
|
<el-table-column type="index" label="序号" width="70">
|
||||||
|
<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">
|
||||||
|
<el-table-column
|
||||||
|
v-if="checkColumnVisible(col.prop || '')"
|
||||||
|
:prop="col.prop"
|
||||||
|
:label="col.label"
|
||||||
|
:width="col.width"
|
||||||
|
:min-width="col.minWidth"
|
||||||
|
show-overflow-tooltip
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<el-icon v-if="col.icon">
|
||||||
|
<component :is="col.icon" />
|
||||||
|
</el-icon>
|
||||||
|
<span style="margin-left: 4px">{{ col.label }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<el-table-column label="操作" width="180" fixed="right">
|
||||||
|
<!-- 操作列内容 -->
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
import { User, Calendar, Phone, Menu } from '@element-plus/icons-vue'
|
||||||
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
|
|
||||||
|
// 定义表格列
|
||||||
|
const tableColumns = [
|
||||||
|
{ prop: 'name', label: '姓名', icon: User },
|
||||||
|
{ prop: 'age', label: '年龄', icon: Calendar },
|
||||||
|
{ prop: 'phone', label: '电话', icon: Phone }
|
||||||
|
]
|
||||||
|
|
||||||
|
// 使用 Hook
|
||||||
|
const {
|
||||||
|
visibleColumns,
|
||||||
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
|
handleColumnChange,
|
||||||
|
handleColumnOrderChange
|
||||||
|
} = useTableColumnControl(tableColumns)
|
||||||
|
|
||||||
|
const columnControlRef = ref()
|
||||||
|
const state = ref({
|
||||||
|
dataList: [],
|
||||||
|
pagination: { current: 1, size: 10 }
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 五、API 参考
|
||||||
|
|
||||||
|
### TableColumn 类型定义
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface TableColumn {
|
||||||
|
prop?: string // 列属性名
|
||||||
|
label?: string // 列标题
|
||||||
|
icon?: any // 列图标组件
|
||||||
|
width?: number | string // 列宽度
|
||||||
|
minWidth?: number | string // 最小宽度
|
||||||
|
showOverflowTooltip?: boolean // 是否显示溢出提示
|
||||||
|
align?: 'left' | 'center' | 'right' // 对齐方式
|
||||||
|
alwaysShow?: boolean // 是否始终显示(不参与列控制)
|
||||||
|
fixed?: boolean | 'left' | 'right' // 是否固定列
|
||||||
|
[key: string]: any // 其他自定义属性
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Hook 返回值
|
||||||
|
|
||||||
|
| 属性/方法 | 类型 | 说明 |
|
||||||
|
|----------|------|------|
|
||||||
|
| `visibleColumns` | `Ref<string[]>` | 当前可见的列 key 数组 |
|
||||||
|
| `columnOrder` | `Ref<string[]>` | 列排序顺序数组 |
|
||||||
|
| `visibleColumnsSorted` | `ComputedRef<TableColumn[]>` | 排序后的可见列配置数组 |
|
||||||
|
| `checkColumnVisible` | `(prop: string) => boolean` | 检查列是否可见 |
|
||||||
|
| `handleColumnChange` | `(value: string[]) => void` | 处理列显示变化 |
|
||||||
|
| `handleColumnOrderChange` | `(order: string[]) => void` | 处理列排序变化 |
|
||||||
|
| `loadSavedConfig` | `() => void` | 手动加载保存的配置 |
|
||||||
|
|
||||||
|
### Hook 选项参数
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface Options {
|
||||||
|
autoLoad?: boolean // 是否自动加载配置,默认 true
|
||||||
|
storageKey?: string // 自定义存储 key,默认使用路由路径
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六、高级用法
|
||||||
|
|
||||||
|
### 1. 自定义存储 Key
|
||||||
|
|
||||||
|
如果不想使用默认的路由路径作为存储 key,可以自定义:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const {
|
||||||
|
visibleColumns,
|
||||||
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
|
handleColumnChange,
|
||||||
|
handleColumnOrderChange
|
||||||
|
} = useTableColumnControl(tableColumns, {
|
||||||
|
storageKey: 'custom-table-columns-key'
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 禁用自动加载
|
||||||
|
|
||||||
|
如果需要在特定时机手动加载配置:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const {
|
||||||
|
visibleColumns,
|
||||||
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
|
handleColumnChange,
|
||||||
|
handleColumnOrderChange,
|
||||||
|
loadSavedConfig
|
||||||
|
} = useTableColumnControl(tableColumns, {
|
||||||
|
autoLoad: false
|
||||||
|
})
|
||||||
|
|
||||||
|
// 在需要的时候手动加载
|
||||||
|
onMounted(() => {
|
||||||
|
loadSavedConfig()
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 固定列和始终显示的列
|
||||||
|
|
||||||
|
某些列(如操作列)不应该参与列控制:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const tableColumns = [
|
||||||
|
{ prop: 'name', label: '姓名', icon: User },
|
||||||
|
{ prop: 'age', label: '年龄', icon: Calendar },
|
||||||
|
{
|
||||||
|
prop: 'action',
|
||||||
|
label: '操作',
|
||||||
|
alwaysShow: true, // 始终显示
|
||||||
|
fixed: 'right' // 固定在右侧
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 特殊列模板
|
||||||
|
|
||||||
|
如果需要为某些列添加特殊模板:
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template v-for="col in visibleColumnsSorted" :key="col.prop">
|
||||||
|
<el-table-column
|
||||||
|
v-if="checkColumnVisible(col.prop || '')"
|
||||||
|
:prop="col.prop"
|
||||||
|
:label="col.label"
|
||||||
|
>
|
||||||
|
<!-- 状态列特殊模板 -->
|
||||||
|
<template v-if="col.prop === 'status'" #default="scope">
|
||||||
|
<el-tag :type="scope.row.status === '1' ? 'success' : 'warning'">
|
||||||
|
{{ scope.row.status }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 其他列默认显示 -->
|
||||||
|
<template v-else #default="scope">
|
||||||
|
{{ scope.row[col.prop] }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 七、注意事项
|
||||||
|
|
||||||
|
### 1. 列 Key 的唯一性
|
||||||
|
|
||||||
|
确保每列的 `prop` 或 `label` 是唯一的,因为 Hook 使用它们作为标识:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ 正确
|
||||||
|
const tableColumns = [
|
||||||
|
{ prop: 'name', label: '姓名' },
|
||||||
|
{ prop: 'age', label: '年龄' }
|
||||||
|
]
|
||||||
|
|
||||||
|
// ❌ 错误:两个列都没有 prop
|
||||||
|
const tableColumns = [
|
||||||
|
{ label: '姓名' },
|
||||||
|
{ label: '年龄' }
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 响应式更新
|
||||||
|
|
||||||
|
`tableColumns` 应该是响应式的,如果需要在运行时动态修改列配置:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const tableColumns = ref([
|
||||||
|
{ prop: 'name', label: '姓名' },
|
||||||
|
{ prop: 'age', label: '年龄' }
|
||||||
|
])
|
||||||
|
|
||||||
|
// 动态添加列
|
||||||
|
tableColumns.value.push({ prop: 'phone', label: '电话' })
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 配置存储位置
|
||||||
|
|
||||||
|
- **本地存储**:使用 `localStorage`,key 为 `user-table-configs-all`
|
||||||
|
- **后端存储**:通过 API 同步,key 为 `user-table-configs`
|
||||||
|
- **页面存储**:每个页面的配置通过路由路径生成唯一 key
|
||||||
|
|
||||||
|
### 4. 配置加载时机
|
||||||
|
|
||||||
|
Hook 默认在组件挂载时自动加载配置。如果需要在数据加载后重新加载:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const { loadSavedConfig } = useTableColumnControl(tableColumns)
|
||||||
|
|
||||||
|
watch(() => someData.value, () => {
|
||||||
|
// 数据变化后重新加载配置
|
||||||
|
loadSavedConfig()
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 八、扩展方式
|
||||||
|
|
||||||
|
### 1. 添加列宽保存功能
|
||||||
|
|
||||||
|
如果需要保存列宽,可以扩展 Hook:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 扩展 Hook
|
||||||
|
function useTableColumnControlWithWidth(tableColumns: TableColumn[]) {
|
||||||
|
const baseHook = useTableColumnControl(tableColumns)
|
||||||
|
const columnWidths = ref<Record<string, number>>({})
|
||||||
|
|
||||||
|
const handleColumnWidthChange = (prop: string, width: number) => {
|
||||||
|
columnWidths.value[prop] = width
|
||||||
|
// 保存到本地存储
|
||||||
|
const storageKey = getStorageKey()
|
||||||
|
const config = getTableConfigFromLocal(storageKey) || {}
|
||||||
|
config.columnWidths = columnWidths.value
|
||||||
|
saveTableConfigToLocal(storageKey, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...baseHook,
|
||||||
|
columnWidths,
|
||||||
|
handleColumnWidthChange
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 添加列固定功能
|
||||||
|
|
||||||
|
如果需要保存列的固定状态:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function useTableColumnControlWithFixed(tableColumns: TableColumn[]) {
|
||||||
|
const baseHook = useTableColumnControl(tableColumns)
|
||||||
|
const fixedColumns = ref<string[]>([])
|
||||||
|
|
||||||
|
const toggleColumnFixed = (prop: string) => {
|
||||||
|
const index = fixedColumns.value.indexOf(prop)
|
||||||
|
if (index > -1) {
|
||||||
|
fixedColumns.value.splice(index, 1)
|
||||||
|
} else {
|
||||||
|
fixedColumns.value.push(prop)
|
||||||
|
}
|
||||||
|
// 保存配置
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...baseHook,
|
||||||
|
fixedColumns,
|
||||||
|
toggleColumnFixed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 添加列分组功能
|
||||||
|
|
||||||
|
如果需要按分组管理列:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function useTableColumnControlWithGroup(
|
||||||
|
tableColumns: TableColumn[],
|
||||||
|
groups: Record<string, string[]>
|
||||||
|
) {
|
||||||
|
const baseHook = useTableColumnControl(tableColumns)
|
||||||
|
|
||||||
|
const getColumnsByGroup = (groupName: string) => {
|
||||||
|
return tableColumns.filter(col =>
|
||||||
|
groups[groupName]?.includes(col.prop || '')
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...baseHook,
|
||||||
|
getColumnsByGroup
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 自定义存储策略
|
||||||
|
|
||||||
|
如果需要使用不同的存储策略(如 IndexedDB):
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function useTableColumnControlWithCustomStorage(
|
||||||
|
tableColumns: TableColumn[],
|
||||||
|
storageAdapter: {
|
||||||
|
get: (key: string) => Promise<any>
|
||||||
|
set: (key: string, value: any) => Promise<void>
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
// 实现自定义存储逻辑
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 九、常见问题
|
||||||
|
|
||||||
|
### Q1: 为什么配置没有保存?
|
||||||
|
|
||||||
|
**A:** 检查以下几点:
|
||||||
|
1. 确保 `handleColumnChange` 和 `handleColumnOrderChange` 已正确绑定到组件
|
||||||
|
2. 检查浏览器控制台是否有错误
|
||||||
|
3. 确认 API 调用是否成功(检查网络请求)
|
||||||
|
|
||||||
|
### Q2: 为什么某些列无法隐藏?
|
||||||
|
|
||||||
|
**A:** 检查列配置中是否设置了 `alwaysShow: true` 或 `fixed` 属性:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 这些列无法隐藏
|
||||||
|
{ prop: 'action', label: '操作', alwaysShow: true }
|
||||||
|
{ prop: 'id', label: 'ID', fixed: 'left' }
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q3: 如何重置列配置?
|
||||||
|
|
||||||
|
**A:** 可以手动清除本地存储或调用 API 删除配置:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 清除本地存储
|
||||||
|
localStorage.removeItem('user-table-configs-all')
|
||||||
|
|
||||||
|
// 或通过 API 删除
|
||||||
|
import { deleteUserTableConfig } from '/@/api/admin/usertable'
|
||||||
|
deleteUserTableConfig(storageKey)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q4: 如何在多个页面共享列配置?
|
||||||
|
|
||||||
|
**A:** 使用相同的 `storageKey`:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const { visibleColumns } = useTableColumnControl(tableColumns, {
|
||||||
|
storageKey: 'shared-table-columns'
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 十、最佳实践
|
||||||
|
|
||||||
|
### 1. 列配置管理
|
||||||
|
|
||||||
|
将列配置提取到单独的文件中,便于维护:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// src/views/xxx/columns.ts
|
||||||
|
import { User, Calendar } from '@element-plus/icons-vue'
|
||||||
|
|
||||||
|
export const tableColumns = [
|
||||||
|
{ prop: 'name', label: '姓名', icon: User },
|
||||||
|
{ prop: 'age', label: '年龄', icon: Calendar }
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 类型定义
|
||||||
|
|
||||||
|
为列配置添加类型约束:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import type { TableColumn } from '/@/hooks/tableColumn'
|
||||||
|
|
||||||
|
const tableColumns: TableColumn[] = [
|
||||||
|
{ prop: 'name', label: '姓名', icon: User }
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 统一命名
|
||||||
|
|
||||||
|
保持列 `prop` 与后端字段名一致,便于数据处理。
|
||||||
|
|
||||||
|
### 4. 性能优化
|
||||||
|
|
||||||
|
对于大量列的表格,考虑使用虚拟滚动或分页加载列配置。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 十一、迁移指南
|
||||||
|
|
||||||
|
### 从旧实现迁移到 Hook
|
||||||
|
|
||||||
|
**旧代码(~90 行):**
|
||||||
|
```typescript
|
||||||
|
const visibleColumns = ref<string[]>([])
|
||||||
|
const columnOrder = ref<string[]>([])
|
||||||
|
const loadSavedConfig = () => { /* ... */ }
|
||||||
|
const handleColumnChange = () => { /* ... */ }
|
||||||
|
// ... 更多代码
|
||||||
|
```
|
||||||
|
|
||||||
|
**新代码(~5 行):**
|
||||||
|
```typescript
|
||||||
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
|
||||||
|
const {
|
||||||
|
visibleColumns,
|
||||||
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
|
handleColumnChange,
|
||||||
|
handleColumnOrderChange
|
||||||
|
} = useTableColumnControl(tableColumns)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 迁移步骤
|
||||||
|
|
||||||
|
1. 引入 Hook
|
||||||
|
2. 删除旧的列控制逻辑(`loadSavedConfig`、`handleColumnChange` 等)
|
||||||
|
3. 使用 Hook 返回值替换原有变量
|
||||||
|
4. 测试功能是否正常
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 十二、更新日志
|
||||||
|
|
||||||
|
### v1.0.0 (2024-01-XX)
|
||||||
|
- ✅ 初始版本发布
|
||||||
|
- ✅ 支持列显示/隐藏控制
|
||||||
|
- ✅ 支持列排序管理
|
||||||
|
- ✅ 支持本地存储和服务器同步
|
||||||
|
- ✅ 支持自动加载配置
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 十三、相关资源
|
||||||
|
|
||||||
|
- **Hook 源码**:`src/hooks/tableColumn.ts`
|
||||||
|
- **API 接口**:`src/api/admin/usertable.ts`
|
||||||
|
- **组件源码**:`src/components/TableColumnControl/index.vue`
|
||||||
|
- **示例页面**:`src/views/stuwork/classmasterresume/index.vue`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 十四、贡献指南
|
||||||
|
|
||||||
|
如果发现 bug 或有改进建议,请:
|
||||||
|
1. 提交 Issue 描述问题
|
||||||
|
2. 提交 Pull Request 提供修复方案
|
||||||
|
3. 更新本文档说明变更
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**最后更新:2024-01-XX**
|
||||||
|
|
||||||
252
src/assets/styles/modern-page.scss
Normal file
252
src/assets/styles/modern-page.scss
Normal file
@@ -0,0 +1,252 @@
|
|||||||
|
/**
|
||||||
|
* 现代化页面布局公共样式
|
||||||
|
* 用于统一的卡片式布局和表格样式
|
||||||
|
*
|
||||||
|
* 使用方法:
|
||||||
|
* @import '/@/assets/styles/modern-page.scss';
|
||||||
|
*
|
||||||
|
* 然后在模板中使用以下类名:
|
||||||
|
* - .modern-page-container: 页面容器
|
||||||
|
* - .page-wrapper: 页面包装器
|
||||||
|
* - .search-card: 搜索表单卡片
|
||||||
|
* - .content-card: 内容卡片
|
||||||
|
* - .modern-table: 现代化表格
|
||||||
|
* - .search-form: 搜索表单
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 页面容器
|
||||||
|
.modern-page-container {
|
||||||
|
padding: 20px;
|
||||||
|
background: #f5f7fa;
|
||||||
|
min-height: calc(100vh - 84px);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 页面包装器
|
||||||
|
.page-wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索卡片
|
||||||
|
.search-card {
|
||||||
|
border-radius: 12px;
|
||||||
|
border: none;
|
||||||
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.04);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
box-shadow: 0 4px 20px 0 rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-card__header) {
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-bottom: 1px solid #f0f2f5;
|
||||||
|
background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-card__body) {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 内容卡片
|
||||||
|
.content-card {
|
||||||
|
border-radius: 12px;
|
||||||
|
border: none;
|
||||||
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.04);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
box-shadow: 0 4px 20px 0 rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-card__header) {
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-bottom: 1px solid #f0f2f5;
|
||||||
|
background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-card__body) {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 卡片头部
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
|
||||||
|
.title-icon {
|
||||||
|
margin-right: 8px;
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索表单 - 保持原有宽度
|
||||||
|
.search-form {
|
||||||
|
:deep(.el-form-item) {
|
||||||
|
margin-bottom: 18px;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-form-item__label) {
|
||||||
|
font-weight: 500;
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保表单项宽度保持200px
|
||||||
|
:deep(.el-select),
|
||||||
|
:deep(.el-input) {
|
||||||
|
width: 200px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 现代化表格
|
||||||
|
.modern-table {
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
:deep(.el-table__header) {
|
||||||
|
th {
|
||||||
|
background: linear-gradient(135deg, #fafbfc 0%, #f5f7fa 100%);
|
||||||
|
color: #606266;
|
||||||
|
font-weight: 600;
|
||||||
|
border-bottom: 2px solid #e4e7ed;
|
||||||
|
padding: 14px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-table__body) {
|
||||||
|
tr {
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #f5f7ff;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
border-bottom: 1px solid #f0f2f5;
|
||||||
|
padding: 16px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-table__row) {
|
||||||
|
&:nth-child(even) {
|
||||||
|
background-color: #fafbfc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页包装器
|
||||||
|
.pagination-wrapper {
|
||||||
|
margin-top: 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
padding-top: 16px;
|
||||||
|
border-top: 1px solid #f0f2f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 响应式设计
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.modern-page-container {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-wrapper {
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-card,
|
||||||
|
.content-card {
|
||||||
|
:deep(.el-card__header) {
|
||||||
|
padding: 12px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-card__body) {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-actions {
|
||||||
|
width: 100%;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移动端保持表单宽度
|
||||||
|
.search-form {
|
||||||
|
:deep(.el-form-item) {
|
||||||
|
margin-right: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-select),
|
||||||
|
:deep(.el-input) {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 动画效果
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(10px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-card,
|
||||||
|
.content-card {
|
||||||
|
animation: fadeIn 0.3s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按钮样式优化
|
||||||
|
:deep(.el-button) {
|
||||||
|
border-radius: 6px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表格空状态优化
|
||||||
|
:deep(.el-empty) {
|
||||||
|
padding: 40px 0;
|
||||||
|
}
|
||||||
|
|
||||||
220
src/hooks/tableColumn.ts
Normal file
220
src/hooks/tableColumn.ts
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
import { ref, computed, onMounted, nextTick } from 'vue'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表格列配置类型
|
||||||
|
*/
|
||||||
|
export interface TableColumn {
|
||||||
|
prop?: string
|
||||||
|
label?: string
|
||||||
|
icon?: any
|
||||||
|
width?: number | string
|
||||||
|
minWidth?: number | string
|
||||||
|
showOverflowTooltip?: boolean
|
||||||
|
align?: 'left' | 'center' | 'right'
|
||||||
|
alwaysShow?: boolean
|
||||||
|
fixed?: boolean | 'left' | 'right'
|
||||||
|
[key: string]: any
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* useTableColumnControl Hook
|
||||||
|
*
|
||||||
|
* 用于统一管理表格列的显示/隐藏和排序功能
|
||||||
|
*
|
||||||
|
* @param tableColumns 表格列配置数组
|
||||||
|
* @param options 可选配置项
|
||||||
|
* @returns 返回列控制相关的状态和方法
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* const tableColumns = [
|
||||||
|
* { prop: 'name', label: '姓名', icon: User },
|
||||||
|
* { prop: 'age', label: '年龄', icon: Calendar }
|
||||||
|
* ]
|
||||||
|
*
|
||||||
|
* const {
|
||||||
|
* visibleColumns,
|
||||||
|
* visibleColumnsSorted,
|
||||||
|
* checkColumnVisible,
|
||||||
|
* handleColumnChange,
|
||||||
|
* handleColumnOrderChange
|
||||||
|
* } = useTableColumnControl(tableColumns)
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export function useTableColumnControl(
|
||||||
|
tableColumns: TableColumn[],
|
||||||
|
options?: {
|
||||||
|
/** 是否在挂载时自动加载配置,默认为 true */
|
||||||
|
autoLoad?: boolean
|
||||||
|
/** 自定义存储 key,默认使用路由路径 */
|
||||||
|
storageKey?: string
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
const route = useRoute()
|
||||||
|
const { autoLoad = true, storageKey: customStorageKey } = options || {}
|
||||||
|
|
||||||
|
// 生成存储 key
|
||||||
|
const getStorageKey = () => {
|
||||||
|
if (customStorageKey) {
|
||||||
|
return customStorageKey
|
||||||
|
}
|
||||||
|
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
||||||
|
return `table-columns-${routePath}`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 当前显示的列
|
||||||
|
const visibleColumns = ref<string[]>([])
|
||||||
|
// 列排序顺序
|
||||||
|
const columnOrder = ref<string[]>([])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从本地统一存储加载配置
|
||||||
|
*/
|
||||||
|
const loadSavedConfig = () => {
|
||||||
|
const storageKey = getStorageKey()
|
||||||
|
const savedConfig = getTableConfigFromLocal(storageKey)
|
||||||
|
|
||||||
|
// 获取所有有效的列 key
|
||||||
|
const validColumns = tableColumns
|
||||||
|
.filter(col => !col.alwaysShow && !col.fixed)
|
||||||
|
.map(col => col.prop || col.label || '')
|
||||||
|
.filter(Boolean)
|
||||||
|
|
||||||
|
// 加载可见列配置
|
||||||
|
if (savedConfig && savedConfig.visibleColumns) {
|
||||||
|
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
||||||
|
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
||||||
|
} else {
|
||||||
|
visibleColumns.value = validColumns
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载列排序配置
|
||||||
|
if (savedConfig && savedConfig.columnOrder) {
|
||||||
|
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
||||||
|
// 补充缺失的列
|
||||||
|
validColumns.forEach(key => {
|
||||||
|
if (!columnOrder.value.includes(key)) {
|
||||||
|
columnOrder.value.push(key)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
columnOrder.value = validColumns
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列显示控制函数
|
||||||
|
* @param prop 列的 prop 或 label
|
||||||
|
* @returns 是否可见
|
||||||
|
*/
|
||||||
|
const checkColumnVisible = (prop: string): boolean => {
|
||||||
|
if (visibleColumns.value.length === 0) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return visibleColumns.value.includes(prop)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列变化处理
|
||||||
|
* @param value 新的可见列数组
|
||||||
|
*/
|
||||||
|
const handleColumnChange = (value: string[]) => {
|
||||||
|
visibleColumns.value = value
|
||||||
|
const storageKey = getStorageKey()
|
||||||
|
|
||||||
|
// 过滤掉 alwaysShow 和 fixed 的列
|
||||||
|
const selectableColumns = value.filter(col => {
|
||||||
|
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
||||||
|
return column && !column.alwaysShow && !column.fixed
|
||||||
|
})
|
||||||
|
|
||||||
|
// 保存到本地统一存储
|
||||||
|
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
||||||
|
// 异步保存到后端
|
||||||
|
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列排序变化处理
|
||||||
|
* @param order 新的列排序数组
|
||||||
|
*/
|
||||||
|
const handleColumnOrderChange = (order: string[]) => {
|
||||||
|
columnOrder.value = order
|
||||||
|
const storageKey = getStorageKey()
|
||||||
|
|
||||||
|
// 保存到本地统一存储
|
||||||
|
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
||||||
|
// 异步保存到后端
|
||||||
|
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序后的表格列
|
||||||
|
* 根据 visibleColumns 和 columnOrder 计算最终显示的列
|
||||||
|
*/
|
||||||
|
const visibleColumnsSorted = computed(() => {
|
||||||
|
// 过滤出可见的列
|
||||||
|
const columns = tableColumns.filter(col => {
|
||||||
|
const key = col.prop || col.label || ''
|
||||||
|
return visibleColumns.value.includes(key)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 如果有排序配置,按排序顺序排列
|
||||||
|
if (columnOrder.value.length > 0) {
|
||||||
|
const orderedColumns: TableColumn[] = []
|
||||||
|
const unorderedColumns: TableColumn[] = []
|
||||||
|
|
||||||
|
// 先按保存的顺序添加列
|
||||||
|
columnOrder.value.forEach(key => {
|
||||||
|
const col = columns.find(c => (c.prop || c.label) === key)
|
||||||
|
if (col) {
|
||||||
|
orderedColumns.push(col)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 添加未在排序列表中的列(新增的列)
|
||||||
|
columns.forEach(col => {
|
||||||
|
const key = col.prop || col.label || ''
|
||||||
|
if (!columnOrder.value.includes(key)) {
|
||||||
|
unorderedColumns.push(col)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return [...orderedColumns, ...unorderedColumns]
|
||||||
|
}
|
||||||
|
|
||||||
|
return columns
|
||||||
|
})
|
||||||
|
|
||||||
|
// 自动加载配置
|
||||||
|
if (autoLoad) {
|
||||||
|
loadSavedConfig()
|
||||||
|
onMounted(() => {
|
||||||
|
nextTick(() => {
|
||||||
|
if (visibleColumns.value.length === 0) {
|
||||||
|
loadSavedConfig()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
/** 当前可见的列 key 数组 */
|
||||||
|
visibleColumns,
|
||||||
|
/** 列排序顺序 */
|
||||||
|
columnOrder,
|
||||||
|
/** 排序后的可见列配置数组 */
|
||||||
|
visibleColumnsSorted,
|
||||||
|
/** 检查列是否可见 */
|
||||||
|
checkColumnVisible,
|
||||||
|
/** 列变化处理函数 */
|
||||||
|
handleColumnChange,
|
||||||
|
/** 列排序变化处理函数 */
|
||||||
|
handleColumnOrderChange,
|
||||||
|
/** 手动加载配置 */
|
||||||
|
loadSavedConfig
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch" class="search-form">
|
||||||
<el-form-item label="学院" prop="deptCode">
|
<el-form-item label="学院" prop="deptCode">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="searchForm.deptCode"
|
v-model="searchForm.deptCode"
|
||||||
@@ -73,81 +81,90 @@
|
|||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="FolderAdd"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
class="ml10"
|
班级列表
|
||||||
@click="formDialogRef.openDialog()">
|
</span>
|
||||||
新 增
|
<div class="header-actions">
|
||||||
</el-button>
|
<el-button
|
||||||
<el-button
|
icon="FolderAdd"
|
||||||
icon="Link"
|
type="primary"
|
||||||
type="success"
|
@click="formDialogRef.openDialog()">
|
||||||
class="ml10"
|
新增
|
||||||
@click="handleLinkRule">
|
</el-button>
|
||||||
关联门禁规则
|
<el-button
|
||||||
</el-button>
|
icon="Link"
|
||||||
<el-button
|
type="success"
|
||||||
icon="Download"
|
class="ml10"
|
||||||
type="warning"
|
@click="handleLinkRule">
|
||||||
class="ml10"
|
关联门禁规则
|
||||||
@click="handleExport">
|
</el-button>
|
||||||
导 出
|
<el-button
|
||||||
</el-button>
|
icon="Download"
|
||||||
<el-button
|
type="warning"
|
||||||
icon="DocumentAdd"
|
class="ml10"
|
||||||
type="info"
|
@click="handleExport">
|
||||||
class="ml10"
|
导出
|
||||||
@click="handleGenerateAssessment">
|
</el-button>
|
||||||
生成考核班级
|
<el-button
|
||||||
</el-button>
|
icon="DocumentAdd"
|
||||||
<right-toolbar
|
type="info"
|
||||||
v-model:showSearch="showSearch"
|
class="ml10"
|
||||||
class="ml10 mr20"
|
@click="handleGenerateAssessment">
|
||||||
style="float: right;"
|
生成考核班级
|
||||||
@queryTable="getDataList">
|
</el-button>
|
||||||
<TableColumnControl
|
<right-toolbar
|
||||||
ref="columnControlRef"
|
v-model:showSearch="showSearch"
|
||||||
:columns="tableColumns"
|
class="ml10"
|
||||||
v-model="visibleColumns"
|
@queryTable="getDataList">
|
||||||
trigger-type="default"
|
<TableColumnControl
|
||||||
trigger-circle
|
ref="columnControlRef"
|
||||||
@change="handleColumnChange"
|
:columns="tableColumns"
|
||||||
@order-change="handleColumnOrderChange"
|
v-model="visibleColumns"
|
||||||
>
|
trigger-type="default"
|
||||||
<template #trigger>
|
trigger-circle
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
@change="handleColumnChange"
|
||||||
<el-button circle style="margin-left: 0;">
|
@order-change="handleColumnOrderChange"
|
||||||
<el-icon><Menu /></el-icon>
|
>
|
||||||
</el-button>
|
<template #trigger>
|
||||||
</el-tooltip>
|
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
||||||
</template>
|
<el-button circle style="margin-left: 0;">
|
||||||
</TableColumnControl>
|
<el-icon><Menu /></el-icon>
|
||||||
</right-toolbar>
|
</el-button>
|
||||||
</div>
|
</el-tooltip>
|
||||||
</el-row>
|
</template>
|
||||||
|
</TableColumnControl>
|
||||||
|
</right-toolbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table
|
<el-table
|
||||||
:data="state.dataList"
|
:data="state.dataList"
|
||||||
v-loading="state.loading"
|
v-loading="state.loading"
|
||||||
border
|
stripe
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle"
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
row-key="id"
|
class="modern-table"
|
||||||
@selection-change="handleSelectionChange"
|
row-key="id"
|
||||||
@sort-change="sortChangeHandle">
|
@selection-change="handleSelectionChange"
|
||||||
<el-table-column type="selection" width="55" align="center" />
|
@sort-change="sortChangeHandle">
|
||||||
<el-table-column type="index" label="序号" align="center">
|
<el-table-column type="selection" width="55" align="center" />
|
||||||
<template #header>
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
<el-icon><List /></el-icon>
|
<template #header>
|
||||||
</template>
|
<el-icon><List /></el-icon>
|
||||||
</el-table-column>
|
</template>
|
||||||
|
<template #default="{ $index }">
|
||||||
|
{{ $index + 1 + ((state.pagination?.current || 1) - 1) * (state.pagination?.size || 10) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
v-for="col in visibleColumnsSorted"
|
v-for="col in visibleColumnsSorted"
|
||||||
:key="col.prop"
|
:key="col.prop"
|
||||||
@@ -183,34 +200,37 @@
|
|||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button
|
<el-button
|
||||||
icon="View"
|
icon="View"
|
||||||
text
|
link
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="handleViewDetail(scope.row)">
|
@click="handleViewDetail(scope.row)">
|
||||||
班级概况
|
班级概况
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
icon="Edit"
|
icon="Edit"
|
||||||
text
|
link
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="formDialogRef.openDialog(scope.row.id)">
|
@click="formDialogRef.openDialog(scope.row.id)">
|
||||||
编辑
|
编辑
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
icon="Delete"
|
icon="Delete"
|
||||||
text
|
link
|
||||||
type="danger"
|
type="danger"
|
||||||
@click="handleDelete([scope.row.id])">
|
@click="handleDelete([scope.row.id])">
|
||||||
删除
|
删除
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<pagination
|
<div class="pagination-wrapper">
|
||||||
@size-change="sizeChangeHandle"
|
<pagination
|
||||||
@current-change="currentChangeHandle"
|
@size-change="sizeChangeHandle"
|
||||||
v-bind="state.pagination" />
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 编辑、新增 -->
|
<!-- 编辑、新增 -->
|
||||||
@@ -262,8 +282,9 @@ import { useMessage, useMessageBox } from "/@/hooks/message";
|
|||||||
import { fetchList as getRuleList } from "/@/api/stuwork/entrancerule";
|
import { fetchList as getRuleList } from "/@/api/stuwork/entrancerule";
|
||||||
import { downBlobFile, adaptationUrl } from "/@/utils/other";
|
import { downBlobFile, adaptationUrl } from "/@/utils/other";
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import { List, OfficeBuilding, Grid, Document, UserFilled, Phone, User, Lock, CircleCheck, TrendCharts, Setting, Menu } from '@element-plus/icons-vue'
|
import { List, OfficeBuilding, Grid, Document, UserFilled, Phone, User, Lock, CircleCheck, TrendCharts, Setting, Menu, Search } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
import '/@/styles/modern-page.scss'
|
||||||
import { defineAsyncComponent as defineStatusTag } from 'vue'
|
import { defineAsyncComponent as defineStatusTag } from 'vue'
|
||||||
const StatusTag = defineStatusTag(() => import('/@/components/StatusTag/index.vue'))
|
const StatusTag = defineStatusTag(() => import('/@/components/StatusTag/index.vue'))
|
||||||
|
|
||||||
@@ -299,94 +320,14 @@ const tableColumns = [
|
|||||||
{ prop: 'stuLoseRate', label: '流失率', icon: TrendCharts }
|
{ prop: 'stuLoseRate', label: '流失率', icon: TrendCharts }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列变化处理
|
|
||||||
const handleColumnChange = (value: string[]) => {
|
|
||||||
visibleColumns.value = value
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = value.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列排序变化处理
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const visibleColumnsSorted = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: any[] = []
|
|
||||||
const unorderedColumns: any[] = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 搜索表单
|
// 搜索表单
|
||||||
const searchForm = reactive({
|
const searchForm = reactive({
|
||||||
@@ -583,7 +524,6 @@ const getRuleListData = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
loadSavedConfig()
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getDeptListData()
|
getDeptListData()
|
||||||
getClassListData()
|
getClassListData()
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch" class="search-form">
|
||||||
<el-form-item label="学院" prop="deptCode">
|
<el-form-item label="学院" prop="deptCode">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="searchForm.deptCode"
|
v-model="searchForm.deptCode"
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch" class="search-form">
|
||||||
<el-form-item label="班级" prop="classCode">
|
<el-form-item label="班级" prop="classCode">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="searchForm.classCode"
|
v-model="searchForm.classCode"
|
||||||
@@ -24,51 +32,60 @@
|
|||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<right-toolbar
|
<div class="card-header">
|
||||||
v-model:showSearch="showSearch"
|
<span class="card-title">
|
||||||
class="ml10 mr20"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
style="float: right;"
|
学生头像列表
|
||||||
@queryTable="getDataList">
|
</span>
|
||||||
<TableColumnControl
|
<div class="header-actions">
|
||||||
ref="columnControlRef"
|
<right-toolbar
|
||||||
:columns="tableColumns"
|
v-model:showSearch="showSearch"
|
||||||
v-model="visibleColumns"
|
class="ml10"
|
||||||
trigger-type="default"
|
@queryTable="getDataList">
|
||||||
trigger-circle
|
<TableColumnControl
|
||||||
@change="handleColumnChange"
|
ref="columnControlRef"
|
||||||
@order-change="handleColumnOrderChange"
|
:columns="tableColumns"
|
||||||
>
|
v-model="visibleColumns"
|
||||||
<template #trigger>
|
trigger-type="default"
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
trigger-circle
|
||||||
<el-button circle style="margin-left: 0;">
|
@change="handleColumnChange"
|
||||||
<el-icon><Menu /></el-icon>
|
@order-change="handleColumnOrderChange"
|
||||||
</el-button>
|
>
|
||||||
</el-tooltip>
|
<template #trigger>
|
||||||
</template>
|
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
||||||
</TableColumnControl>
|
<el-button circle style="margin-left: 0;">
|
||||||
</right-toolbar>
|
<el-icon><Menu /></el-icon>
|
||||||
</div>
|
</el-button>
|
||||||
</el-row>
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
</TableColumnControl>
|
||||||
|
</right-toolbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table
|
<el-table
|
||||||
:data="state.dataList"
|
:data="state.dataList"
|
||||||
v-loading="state.loading"
|
v-loading="state.loading"
|
||||||
border
|
stripe
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle"
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
@sort-change="sortChangeHandle">
|
class="modern-table"
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
@sort-change="sortChangeHandle">
|
||||||
<template #header>
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
<el-icon><List /></el-icon>
|
<template #header>
|
||||||
<span style="margin-left: 4px;">序号</span>
|
<el-icon><List /></el-icon>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
<template #default="{ $index }">
|
||||||
|
{{ $index + 1 + ((state.pagination?.current || 1) - 1) * (state.pagination?.size || 10) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
v-for="col in visibleColumnsSorted"
|
v-for="col in visibleColumnsSorted"
|
||||||
:key="col.prop"
|
:key="col.prop"
|
||||||
@@ -99,13 +116,16 @@
|
|||||||
<span v-else>-</span>
|
<span v-else>-</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<pagination
|
<div class="pagination-wrapper">
|
||||||
@size-change="sizeChangeHandle"
|
<pagination
|
||||||
@current-change="currentChangeHandle"
|
@size-change="sizeChangeHandle"
|
||||||
v-bind="state.pagination" />
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -117,8 +137,9 @@ import { BasicTableProps, useTable } from "/@/hooks/table";
|
|||||||
import { fetchList } from "/@/api/basic/basicstudentavatar";
|
import { fetchList } from "/@/api/basic/basicstudentavatar";
|
||||||
import { getClassListByRole } from "/@/api/basic/basicclass";
|
import { getClassListByRole } from "/@/api/basic/basicclass";
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import { Picture, List, Document, UserFilled, Grid, Menu } from '@element-plus/icons-vue'
|
import { Picture, List, Document, UserFilled, Grid, Menu, Search } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
import '/@/styles/modern-page.scss'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -135,94 +156,14 @@ const tableColumns = [
|
|||||||
{ prop: 'headImg', label: '头像', icon: Picture, width: 120 }
|
{ prop: 'headImg', label: '头像', icon: Picture, width: 120 }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列变化处理
|
|
||||||
const handleColumnChange = (value: string[]) => {
|
|
||||||
visibleColumns.value = value
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = value.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列排序变化处理
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const visibleColumnsSorted = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: any[] = []
|
|
||||||
const unorderedColumns: any[] = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 搜索表单
|
// 搜索表单
|
||||||
const searchForm = reactive({
|
const searchForm = reactive({
|
||||||
@@ -309,7 +250,6 @@ const getClassListData = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
loadSavedConfig()
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getClassListData()
|
getClassListData()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,112 +1,133 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="Plus"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
@click="formDialogRef.openDialog()">
|
获奖活动信息列表
|
||||||
新增
|
</span>
|
||||||
</el-button>
|
<div class="header-actions">
|
||||||
<el-button
|
<el-button
|
||||||
icon="Upload"
|
icon="Plus"
|
||||||
type="primary"
|
type="primary"
|
||||||
class="ml10"
|
@click="formDialogRef.openDialog()">
|
||||||
@click="handleImport">
|
新增
|
||||||
导入
|
</el-button>
|
||||||
</el-button>
|
<el-button
|
||||||
<right-toolbar
|
icon="Upload"
|
||||||
v-model:showSearch="showSearch"
|
type="primary"
|
||||||
class="ml10 mr20"
|
class="ml10"
|
||||||
style="float: right;"
|
@click="handleImport">
|
||||||
@queryTable="getDataList">
|
导入
|
||||||
<TableColumnControl
|
</el-button>
|
||||||
ref="columnControlRef"
|
<right-toolbar
|
||||||
:columns="tableColumns"
|
v-model:showSearch="showSearch"
|
||||||
v-model="visibleColumns"
|
class="ml10"
|
||||||
trigger-type="default"
|
@queryTable="getDataList">
|
||||||
trigger-circle
|
<TableColumnControl
|
||||||
@change="handleColumnChange"
|
ref="columnControlRef"
|
||||||
@order-change="handleColumnOrderChange"
|
:columns="tableColumns"
|
||||||
>
|
v-model="visibleColumns"
|
||||||
<template #trigger>
|
trigger-type="default"
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
trigger-circle
|
||||||
<el-button circle style="margin-left: 0;">
|
@change="handleColumnChange"
|
||||||
<el-icon><Menu /></el-icon>
|
@order-change="handleColumnOrderChange"
|
||||||
</el-button>
|
>
|
||||||
</el-tooltip>
|
<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="序号" 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 !== '操作'"
|
||||||
|
: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>
|
||||||
</TableColumnControl>
|
<!-- 获奖信息列特殊模板 -->
|
||||||
</right-toolbar>
|
<template v-if="col.prop === 'awards'" #default="scope">
|
||||||
|
<el-tag v-if="scope.row.awards" size="small" type="warning" effect="plain" round>
|
||||||
|
{{ scope.row.awards }}
|
||||||
|
</el-tag>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
<!-- 获奖时间列特殊模板 -->
|
||||||
|
<template v-else-if="col.prop === 'awardTime'" #default="scope">
|
||||||
|
<span>{{ parseTime(scope.row.awardTime || scope.row.month, '{y}-{m}-{d}') }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column label="操作" width="180" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
icon="EditPen"
|
||||||
|
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>
|
||||||
|
<template #empty>
|
||||||
|
<el-empty description="暂无数据" :image-size="120">
|
||||||
|
<el-button type="primary" icon="Plus" @click="formDialogRef.openDialog()">新增获奖信息</el-button>
|
||||||
|
</el-empty>
|
||||||
|
</template>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<div class="pagination-wrapper">
|
||||||
|
<pagination
|
||||||
|
@size-change="sizeChangeHandle"
|
||||||
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
</div>
|
</div>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 表格 -->
|
|
||||||
<el-table
|
|
||||||
:data="state.dataList"
|
|
||||||
v-loading="state.loading"
|
|
||||||
border
|
|
||||||
:cell-style="tableStyle.cellStyle"
|
|
||||||
:header-cell-style="tableStyle.headerCellStyle">
|
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
v-for="col in visibleColumnsSorted"
|
|
||||||
:key="col.prop"
|
|
||||||
: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 === 'awards'">
|
|
||||||
<el-tag v-if="scope.row.awards" size="small" type="warning" effect="light">
|
|
||||||
{{ scope.row.awards }}
|
|
||||||
</el-tag>
|
|
||||||
<span v-else>-</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope" v-else-if="col.prop === 'awardTime'">
|
|
||||||
<span>{{ parseTime(scope.row.awardTime || scope.row.month, '{y}-{m}-{d}') }}</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><Setting /></el-icon>
|
|
||||||
<span style="margin-left: 4px">操作</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
icon="Edit"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleEdit(scope.row)">
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Delete"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row)">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
|
||||||
<pagination
|
|
||||||
@size-change="sizeChangeHandle"
|
|
||||||
@current-change="currentChangeHandle"
|
|
||||||
v-bind="state.pagination" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 新增/编辑表单弹窗 -->
|
<!-- 新增/编辑表单弹窗 -->
|
||||||
@@ -155,19 +176,17 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="ActivityAwards">
|
<script setup lang="ts" name="ActivityAwards">
|
||||||
import { reactive, ref, computed } from 'vue'
|
import { reactive, ref } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||||
import { fetchList, delObj, importExcel } from "/@/api/stuwork/activityawards";
|
import { fetchList, delObj, importExcel } from "/@/api/stuwork/activityawards";
|
||||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||||
import { parseTime } from "/@/utils/formatTime";
|
import { parseTime } from "/@/utils/formatTime";
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import { UploadFilled, List, Trophy, CreditCard, Avatar, Medal, Calendar, EditPen, Setting, Menu } from '@element-plus/icons-vue'
|
import { UploadFilled, List, Trophy, CreditCard, Avatar, Medal, Calendar, EditPen, Setting, Menu, Document } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
import FormDialog from './form.vue'
|
import FormDialog from './form.vue'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
|
||||||
const formDialogRef = ref()
|
const formDialogRef = ref()
|
||||||
const uploadRef = ref()
|
const uploadRef = ref()
|
||||||
const columnControlRef = ref()
|
const columnControlRef = ref()
|
||||||
@@ -186,94 +205,14 @@ const tableColumns = [
|
|||||||
{ prop: 'remarks', label: '备注', icon: EditPen, minWidth: 200 }
|
{ prop: 'remarks', label: '备注', icon: EditPen, minWidth: 200 }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制 Hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列变化处理
|
|
||||||
const handleColumnChange = (value: string[]) => {
|
|
||||||
visibleColumns.value = value
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = value.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列排序变化处理
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const visibleColumnsSorted = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: any[] = []
|
|
||||||
const unorderedColumns: any[] = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 表格样式
|
// 表格样式
|
||||||
const tableStyle = {
|
const tableStyle = {
|
||||||
@@ -282,7 +221,6 @@ const tableStyle = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 配置 useTable
|
// 配置 useTable
|
||||||
loadSavedConfig()
|
|
||||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||||
queryForm: {},
|
queryForm: {},
|
||||||
pageList: fetchList,
|
pageList: fetchList,
|
||||||
@@ -290,7 +228,7 @@ const state: BasicTableProps = reactive<BasicTableProps>({
|
|||||||
item: 'records',
|
item: 'records',
|
||||||
totalCount: 'total'
|
totalCount: 'total'
|
||||||
},
|
},
|
||||||
createdIsNeed: true // 页面加载时自动获取数据
|
createdIsNeed: true
|
||||||
})
|
})
|
||||||
|
|
||||||
// table hook
|
// table hook
|
||||||
@@ -332,7 +270,6 @@ const handleImport = () => {
|
|||||||
const handleDownloadTemplate = async () => {
|
const handleDownloadTemplate = async () => {
|
||||||
try {
|
try {
|
||||||
const fileName = '获奖活动信息导入模板.xlsx'
|
const fileName = '获奖活动信息导入模板.xlsx'
|
||||||
// 使用动态导入获取文件URL,从 views/stuwork/activityawards 到 assets/file 的相对路径
|
|
||||||
const fileUrl = new URL(`../../../assets/file/${fileName}`, import.meta.url).href
|
const fileUrl = new URL(`../../../assets/file/${fileName}`, import.meta.url).href
|
||||||
const response = await fetch(fileUrl)
|
const response = await fetch(fileUrl)
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
@@ -382,5 +319,5 @@ const handleImportSubmit = async () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,122 +1,144 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="Plus"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
@click="formDialogRef.openDialog()">
|
活动信息列表
|
||||||
新增
|
</span>
|
||||||
</el-button>
|
<div class="header-actions">
|
||||||
<right-toolbar
|
<el-button
|
||||||
v-model:showSearch="showSearch"
|
icon="Plus"
|
||||||
class="ml10 mr20"
|
type="primary"
|
||||||
style="float: right;"
|
@click="formDialogRef.openDialog()">
|
||||||
@queryTable="getDataList">
|
新增
|
||||||
<TableColumnControl
|
</el-button>
|
||||||
ref="columnControlRef"
|
<right-toolbar
|
||||||
:columns="tableColumns"
|
v-model:showSearch="showSearch"
|
||||||
v-model="visibleColumns"
|
class="ml10"
|
||||||
trigger-type="default"
|
@queryTable="getDataList">
|
||||||
trigger-circle
|
<TableColumnControl
|
||||||
@change="handleColumnChange"
|
ref="columnControlRef"
|
||||||
@order-change="handleColumnOrderChange"
|
:columns="tableColumns"
|
||||||
>
|
v-model="visibleColumns"
|
||||||
<template #trigger>
|
trigger-type="default"
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
trigger-circle
|
||||||
<el-button circle style="margin-left: 0;">
|
@change="handleColumnChange"
|
||||||
<el-icon><Menu /></el-icon>
|
@order-change="handleColumnOrderChange"
|
||||||
</el-button>
|
>
|
||||||
</el-tooltip>
|
<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="序号" 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 !== '操作'"
|
||||||
|
: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>
|
||||||
</TableColumnControl>
|
<!-- 活动兼报数列特殊模板 -->
|
||||||
</right-toolbar>
|
<template v-if="col.prop === 'maxSub'" #default="scope">
|
||||||
|
<el-tag v-if="scope.row.maxSub" size="small" type="success" effect="plain" round>
|
||||||
|
{{ scope.row.maxSub }}
|
||||||
|
</el-tag>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
<!-- 开始时间列特殊模板 -->
|
||||||
|
<template v-else-if="col.prop === 'startTime'" #default="scope">
|
||||||
|
<span>{{ parseTime(scope.row.startTime, '{y}-{m}-{d}') }}</span>
|
||||||
|
</template>
|
||||||
|
<!-- 结束时间列特殊模板 -->
|
||||||
|
<template v-else-if="col.prop === 'endTime'" #default="scope">
|
||||||
|
<span>{{ parseTime(scope.row.endTime, '{y}-{m}-{d}') }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column label="操作" width="300" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
icon="View"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="handleView(scope.row)">
|
||||||
|
查看详情
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="Upload"
|
||||||
|
link
|
||||||
|
type="success"
|
||||||
|
@click="handleImportSub(scope.row)">
|
||||||
|
导入子项目
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="EditPen"
|
||||||
|
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>
|
||||||
|
<template #empty>
|
||||||
|
<el-empty description="暂无数据" :image-size="120">
|
||||||
|
<el-button type="primary" icon="Plus" @click="formDialogRef.openDialog()">新增活动</el-button>
|
||||||
|
</el-empty>
|
||||||
|
</template>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<div class="pagination-wrapper">
|
||||||
|
<pagination
|
||||||
|
@size-change="sizeChangeHandle"
|
||||||
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
</div>
|
</div>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 表格 -->
|
|
||||||
<el-table
|
|
||||||
:data="state.dataList"
|
|
||||||
v-loading="state.loading"
|
|
||||||
border
|
|
||||||
:cell-style="tableStyle.cellStyle"
|
|
||||||
:header-cell-style="tableStyle.headerCellStyle">
|
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
v-for="col in visibleColumnsSorted"
|
|
||||||
:key="col.prop"
|
|
||||||
: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 === 'maxSub'">
|
|
||||||
<el-tag v-if="scope.row.maxSub" size="small" type="success" effect="plain">
|
|
||||||
{{ scope.row.maxSub }}
|
|
||||||
</el-tag>
|
|
||||||
<span v-else>-</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope" v-else-if="col.prop === 'startTime'">
|
|
||||||
<span>{{ parseTime(scope.row.startTime, '{y}-{m}-{d}') }}</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope" v-else-if="col.prop === 'endTime'">
|
|
||||||
<span>{{ parseTime(scope.row.endTime, '{y}-{m}-{d}') }}</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="操作" width="300" align="center" fixed="right">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><Setting /></el-icon>
|
|
||||||
<span style="margin-left: 4px">操作</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
icon="View"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleView(scope.row)">
|
|
||||||
查看详情
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Upload"
|
|
||||||
text
|
|
||||||
type="success"
|
|
||||||
@click="handleImportSub(scope.row)">
|
|
||||||
导入子项目
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Edit"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleEdit(scope.row)">
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Delete"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row)">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
|
||||||
<pagination
|
|
||||||
@size-change="sizeChangeHandle"
|
|
||||||
@current-change="currentChangeHandle"
|
|
||||||
v-bind="state.pagination" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 新增/编辑表单弹窗 -->
|
<!-- 新增/编辑表单弹窗 -->
|
||||||
@@ -163,20 +185,18 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="ActivityInfo">
|
<script setup lang="ts" name="ActivityInfo">
|
||||||
import { reactive, ref, computed } from 'vue'
|
import { reactive, ref } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||||
import { fetchList, delObj, importSub } from "/@/api/stuwork/activityinfo";
|
import { fetchList, delObj, importSub } from "/@/api/stuwork/activityinfo";
|
||||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||||
import { parseTime } from "/@/utils/formatTime";
|
import { parseTime } from "/@/utils/formatTime";
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import { UploadFilled, List, Trophy, Document, UserFilled, Calendar, Setting, Menu } from '@element-plus/icons-vue'
|
import { UploadFilled, List, Trophy, Document, UserFilled, Calendar, Setting, Menu } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
import FormDialog from './form.vue'
|
import FormDialog from './form.vue'
|
||||||
import DetailDialog from './detail.vue'
|
import DetailDialog from './detail.vue'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
|
||||||
const formDialogRef = ref()
|
const formDialogRef = ref()
|
||||||
const detailDialogRef = ref()
|
const detailDialogRef = ref()
|
||||||
const uploadRef = ref()
|
const uploadRef = ref()
|
||||||
@@ -196,94 +216,14 @@ const tableColumns = [
|
|||||||
{ prop: 'endTime', label: '结束时间', icon: Calendar, width: 120 }
|
{ prop: 'endTime', label: '结束时间', icon: Calendar, width: 120 }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制 Hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列变化处理
|
|
||||||
const handleColumnChange = (value: string[]) => {
|
|
||||||
visibleColumns.value = value
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = value.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列排序变化处理
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const visibleColumnsSorted = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: any[] = []
|
|
||||||
const unorderedColumns: any[] = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 表格样式
|
// 表格样式
|
||||||
const tableStyle = {
|
const tableStyle = {
|
||||||
@@ -299,7 +239,7 @@ const state: BasicTableProps = reactive<BasicTableProps>({
|
|||||||
item: 'records',
|
item: 'records',
|
||||||
totalCount: 'total'
|
totalCount: 'total'
|
||||||
},
|
},
|
||||||
createdIsNeed: true // 页面加载时自动获取数据
|
createdIsNeed: true
|
||||||
})
|
})
|
||||||
|
|
||||||
// table hook
|
// table hook
|
||||||
@@ -376,5 +316,5 @@ const handleImportSubmit = async () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="state.queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form
|
||||||
|
:model="state.queryForm"
|
||||||
|
ref="searchFormRef"
|
||||||
|
:inline="true"
|
||||||
|
@keyup.enter="getDataList"
|
||||||
|
class="search-form">
|
||||||
<el-form-item label="活动主题" prop="activityInfoId">
|
<el-form-item label="活动主题" prop="activityInfoId">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="state.queryForm.activityInfoId"
|
v-model="state.queryForm.activityInfoId"
|
||||||
@@ -20,124 +33,143 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" plain icon="Search" @click="getDataList">查询</el-button>
|
<el-button type="primary" icon="Search" @click="getDataList">查询</el-button>
|
||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<right-toolbar
|
<div class="card-header">
|
||||||
v-model:showSearch="showSearch"
|
<span class="card-title">
|
||||||
class="ml10 mr20"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
style="float: right;"
|
活动子项目列表
|
||||||
@queryTable="getDataList">
|
</span>
|
||||||
<TableColumnControl
|
<div class="header-actions">
|
||||||
ref="columnControlRef"
|
<right-toolbar
|
||||||
:columns="tableColumns"
|
v-model:showSearch="showSearch"
|
||||||
v-model="visibleColumns"
|
class="ml10"
|
||||||
trigger-type="default"
|
@queryTable="getDataList">
|
||||||
trigger-circle
|
<TableColumnControl
|
||||||
@change="handleColumnChange"
|
ref="columnControlRef"
|
||||||
@order-change="handleColumnOrderChange"
|
:columns="tableColumns"
|
||||||
>
|
v-model="visibleColumns"
|
||||||
<template #trigger>
|
trigger-type="default"
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
trigger-circle
|
||||||
<el-button circle style="margin-left: 0;">
|
@change="handleColumnChange"
|
||||||
<el-icon><Menu /></el-icon>
|
@order-change="handleColumnOrderChange"
|
||||||
</el-button>
|
>
|
||||||
</el-tooltip>
|
<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="序号" 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 !== '操作'"
|
||||||
|
: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>
|
||||||
</TableColumnControl>
|
<!-- 开始时间列特殊模板 -->
|
||||||
</right-toolbar>
|
<template v-if="col.prop === 'startTime'" #default="scope">
|
||||||
|
<span>{{ parseTime(scope.row.startTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
||||||
|
</template>
|
||||||
|
<!-- 结束时间列特殊模板 -->
|
||||||
|
<template v-else-if="col.prop === 'endTime'" #default="scope">
|
||||||
|
<span>{{ parseTime(scope.row.endTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
||||||
|
</template>
|
||||||
|
<!-- 报名人数限制列特殊模板 -->
|
||||||
|
<template v-else-if="col.prop === 'maxNum'" #default="scope">
|
||||||
|
<el-tag v-if="scope.row.maxNum !== undefined && scope.row.maxNum !== null" size="small" type="success" effect="plain" round>
|
||||||
|
{{ scope.row.maxNum }}
|
||||||
|
</el-tag>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column prop="remarks" label="活动说明" show-overflow-tooltip align="center" min-width="200">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><EditPen /></el-icon>
|
||||||
|
<span style="margin-left: 4px">活动说明</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="100" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
icon="Delete"
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
@click="handleDelete(scope.row)">
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<template #empty>
|
||||||
|
<el-empty description="暂无数据" :image-size="120">
|
||||||
|
</el-empty>
|
||||||
|
</template>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<div class="pagination-wrapper">
|
||||||
|
<pagination
|
||||||
|
@size-change="sizeChangeHandle"
|
||||||
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
</div>
|
</div>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 表格 -->
|
|
||||||
<el-table
|
|
||||||
:data="state.dataList"
|
|
||||||
v-loading="state.loading"
|
|
||||||
border
|
|
||||||
:cell-style="tableStyle.cellStyle"
|
|
||||||
:header-cell-style="tableStyle.headerCellStyle">
|
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
v-for="col in visibleColumnsSorted"
|
|
||||||
:key="col.prop"
|
|
||||||
: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 === 'startTime'">
|
|
||||||
<span>{{ parseTime(scope.row.startTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope" v-else-if="col.prop === 'endTime'">
|
|
||||||
<span>{{ parseTime(scope.row.endTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope" v-else-if="col.prop === 'maxNum'">
|
|
||||||
<el-tag v-if="scope.row.maxNum !== undefined && scope.row.maxNum !== null" size="small" type="success" effect="plain">
|
|
||||||
{{ scope.row.maxNum }}
|
|
||||||
</el-tag>
|
|
||||||
<span v-else>-</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column prop="remarks" label="活动说明" show-overflow-tooltip align="center" min-width="200">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><EditPen /></el-icon>
|
|
||||||
<span style="margin-left: 4px">活动说明</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="操作" width="100" align="center" fixed="right">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><Setting /></el-icon>
|
|
||||||
<span style="margin-left: 4px">操作</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
icon="Delete"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row)">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
|
||||||
<pagination
|
|
||||||
@size-change="sizeChangeHandle"
|
|
||||||
@current-change="currentChangeHandle"
|
|
||||||
v-bind="state.pagination" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="ActivityInfoSub">
|
<script setup lang="ts" name="ActivityInfoSub">
|
||||||
import { reactive, ref, onMounted, computed } from 'vue'
|
import { reactive, ref, onMounted } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||||
import { fetchList, delObj, getActivityInfoList } from "/@/api/stuwork/activityinfosub";
|
import { fetchList, delObj, getActivityInfoList } from "/@/api/stuwork/activityinfosub";
|
||||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||||
import { parseTime } from "/@/utils/formatTime";
|
import { parseTime } from "/@/utils/formatTime";
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import { List, Trophy, Document, Files, Calendar, UserFilled, EditPen, Setting, Menu } from '@element-plus/icons-vue'
|
import { List, Trophy, Document, Files, Calendar, UserFilled, EditPen, Setting, Menu, Search } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
|
||||||
const searchFormRef = ref()
|
const searchFormRef = ref()
|
||||||
const columnControlRef = ref()
|
const columnControlRef = ref()
|
||||||
const showSearch = ref(true)
|
const showSearch = ref(true)
|
||||||
@@ -153,94 +185,14 @@ const tableColumns = [
|
|||||||
{ prop: 'maxNum', label: '报名人数限制', icon: UserFilled, width: 120 }
|
{ prop: 'maxNum', label: '报名人数限制', icon: UserFilled, width: 120 }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制 Hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列变化处理
|
|
||||||
const handleColumnChange = (value: string[]) => {
|
|
||||||
visibleColumns.value = value
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = value.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列排序变化处理
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const visibleColumnsSorted = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: any[] = []
|
|
||||||
const unorderedColumns: any[] = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 表格样式
|
// 表格样式
|
||||||
const tableStyle = {
|
const tableStyle = {
|
||||||
@@ -258,7 +210,7 @@ const state: BasicTableProps = reactive<BasicTableProps>({
|
|||||||
item: 'records',
|
item: 'records',
|
||||||
totalCount: 'total'
|
totalCount: 'total'
|
||||||
},
|
},
|
||||||
createdIsNeed: true // 页面加载时自动获取数据
|
createdIsNeed: true
|
||||||
})
|
})
|
||||||
|
|
||||||
// table hook
|
// table hook
|
||||||
@@ -306,12 +258,11 @@ const getActivityInfoListData = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
loadSavedConfig()
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getActivityInfoListData()
|
getActivityInfoListData()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="state.queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form
|
||||||
|
:model="state.queryForm"
|
||||||
|
ref="searchFormRef"
|
||||||
|
:inline="true"
|
||||||
|
@keyup.enter="getDataList"
|
||||||
|
class="search-form">
|
||||||
<el-form-item label="活动主题" prop="activityInfoId">
|
<el-form-item label="活动主题" prop="activityInfoId">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="state.queryForm.activityInfoId"
|
v-model="state.queryForm.activityInfoId"
|
||||||
@@ -43,113 +56,128 @@
|
|||||||
style="width: 200px" />
|
style="width: 200px" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" plain icon="Search" @click="getDataList">查询</el-button>
|
<el-button type="primary" icon="Search" @click="getDataList">查询</el-button>
|
||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="Download"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
class="ml10"
|
活动报名列表
|
||||||
@click="handleExport"
|
</span>
|
||||||
:loading="exportLoading">
|
<div class="header-actions">
|
||||||
导出
|
<el-button
|
||||||
</el-button>
|
icon="Download"
|
||||||
<right-toolbar
|
type="primary"
|
||||||
v-model:showSearch="showSearch"
|
@click="handleExport"
|
||||||
class="ml10 mr20"
|
:loading="exportLoading">
|
||||||
style="float: right;"
|
导出
|
||||||
@queryTable="getDataList">
|
</el-button>
|
||||||
<TableColumnControl
|
<right-toolbar
|
||||||
ref="columnControlRef"
|
v-model:showSearch="showSearch"
|
||||||
:columns="tableColumns"
|
class="ml10"
|
||||||
v-model="visibleColumns"
|
@queryTable="getDataList">
|
||||||
trigger-type="default"
|
<TableColumnControl
|
||||||
trigger-circle
|
ref="columnControlRef"
|
||||||
@change="handleColumnChange"
|
:columns="tableColumns"
|
||||||
@order-change="handleColumnOrderChange"
|
v-model="visibleColumns"
|
||||||
>
|
trigger-type="default"
|
||||||
<template #trigger>
|
trigger-circle
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
@change="handleColumnChange"
|
||||||
<el-button circle style="margin-left: 0;">
|
@order-change="handleColumnOrderChange"
|
||||||
<el-icon><Menu /></el-icon>
|
>
|
||||||
</el-button>
|
<template #trigger>
|
||||||
</el-tooltip>
|
<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="序号" 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 !== '操作'"
|
||||||
|
: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>
|
||||||
</TableColumnControl>
|
</el-table-column>
|
||||||
</right-toolbar>
|
</template>
|
||||||
|
<el-table-column label="操作" width="100" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
icon="Delete"
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
@click="handleDelete(scope.row)">
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<template #empty>
|
||||||
|
<el-empty description="暂无数据" :image-size="120">
|
||||||
|
</el-empty>
|
||||||
|
</template>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<div class="pagination-wrapper">
|
||||||
|
<pagination
|
||||||
|
@size-change="sizeChangeHandle"
|
||||||
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
</div>
|
</div>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 表格 -->
|
|
||||||
<el-table
|
|
||||||
:data="state.dataList"
|
|
||||||
v-loading="state.loading"
|
|
||||||
border
|
|
||||||
:cell-style="tableStyle.cellStyle"
|
|
||||||
:header-cell-style="tableStyle.headerCellStyle">
|
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
v-for="col in visibleColumnsSorted"
|
|
||||||
:key="col.prop"
|
|
||||||
: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>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="操作" width="100" align="center" fixed="right">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><Setting /></el-icon>
|
|
||||||
<span style="margin-left: 4px">操作</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
icon="Delete"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row)">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
|
||||||
<pagination
|
|
||||||
@size-change="sizeChangeHandle"
|
|
||||||
@current-change="currentChangeHandle"
|
|
||||||
v-bind="state.pagination" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="ActivityInfoSubSignup">
|
<script setup lang="ts" name="ActivityInfoSubSignup">
|
||||||
import { reactive, ref, onMounted, computed } from 'vue'
|
import { reactive, ref, onMounted, computed } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||||
import { fetchList, delObj, exportExcel, getActivityInfoList, getActivityInfoSubList } from "/@/api/stuwork/activityinfosubsignup";
|
import { fetchList, delObj, exportExcel, getActivityInfoList, getActivityInfoSubList } from "/@/api/stuwork/activityinfosubsignup";
|
||||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import { List, Trophy, Document, Files, CreditCard, Avatar, OfficeBuilding, Grid, UserFilled, Phone, Setting, Menu } from '@element-plus/icons-vue'
|
import { List, Trophy, Document, Files, CreditCard, Avatar, OfficeBuilding, Grid, UserFilled, Phone, Setting, Menu, Search, Document as DocIcon } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
|
||||||
const searchFormRef = ref()
|
const searchFormRef = ref()
|
||||||
const columnControlRef = ref()
|
const columnControlRef = ref()
|
||||||
const showSearch = ref(true)
|
const showSearch = ref(true)
|
||||||
@@ -170,94 +198,14 @@ const tableColumns = [
|
|||||||
{ prop: 'phone', label: '联系电话', icon: Phone, width: 120 }
|
{ prop: 'phone', label: '联系电话', icon: Phone, width: 120 }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制 Hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列变化处理
|
|
||||||
const handleColumnChange = (value: string[]) => {
|
|
||||||
visibleColumns.value = value
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = value.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列排序变化处理
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const visibleColumnsSorted = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: any[] = []
|
|
||||||
const unorderedColumns: any[] = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 表格样式
|
// 表格样式
|
||||||
const tableStyle = {
|
const tableStyle = {
|
||||||
@@ -277,7 +225,7 @@ const state: BasicTableProps = reactive<BasicTableProps>({
|
|||||||
item: 'records',
|
item: 'records',
|
||||||
totalCount: 'total'
|
totalCount: 'total'
|
||||||
},
|
},
|
||||||
createdIsNeed: true // 页面加载时自动获取数据
|
createdIsNeed: true
|
||||||
})
|
})
|
||||||
|
|
||||||
// table hook
|
// table hook
|
||||||
@@ -335,11 +283,9 @@ const handleExport = async () => {
|
|||||||
exportLoading.value = true
|
exportLoading.value = true
|
||||||
try {
|
try {
|
||||||
const res = await exportExcel(state.queryForm)
|
const res = await exportExcel(state.queryForm)
|
||||||
// 创建 Blob 对象
|
|
||||||
const blob = new Blob([res.data as BlobPart], {
|
const blob = new Blob([res.data as BlobPart], {
|
||||||
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||||
})
|
})
|
||||||
// 创建下载链接
|
|
||||||
const url = window.URL.createObjectURL(blob)
|
const url = window.URL.createObjectURL(blob)
|
||||||
const link = document.createElement('a')
|
const link = document.createElement('a')
|
||||||
link.href = url
|
link.href = url
|
||||||
@@ -385,7 +331,6 @@ const getSubListData = async (activityInfoId?: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
loadSavedConfig()
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getActivityInfoListData()
|
getActivityInfoListData()
|
||||||
getSubListData()
|
getSubListData()
|
||||||
@@ -393,5 +338,5 @@ onMounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="state.queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form
|
||||||
|
:model="state.queryForm"
|
||||||
|
ref="searchFormRef"
|
||||||
|
:inline="true"
|
||||||
|
@keyup.enter="getDataList"
|
||||||
|
class="search-form">
|
||||||
<el-form-item label="考核类型" prop="type">
|
<el-form-item label="考核类型" prop="type">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="state.queryForm.type"
|
v-model="state.queryForm.type"
|
||||||
@@ -19,107 +32,127 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" plain icon="Search" @click="getDataList">查询</el-button>
|
<el-button type="primary" icon="Search" @click="getDataList">查询</el-button>
|
||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="Plus"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
@click="formDialogRef.openDialog()">
|
考核项列表
|
||||||
新增
|
</span>
|
||||||
</el-button>
|
<div class="header-actions">
|
||||||
<right-toolbar
|
<el-button
|
||||||
v-model:showSearch="showSearch"
|
icon="Plus"
|
||||||
class="ml10 mr20"
|
type="primary"
|
||||||
style="float: right;"
|
@click="formDialogRef.openDialog()">
|
||||||
@queryTable="getDataList">
|
新增
|
||||||
<TableColumnControl
|
</el-button>
|
||||||
ref="columnControlRef"
|
<right-toolbar
|
||||||
:columns="tableColumns"
|
v-model:showSearch="showSearch"
|
||||||
v-model="visibleColumns"
|
class="ml10"
|
||||||
trigger-type="default"
|
@queryTable="getDataList">
|
||||||
trigger-circle
|
<TableColumnControl
|
||||||
@change="handleColumnChange"
|
ref="columnControlRef"
|
||||||
@order-change="handleColumnOrderChange"
|
:columns="tableColumns"
|
||||||
>
|
v-model="visibleColumns"
|
||||||
<template #trigger>
|
trigger-type="default"
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
trigger-circle
|
||||||
<el-button circle style="margin-left: 0;">
|
@change="handleColumnChange"
|
||||||
<el-icon><Menu /></el-icon>
|
@order-change="handleColumnOrderChange"
|
||||||
</el-button>
|
>
|
||||||
</el-tooltip>
|
<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="序号" 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 !== '操作'"
|
||||||
|
: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>
|
||||||
</TableColumnControl>
|
<!-- 考核类型列特殊模板 -->
|
||||||
</right-toolbar>
|
<template v-if="col.prop === 'type'" #default="scope">
|
||||||
|
<el-tag size="small" type="info" effect="plain" round>
|
||||||
|
{{ formatType(scope.row.type) }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column label="操作" width="180" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
icon="EditPen"
|
||||||
|
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>
|
||||||
|
<template #empty>
|
||||||
|
<el-empty description="暂无数据" :image-size="120">
|
||||||
|
<el-button type="primary" icon="Plus" @click="formDialogRef.openDialog()">新增考核项</el-button>
|
||||||
|
</el-empty>
|
||||||
|
</template>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<div class="pagination-wrapper">
|
||||||
|
<pagination
|
||||||
|
@size-change="sizeChangeHandle"
|
||||||
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
</div>
|
</div>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 表格 -->
|
|
||||||
<el-table
|
|
||||||
:data="state.dataList"
|
|
||||||
v-loading="state.loading"
|
|
||||||
border
|
|
||||||
:cell-style="tableStyle.cellStyle"
|
|
||||||
:header-cell-style="tableStyle.headerCellStyle">
|
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
v-for="col in visibleColumnsSorted"
|
|
||||||
:key="col.prop"
|
|
||||||
: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 === 'type'">
|
|
||||||
<el-tag size="small" type="info" effect="plain">
|
|
||||||
{{ formatType(scope.row.type) }}
|
|
||||||
</el-tag>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><Setting /></el-icon>
|
|
||||||
<span style="margin-left: 4px">操作</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
icon="Edit"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleEdit(scope.row)">
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Delete"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row)">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
|
||||||
<pagination
|
|
||||||
@size-change="sizeChangeHandle"
|
|
||||||
@current-change="currentChangeHandle"
|
|
||||||
v-bind="state.pagination" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 新增/编辑表单弹窗 -->
|
<!-- 新增/编辑表单弹窗 -->
|
||||||
@@ -128,18 +161,16 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="AssessmentCategory">
|
<script setup lang="ts" name="AssessmentCategory">
|
||||||
import { reactive, ref, onMounted, computed } from 'vue'
|
import { reactive, ref, onMounted } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||||
import { fetchList, delObj } from "/@/api/stuwork/assessmentcategory";
|
import { fetchList, delObj } from "/@/api/stuwork/assessmentcategory";
|
||||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import FormDialog from './form.vue'
|
import FormDialog from './form.vue'
|
||||||
import { List, Document, Collection, EditPen, Setting, Menu } from '@element-plus/icons-vue'
|
import { List, Document, Collection, EditPen, Setting, Menu, Search } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
|
||||||
const formDialogRef = ref()
|
const formDialogRef = ref()
|
||||||
const searchFormRef = ref()
|
const searchFormRef = ref()
|
||||||
const columnControlRef = ref()
|
const columnControlRef = ref()
|
||||||
@@ -152,94 +183,14 @@ const tableColumns = [
|
|||||||
{ prop: 'remarks', label: '备注', icon: EditPen, minWidth: 200 }
|
{ prop: 'remarks', label: '备注', icon: EditPen, minWidth: 200 }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制 Hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列变化处理
|
|
||||||
const handleColumnChange = (value: string[]) => {
|
|
||||||
visibleColumns.value = value
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = value.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列排序变化处理
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const visibleColumnsSorted = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: any[] = []
|
|
||||||
const unorderedColumns: any[] = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 考核类型列表
|
// 考核类型列表
|
||||||
const typeList = ref([
|
const typeList = ref([
|
||||||
@@ -254,7 +205,6 @@ const tableStyle = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 配置 useTable
|
// 配置 useTable
|
||||||
loadSavedConfig()
|
|
||||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||||
queryForm: {
|
queryForm: {
|
||||||
type: ''
|
type: ''
|
||||||
@@ -264,7 +214,7 @@ const state: BasicTableProps = reactive<BasicTableProps>({
|
|||||||
item: 'records',
|
item: 'records',
|
||||||
totalCount: 'total'
|
totalCount: 'total'
|
||||||
},
|
},
|
||||||
createdIsNeed: true // 页面加载时自动获取数据
|
createdIsNeed: true
|
||||||
})
|
})
|
||||||
|
|
||||||
// table hook
|
// table hook
|
||||||
@@ -313,5 +263,5 @@ const handleDelete = async (row: any) => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="state.queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form
|
||||||
|
:model="state.queryForm"
|
||||||
|
ref="searchFormRef"
|
||||||
|
:inline="true"
|
||||||
|
@keyup.enter="getDataList"
|
||||||
|
class="search-form">
|
||||||
<el-form-item label="指标名称" prop="pointName">
|
<el-form-item label="指标名称" prop="pointName">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="state.queryForm.pointName"
|
v-model="state.queryForm.pointName"
|
||||||
@@ -12,108 +25,128 @@
|
|||||||
style="width: 200px" />
|
style="width: 200px" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" plain icon="Search" @click="getDataList">查询</el-button>
|
<el-button type="primary" icon="Search" @click="getDataList">查询</el-button>
|
||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="Plus"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
@click="formDialogRef.openDialog()">
|
考核指标列表
|
||||||
新增
|
</span>
|
||||||
</el-button>
|
<div class="header-actions">
|
||||||
<right-toolbar
|
<el-button
|
||||||
v-model:showSearch="showSearch"
|
icon="Plus"
|
||||||
class="ml10 mr20"
|
type="primary"
|
||||||
style="float: right;"
|
@click="formDialogRef.openDialog()">
|
||||||
@queryTable="getDataList">
|
新增
|
||||||
<TableColumnControl
|
</el-button>
|
||||||
ref="columnControlRef"
|
<right-toolbar
|
||||||
:columns="tableColumns"
|
v-model:showSearch="showSearch"
|
||||||
v-model="visibleColumns"
|
class="ml10"
|
||||||
trigger-type="default"
|
@queryTable="getDataList">
|
||||||
trigger-circle
|
<TableColumnControl
|
||||||
@change="handleColumnChange"
|
ref="columnControlRef"
|
||||||
@order-change="handleColumnOrderChange"
|
:columns="tableColumns"
|
||||||
>
|
v-model="visibleColumns"
|
||||||
<template #trigger>
|
trigger-type="default"
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
trigger-circle
|
||||||
<el-button circle style="margin-left: 0;">
|
@change="handleColumnChange"
|
||||||
<el-icon><Menu /></el-icon>
|
@order-change="handleColumnOrderChange"
|
||||||
</el-button>
|
>
|
||||||
</el-tooltip>
|
<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="序号" 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 !== '操作'"
|
||||||
|
: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>
|
||||||
</TableColumnControl>
|
<!-- 默认扣分值列特殊模板 -->
|
||||||
</right-toolbar>
|
<template v-if="col.prop === 'score'" #default="scope">
|
||||||
|
<el-tag v-if="scope.row.score !== undefined && scope.row.score !== null" size="small" type="danger" effect="plain" round>
|
||||||
|
{{ scope.row.score }}
|
||||||
|
</el-tag>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column label="操作" width="180" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
icon="EditPen"
|
||||||
|
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>
|
||||||
|
<template #empty>
|
||||||
|
<el-empty description="暂无数据" :image-size="120">
|
||||||
|
<el-button type="primary" icon="Plus" @click="formDialogRef.openDialog()">新增指标</el-button>
|
||||||
|
</el-empty>
|
||||||
|
</template>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<div class="pagination-wrapper">
|
||||||
|
<pagination
|
||||||
|
@size-change="sizeChangeHandle"
|
||||||
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
</div>
|
</div>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 表格 -->
|
|
||||||
<el-table
|
|
||||||
:data="state.dataList"
|
|
||||||
v-loading="state.loading"
|
|
||||||
border
|
|
||||||
:cell-style="tableStyle.cellStyle"
|
|
||||||
:header-cell-style="tableStyle.headerCellStyle">
|
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
v-for="col in visibleColumnsSorted"
|
|
||||||
:key="col.prop"
|
|
||||||
: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 === 'score'">
|
|
||||||
<el-tag v-if="scope.row.score !== undefined && scope.row.score !== null" size="small" type="danger" effect="plain">
|
|
||||||
{{ scope.row.score }}
|
|
||||||
</el-tag>
|
|
||||||
<span v-else>-</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><Setting /></el-icon>
|
|
||||||
<span style="margin-left: 4px">操作</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
icon="Edit"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleEdit(scope.row)">
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Delete"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row)">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
|
||||||
<pagination
|
|
||||||
@size-change="sizeChangeHandle"
|
|
||||||
@current-change="currentChangeHandle"
|
|
||||||
v-bind="state.pagination" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 新增/编辑表单弹窗 -->
|
<!-- 新增/编辑表单弹窗 -->
|
||||||
@@ -122,18 +155,16 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="AssessmentPoint">
|
<script setup lang="ts" name="AssessmentPoint">
|
||||||
import { reactive, ref, onMounted, computed } from 'vue'
|
import { reactive, ref } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||||
import { fetchList, delObj } from "/@/api/stuwork/assessmentpoint";
|
import { fetchList, delObj } from "/@/api/stuwork/assessmentpoint";
|
||||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import FormDialog from './form.vue'
|
import FormDialog from './form.vue'
|
||||||
import { List, Document, Trophy, Reading, Minus, EditPen, Setting, Menu } from '@element-plus/icons-vue'
|
import { List, Document, Trophy, Reading, Minus, EditPen, Setting, Menu, Search } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
|
||||||
const formDialogRef = ref()
|
const formDialogRef = ref()
|
||||||
const searchFormRef = ref()
|
const searchFormRef = ref()
|
||||||
const columnControlRef = ref()
|
const columnControlRef = ref()
|
||||||
@@ -148,94 +179,14 @@ const tableColumns = [
|
|||||||
{ prop: 'remarks', label: '备注', icon: EditPen, minWidth: 200 }
|
{ prop: 'remarks', label: '备注', icon: EditPen, minWidth: 200 }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制 Hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列变化处理
|
|
||||||
const handleColumnChange = (value: string[]) => {
|
|
||||||
visibleColumns.value = value
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = value.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列排序变化处理
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const visibleColumnsSorted = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: any[] = []
|
|
||||||
const unorderedColumns: any[] = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 表格样式
|
// 表格样式
|
||||||
const tableStyle = {
|
const tableStyle = {
|
||||||
@@ -244,7 +195,6 @@ const tableStyle = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 配置 useTable
|
// 配置 useTable
|
||||||
loadSavedConfig()
|
|
||||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||||
queryForm: {
|
queryForm: {
|
||||||
pointName: ''
|
pointName: ''
|
||||||
@@ -254,7 +204,7 @@ const state: BasicTableProps = reactive<BasicTableProps>({
|
|||||||
item: 'records',
|
item: 'records',
|
||||||
totalCount: 'total'
|
totalCount: 'total'
|
||||||
},
|
},
|
||||||
createdIsNeed: true // 页面加载时自动获取数据
|
createdIsNeed: true
|
||||||
})
|
})
|
||||||
|
|
||||||
// table hook
|
// table hook
|
||||||
@@ -291,13 +241,8 @@ const handleDelete = async (row: any) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化
|
|
||||||
onMounted(() => {
|
|
||||||
// 初始化完成
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="state.queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</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-form-item label="学年" prop="schoolYear">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="state.queryForm.schoolYear"
|
v-model="state.queryForm.schoolYear"
|
||||||
@@ -49,142 +62,165 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" plain icon="Search" @click="getDataList">查询</el-button>
|
<el-button type="primary" icon="Search" @click="getDataList">查询</el-button>
|
||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="Plus"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
class="ml10"
|
班级活动列表
|
||||||
@click="formDialogRef.openDialog()">
|
</span>
|
||||||
新增
|
<div class="header-actions">
|
||||||
</el-button>
|
<el-button
|
||||||
<right-toolbar
|
icon="Plus"
|
||||||
v-model:showSearch="showSearch"
|
type="primary"
|
||||||
class="ml10 mr20"
|
@click="formDialogRef.openDialog()">
|
||||||
style="float: right;"
|
新增
|
||||||
@queryTable="getDataList">
|
</el-button>
|
||||||
<TableColumnControl
|
<right-toolbar
|
||||||
ref="columnControlRef"
|
v-model:showSearch="showSearch"
|
||||||
:columns="tableColumns"
|
class="ml10"
|
||||||
v-model="visibleColumns"
|
@queryTable="getDataList">
|
||||||
trigger-type="default"
|
<TableColumnControl
|
||||||
trigger-circle
|
ref="columnControlRef"
|
||||||
@change="handleColumnChange"
|
:columns="tableColumns"
|
||||||
@order-change="handleColumnOrderChange"
|
v-model="visibleColumns"
|
||||||
>
|
trigger-type="default"
|
||||||
<template #trigger>
|
trigger-circle
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
@change="handleColumnChange"
|
||||||
<el-button circle style="margin-left: 0;">
|
@order-change="handleColumnOrderChange"
|
||||||
<el-icon><Menu /></el-icon>
|
>
|
||||||
</el-button>
|
<template #trigger>
|
||||||
</el-tooltip>
|
<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="序号" 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 !== '操作'"
|
||||||
|
:prop="col.prop"
|
||||||
|
:label="col.label"
|
||||||
|
:width="col.width"
|
||||||
|
:min-width="col.minWidth"
|
||||||
|
:show-overflow-tooltip="col.showOverflowTooltip !== false"
|
||||||
|
:align="col.align || 'center'"
|
||||||
|
:fixed="col.fixed">
|
||||||
|
<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>
|
||||||
</TableColumnControl>
|
<!-- 学期列特殊模板 -->
|
||||||
</right-toolbar>
|
<template v-if="col.prop === 'schoolTerm'" #default="scope">
|
||||||
|
<el-tag size="small" type="primary" effect="plain" round>
|
||||||
|
{{ formatSchoolTerm(scope.row.schoolTerm) }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
<!-- 活动时间列特殊模板 -->
|
||||||
|
<template v-else-if="col.prop === 'activityTime'" #default="scope">
|
||||||
|
<span>{{ scope.row.activityTime || scope.row.recordDate || '-' }}</span>
|
||||||
|
</template>
|
||||||
|
<!-- 参加人数列特殊模板 -->
|
||||||
|
<template v-else-if="col.prop === 'attendNum'" #default="scope">
|
||||||
|
<el-tag v-if="scope.row.attendNum" size="small" type="success" effect="plain" round>
|
||||||
|
{{ scope.row.attendNum }}
|
||||||
|
</el-tag>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
<!-- 活动图片列特殊模板 -->
|
||||||
|
<template v-else-if="col.prop === 'attachment'" #default="scope">
|
||||||
|
<el-button
|
||||||
|
v-if="scope.row.attachment"
|
||||||
|
icon="Picture"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
@click="handleViewImage(scope.row.attachment)">
|
||||||
|
查看
|
||||||
|
</el-button>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
<!-- 活动图片2列特殊模板 -->
|
||||||
|
<template v-else-if="col.prop === 'attachment2'" #default="scope">
|
||||||
|
<el-button
|
||||||
|
v-if="scope.row.attachment2"
|
||||||
|
icon="Picture"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
@click="handleViewImage(scope.row.attachment2)">
|
||||||
|
查看
|
||||||
|
</el-button>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column label="操作" width="180" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
icon="EditPen"
|
||||||
|
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>
|
||||||
|
<template #empty>
|
||||||
|
<el-empty description="暂无数据" :image-size="120">
|
||||||
|
<el-button type="primary" icon="Plus" @click="formDialogRef.openDialog()">新增活动</el-button>
|
||||||
|
</el-empty>
|
||||||
|
</template>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<div class="pagination-wrapper">
|
||||||
|
<pagination
|
||||||
|
@size-change="sizeChangeHandle"
|
||||||
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
</div>
|
</div>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 表格 -->
|
|
||||||
<el-table
|
|
||||||
:data="state.dataList"
|
|
||||||
v-loading="state.loading"
|
|
||||||
border
|
|
||||||
:cell-style="tableStyle.cellStyle"
|
|
||||||
:header-cell-style="tableStyle.headerCellStyle">
|
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
v-for="col in visibleColumnsSorted"
|
|
||||||
:key="col.prop"
|
|
||||||
:prop="col.prop"
|
|
||||||
:label="col.label"
|
|
||||||
:width="col.width"
|
|
||||||
:min-width="col.minWidth"
|
|
||||||
:show-overflow-tooltip="col.showOverflowTooltip !== false"
|
|
||||||
:align="col.align || 'center'"
|
|
||||||
:fixed="col.fixed">
|
|
||||||
<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 === 'activityTime'">
|
|
||||||
<span>{{ scope.row.activityTime || scope.row.recordDate || '-' }}</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope" v-else-if="col.prop === 'attendNum'">
|
|
||||||
<el-tag v-if="scope.row.attendNum" size="small" type="success" effect="plain">
|
|
||||||
{{ scope.row.attendNum }}
|
|
||||||
</el-tag>
|
|
||||||
<span v-else>-</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope" v-else-if="col.prop === 'attachment'">
|
|
||||||
<el-button
|
|
||||||
v-if="scope.row.attachment"
|
|
||||||
icon="Picture"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
size="small"
|
|
||||||
@click="handleViewImage(scope.row.attachment)">
|
|
||||||
查看
|
|
||||||
</el-button>
|
|
||||||
<span v-else>-</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope" v-else-if="col.prop === 'attachment2'">
|
|
||||||
<el-button
|
|
||||||
v-if="scope.row.attachment2"
|
|
||||||
icon="Picture"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
size="small"
|
|
||||||
@click="handleViewImage(scope.row.attachment2)">
|
|
||||||
查看
|
|
||||||
</el-button>
|
|
||||||
<span v-else>-</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="操作" width="200" align="center" fixed="right">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><Setting /></el-icon>
|
|
||||||
<span style="margin-left: 4px">操作</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
icon="Edit"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleEdit(scope.row)">
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Delete"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row)">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
|
||||||
<pagination
|
|
||||||
@size-change="sizeChangeHandle"
|
|
||||||
@current-change="currentChangeHandle"
|
|
||||||
v-bind="state.pagination" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 新增/编辑表单弹窗 -->
|
<!-- 新增/编辑表单弹窗 -->
|
||||||
@@ -193,8 +229,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="ClassActivity">
|
<script setup lang="ts" name="ClassActivity">
|
||||||
import { reactive, ref, onMounted, computed, nextTick } from 'vue'
|
import { reactive, ref, onMounted } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||||
import { fetchList, delObj } from "/@/api/stuwork/classactivity";
|
import { fetchList, delObj } from "/@/api/stuwork/classactivity";
|
||||||
import { getClassListByRole } from "/@/api/basic/basicclass";
|
import { getClassListByRole } from "/@/api/basic/basicclass";
|
||||||
@@ -203,11 +238,10 @@ import { getDicts } from "/@/api/admin/dict";
|
|||||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import FormDialog from './form.vue'
|
import FormDialog from './form.vue'
|
||||||
import { List, Calendar, Clock, Grid, Trophy, User, Location, UserFilled, Setting, Menu } from '@element-plus/icons-vue'
|
import { List, Calendar, Clock, Grid, Trophy, User, Location, UserFilled, Setting, Menu, Search, Document, EditPen, Picture } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
|
||||||
const formDialogRef = ref()
|
const formDialogRef = ref()
|
||||||
const searchFormRef = ref()
|
const searchFormRef = ref()
|
||||||
const columnControlRef = ref()
|
const columnControlRef = ref()
|
||||||
@@ -226,103 +260,18 @@ const tableColumns = [
|
|||||||
{ prop: 'activityTime', label: '活动时间', icon: Calendar, width: 120 },
|
{ prop: 'activityTime', label: '活动时间', icon: Calendar, width: 120 },
|
||||||
{ prop: 'address', label: '活动地点', icon: Location, minWidth: 150 },
|
{ prop: 'address', label: '活动地点', icon: Location, minWidth: 150 },
|
||||||
{ prop: 'attendNum', label: '参加人数', icon: UserFilled },
|
{ prop: 'attendNum', label: '参加人数', icon: UserFilled },
|
||||||
{ prop: 'attachment', label: '活动图片', width: 100 },
|
{ prop: 'attachment', label: '活动图片', icon: Picture, width: 100 },
|
||||||
{ prop: 'attachment2', label: '活动图片2', width: 100 }
|
{ prop: 'attachment2', label: '活动图片2', icon: Picture, width: 100 }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制 Hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查列是否可见
|
|
||||||
const checkColumnVisible = (prop: string) => {
|
|
||||||
return visibleColumns.value.includes(prop)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列变化处理
|
|
||||||
const handleColumnChange = (value: string[]) => {
|
|
||||||
visibleColumns.value = value
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = value.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列排序变化处理
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const visibleColumnsSorted = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: any[] = []
|
|
||||||
const unorderedColumns: any[] = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 表格样式
|
// 表格样式
|
||||||
const tableStyle = {
|
const tableStyle = {
|
||||||
@@ -342,7 +291,7 @@ const state: BasicTableProps = reactive<BasicTableProps>({
|
|||||||
item: 'records',
|
item: 'records',
|
||||||
totalCount: 'total'
|
totalCount: 'total'
|
||||||
},
|
},
|
||||||
createdIsNeed: true // 页面加载时自动获取数据
|
createdIsNeed: true
|
||||||
})
|
})
|
||||||
|
|
||||||
// table hook
|
// table hook
|
||||||
@@ -449,7 +398,6 @@ const getClassListData = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
loadSavedConfig()
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getSchoolYearList()
|
getSchoolYearList()
|
||||||
getSchoolTermDict()
|
getSchoolTermDict()
|
||||||
@@ -458,5 +406,5 @@ onMounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form
|
||||||
|
:model="searchForm"
|
||||||
|
ref="searchFormRef"
|
||||||
|
:inline="true"
|
||||||
|
@keyup.enter="handleSearch"
|
||||||
|
class="search-form">
|
||||||
<el-form-item label="学年" prop="schoolYear">
|
<el-form-item label="学年" prop="schoolYear">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="searchForm.schoolYear"
|
v-model="searchForm.schoolYear"
|
||||||
@@ -58,102 +71,121 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" plain icon="Search" @click="handleSearch">查询</el-button>
|
<el-button type="primary" icon="Search" @click="handleSearch">查询</el-button>
|
||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="FolderAdd"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
class="ml10"
|
班级考核结算列表
|
||||||
@click="formDialogRef.openDialog()">
|
</span>
|
||||||
新 增
|
<div class="header-actions">
|
||||||
</el-button>
|
<el-button
|
||||||
<right-toolbar
|
icon="FolderAdd"
|
||||||
v-model:showSearch="showSearch"
|
type="primary"
|
||||||
class="ml10 mr20"
|
@click="formDialogRef.openDialog()">
|
||||||
style="float: right;"
|
新增
|
||||||
@queryTable="getDataList">
|
</el-button>
|
||||||
<TableColumnControl
|
<right-toolbar
|
||||||
ref="columnControlRef"
|
v-model:showSearch="showSearch"
|
||||||
:columns="tableColumns"
|
class="ml10"
|
||||||
v-model="visibleColumns"
|
@queryTable="getDataList">
|
||||||
trigger-type="default"
|
<TableColumnControl
|
||||||
trigger-circle
|
ref="columnControlRef"
|
||||||
@change="handleColumnChange"
|
:columns="tableColumns"
|
||||||
@order-change="handleColumnOrderChange"
|
v-model="visibleColumns"
|
||||||
>
|
trigger-type="default"
|
||||||
<template #trigger>
|
trigger-circle
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
@change="handleColumnChange"
|
||||||
<el-button circle style="margin-left: 0;">
|
@order-change="handleColumnOrderChange"
|
||||||
<el-icon><Menu /></el-icon>
|
>
|
||||||
</el-button>
|
<template #trigger>
|
||||||
</el-tooltip>
|
<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"
|
||||||
|
@sort-change="sortChangeHandle">
|
||||||
|
<el-table-column type="index" label="序号" 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 !== '操作'"
|
||||||
|
: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>
|
||||||
</TableColumnControl>
|
<!-- 学期列特殊模板 -->
|
||||||
</right-toolbar>
|
<template v-if="col.prop === 'schoolTerm'" #default="scope">
|
||||||
|
<el-tag size="small" type="primary" effect="plain" round>
|
||||||
|
{{ formatSchoolTerm(scope.row.schoolTerm) }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column label="操作" width="100" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
icon="Delete"
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
@click="handleDelete([scope.row.id])">
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<template #empty>
|
||||||
|
<el-empty description="暂无数据" :image-size="120">
|
||||||
|
<el-button type="primary" icon="FolderAdd" @click="formDialogRef.openDialog()">新增结算</el-button>
|
||||||
|
</el-empty>
|
||||||
|
</template>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<div class="pagination-wrapper">
|
||||||
|
<pagination
|
||||||
|
@size-change="sizeChangeHandle"
|
||||||
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
</div>
|
</div>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 表格 -->
|
|
||||||
<el-table
|
|
||||||
:data="state.dataList"
|
|
||||||
v-loading="state.loading"
|
|
||||||
border
|
|
||||||
:cell-style="tableStyle.cellStyle"
|
|
||||||
:header-cell-style="tableStyle.headerCellStyle"
|
|
||||||
@sort-change="sortChangeHandle">
|
|
||||||
<el-table-column type="index" label="序号" align="center">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
v-for="col in visibleColumnsSorted"
|
|
||||||
:key="col.prop"
|
|
||||||
: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>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="操作" align="center" fixed="right">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><Setting /></el-icon>
|
|
||||||
<span style="margin-left: 4px">操作</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
icon="Delete"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete([scope.row.id])">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
|
||||||
<pagination
|
|
||||||
@size-change="sizeChangeHandle"
|
|
||||||
@current-change="currentChangeHandle"
|
|
||||||
v-bind="state.pagination" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 编辑、新增 -->
|
<!-- 编辑、新增 -->
|
||||||
@@ -163,30 +195,29 @@
|
|||||||
|
|
||||||
<script setup lang="ts" name="ClassAssessmentSettle">
|
<script setup lang="ts" name="ClassAssessmentSettle">
|
||||||
import { ref, reactive, defineAsyncComponent, computed, onMounted } from 'vue'
|
import { ref, reactive, defineAsyncComponent, computed, onMounted } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||||
import { fetchList, delObjs } from "/@/api/stuwork/classassessmentsettle";
|
import { fetchList, delObjs } from "/@/api/stuwork/classassessmentsettle";
|
||||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||||
import { getDeptList } from '/@/api/basic/basicclass'
|
import { getDeptList } from '/@/api/basic/basicclass'
|
||||||
import { getClassListByRole } from '/@/api/basic/basicclass'
|
import { getClassListByRole } from '/@/api/basic/basicclass'
|
||||||
import { queryAllSchoolYear } from '/@/api/basic/basicyear'
|
import { queryAllSchoolYear } from '/@/api/basic/basicyear'
|
||||||
|
import { getDicts } from '/@/api/admin/dict'
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
|
import { List, Calendar, Clock, Grid, Setting, Menu, Search, Document, FolderAdd } from '@element-plus/icons-vue'
|
||||||
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
|
||||||
// 引入组件
|
// 引入组件
|
||||||
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
||||||
import { List, Calendar, Clock, Grid, Setting, Menu } from '@element-plus/icons-vue'
|
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
|
||||||
const formDialogRef = ref()
|
const formDialogRef = ref()
|
||||||
const searchFormRef = ref()
|
const searchFormRef = ref()
|
||||||
const columnControlRef = ref()
|
const columnControlRef = ref()
|
||||||
// 搜索变量
|
|
||||||
const showSearch = ref(true)
|
const showSearch = ref(true)
|
||||||
const deptList = ref<any[]>([])
|
const deptList = ref<any[]>([])
|
||||||
const classList = ref<any[]>([])
|
const classList = ref<any[]>([])
|
||||||
const schoolYearList = ref<any[]>([])
|
const schoolYearList = ref<any[]>([])
|
||||||
|
const schoolTermList = ref<any[]>([])
|
||||||
|
|
||||||
// 表格列配置
|
// 表格列配置
|
||||||
const tableColumns = [
|
const tableColumns = [
|
||||||
@@ -195,94 +226,14 @@ const tableColumns = [
|
|||||||
{ prop: 'virtualClassNo', label: '班号', icon: Grid }
|
{ prop: 'virtualClassNo', label: '班号', icon: Grid }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制 Hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列变化处理
|
|
||||||
const handleColumnChange = (value: string[]) => {
|
|
||||||
visibleColumns.value = value
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = value.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列排序变化处理
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const visibleColumnsSorted = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: any[] = []
|
|
||||||
const unorderedColumns: any[] = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 搜索表单
|
// 搜索表单
|
||||||
const searchForm = reactive({
|
const searchForm = reactive({
|
||||||
@@ -319,9 +270,17 @@ const {
|
|||||||
tableStyle
|
tableStyle
|
||||||
} = useTable(state)
|
} = useTable(state)
|
||||||
|
|
||||||
|
// 格式化学期
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
// 学院选择变化
|
// 学院选择变化
|
||||||
const handleDeptChange = () => {
|
const handleDeptChange = () => {
|
||||||
// 清空班号选择
|
|
||||||
searchForm.classCode = ''
|
searchForm.classCode = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -361,7 +320,6 @@ const handleDelete = async (ids: string[]) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 获取学院列表
|
// 获取学院列表
|
||||||
const getDeptListData = async () => {
|
const getDeptListData = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -398,11 +356,32 @@ const getSchoolYearList = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取学期字典
|
||||||
|
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 = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
loadSavedConfig()
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getDeptListData()
|
getDeptListData()
|
||||||
getClassListData()
|
getClassListData()
|
||||||
getSchoolYearList()
|
getSchoolYearList()
|
||||||
|
getSchoolTermDict()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,9 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form
|
||||||
|
:model="searchForm"
|
||||||
|
ref="searchFormRef"
|
||||||
|
:inline="true"
|
||||||
|
@keyup.enter="handleSearch"
|
||||||
|
class="search-form">
|
||||||
<el-form-item label="班级" prop="classCode">
|
<el-form-item label="班级" prop="classCode">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="searchForm.classCode"
|
v-model="searchForm.classCode"
|
||||||
@@ -34,123 +47,141 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" plain icon="Search" @click="handleSearch">查询</el-button>
|
<el-button type="primary" icon="Search" @click="handleSearch">查询</el-button>
|
||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<right-toolbar
|
<div class="card-header">
|
||||||
v-model:showSearch="showSearch"
|
<span class="card-title">
|
||||||
class="ml10 mr20"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
style="float: right;"
|
班级考勤列表
|
||||||
@queryTable="getDataList">
|
</span>
|
||||||
<TableColumnControl
|
<div class="header-actions">
|
||||||
ref="columnControlRef"
|
<right-toolbar
|
||||||
:columns="tableColumns"
|
v-model:showSearch="showSearch"
|
||||||
v-model="visibleColumns"
|
class="ml10"
|
||||||
trigger-type="default"
|
@queryTable="getDataList">
|
||||||
trigger-circle
|
<TableColumnControl
|
||||||
@change="handleColumnChange"
|
ref="columnControlRef"
|
||||||
@order-change="handleColumnOrderChange"
|
:columns="tableColumns"
|
||||||
>
|
v-model="visibleColumns"
|
||||||
<template #trigger>
|
trigger-type="default"
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
trigger-circle
|
||||||
<el-button circle style="margin-left: 0;">
|
@change="handleColumnChange"
|
||||||
<el-icon><Menu /></el-icon>
|
@order-change="handleColumnOrderChange"
|
||||||
</el-button>
|
>
|
||||||
</el-tooltip>
|
<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="dataList"
|
||||||
|
v-loading="loading"
|
||||||
|
stripe
|
||||||
|
:cell-style="tableStyle.cellStyle"
|
||||||
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
|
class="modern-table">
|
||||||
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><List /></el-icon>
|
||||||
|
</template>
|
||||||
|
<template #default="{ $index }">
|
||||||
|
{{ $index + 1 }}
|
||||||
|
</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 !== '操作'"
|
||||||
|
: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>
|
||||||
</TableColumnControl>
|
<!-- 学生状态列特殊模板 -->
|
||||||
</right-toolbar>
|
<template v-if="col.prop === 'stuStatus'" #default="scope">
|
||||||
</div>
|
<el-tag size="small" type="info" effect="plain" round>
|
||||||
</el-row>
|
{{ formatStudentStatus(scope.row.stuStatus) }}
|
||||||
|
</el-tag>
|
||||||
<!-- 表格 -->
|
</template>
|
||||||
<el-table
|
<!-- 考勤类型列特殊模板 -->
|
||||||
:data="dataList"
|
<template v-else-if="col.prop === 'attendanceType'" #default="scope">
|
||||||
v-loading="loading"
|
<el-tag size="small" type="warning" effect="plain" round>
|
||||||
border
|
{{ formatAttendanceType(scope.row.attendanceType) }}
|
||||||
:cell-style="tableStyle.cellStyle"
|
</el-tag>
|
||||||
:header-cell-style="tableStyle.headerCellStyle">
|
</template>
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
<!-- 是否住宿列特殊模板 -->
|
||||||
<template #header>
|
<template v-else-if="col.prop === 'isRoom'" #default="scope">
|
||||||
<el-icon><List /></el-icon>
|
<StatusTag
|
||||||
|
:value="scope.row.isRoom"
|
||||||
|
:options="[{ label: '是', value: '1' }, { label: '否', value: '0' }]"
|
||||||
|
:type-map="{ '1': { type: 'success', effect: 'light' }, '0': { type: 'info', effect: 'light' } }"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<!-- 是否扫脸列特殊模板 -->
|
||||||
|
<template v-else-if="col.prop === 'isDeviceIn'" #default="scope">
|
||||||
|
<el-tag :type="scope.row.isDeviceIn === '1' ? 'success' : 'danger'" size="small" round>
|
||||||
|
{{ scope.row.isDeviceIn === '1' ? '是' : '否' }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
<el-table-column label="操作" width="100" align="center" fixed="right">
|
||||||
<el-table-column
|
<template #header>
|
||||||
v-for="col in visibleColumnsSorted"
|
<el-icon><Setting /></el-icon>
|
||||||
:key="col.prop"
|
<span style="margin-left: 4px">操作</span>
|
||||||
:prop="col.prop"
|
</template>
|
||||||
:label="col.label"
|
<template #default="scope">
|
||||||
:width="col.width"
|
<el-button
|
||||||
:min-width="col.minWidth"
|
icon="EditPen"
|
||||||
:show-overflow-tooltip="col.showOverflowTooltip !== false"
|
link
|
||||||
:align="col.align || 'center'">
|
type="primary"
|
||||||
<template #header>
|
@click="handleEdit(scope.row)">
|
||||||
<el-icon v-if="col.icon"><component :is="col.icon" /></el-icon>
|
编辑
|
||||||
<span :style="{ marginLeft: col.icon ? '4px' : '0' }">{{ col.label }}</span>
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<template #empty>
|
||||||
|
<el-empty description="暂无数据" :image-size="120">
|
||||||
|
</el-empty>
|
||||||
</template>
|
</template>
|
||||||
<template #default="scope" v-if="col.prop === 'stuStatus'">
|
</el-table>
|
||||||
<el-tag size="small" type="info" effect="plain">
|
</el-card>
|
||||||
{{ formatStudentStatus(scope.row.stuStatus) }}
|
|
||||||
</el-tag>
|
|
||||||
</template>
|
|
||||||
<template #default="scope" v-else-if="col.prop === 'attendanceType'">
|
|
||||||
<el-tag size="small" type="warning" effect="plain">
|
|
||||||
{{ formatAttendanceType(scope.row.attendanceType) }}
|
|
||||||
</el-tag>
|
|
||||||
</template>
|
|
||||||
<template #default="scope" v-else-if="col.prop === 'isRoom'">
|
|
||||||
<StatusTag
|
|
||||||
:value="scope.row.isRoom"
|
|
||||||
:options="[{ label: '是', value: '1' }, { label: '否', value: '0' }]"
|
|
||||||
:type-map="{ '1': { type: 'success', effect: 'light' }, '0': { type: 'info', effect: 'light' } }"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #default="scope" v-else-if="col.prop === 'isDeviceIn'">
|
|
||||||
<el-tag :type="scope.row.isDeviceIn === '1' ? 'success' : 'danger'">
|
|
||||||
{{ scope.row.isDeviceIn === '1' ? '是' : '否' }}
|
|
||||||
</el-tag>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="操作" width="100" align="center" fixed="right">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><Setting /></el-icon>
|
|
||||||
<span style="margin-left: 4px">操作</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
icon="Edit"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleEdit(scope.row)">
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="ClassAttendance">
|
<script setup lang="ts" name="ClassAttendance">
|
||||||
import { reactive, ref, onMounted, computed } from 'vue'
|
import { reactive, ref, onMounted } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import { fetchList, queryMyClassList } from '/@/api/stuwork/classattendance'
|
import { fetchList, queryMyClassList } from '/@/api/stuwork/classattendance'
|
||||||
import { getDicts } from '/@/api/admin/dict'
|
import { getDicts } from '/@/api/admin/dict'
|
||||||
import { useMessage } from '/@/hooks/message'
|
import { useMessage } from '/@/hooks/message'
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import { List, CreditCard, Avatar, Phone, CircleCheck, Collection, House, Camera, Setting, Menu } from '@element-plus/icons-vue'
|
import { List, CreditCard, Avatar, Phone, CircleCheck, Collection, House, Camera, Setting, Menu, Search, Document, EditPen } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
import { defineAsyncComponent } from 'vue'
|
import { defineAsyncComponent } from 'vue'
|
||||||
const StatusTag = defineAsyncComponent(() => import('/@/components/StatusTag/index.vue'))
|
const StatusTag = defineAsyncComponent(() => import('/@/components/StatusTag/index.vue'))
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
|
||||||
const searchFormRef = ref()
|
const searchFormRef = ref()
|
||||||
const columnControlRef = ref()
|
const columnControlRef = ref()
|
||||||
const showSearch = ref(true)
|
const showSearch = ref(true)
|
||||||
@@ -175,94 +206,14 @@ const tableColumns = [
|
|||||||
{ prop: 'isDeviceIn', label: '是否扫脸', icon: Camera }
|
{ prop: 'isDeviceIn', label: '是否扫脸', icon: Camera }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制 Hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列变化处理
|
|
||||||
const handleColumnChange = (value: string[]) => {
|
|
||||||
visibleColumns.value = value
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = value.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列排序变化处理
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const visibleColumnsSorted = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: any[] = []
|
|
||||||
const unorderedColumns: any[] = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 搜索表单
|
// 搜索表单
|
||||||
const searchForm = reactive({
|
const searchForm = reactive({
|
||||||
@@ -294,12 +245,6 @@ const formatAttendanceType = (value: string) => {
|
|||||||
return item ? item.label : value
|
return item ? item.label : value
|
||||||
}
|
}
|
||||||
|
|
||||||
// 格式化是否
|
|
||||||
const formatYesNo = (value: string | null) => {
|
|
||||||
if (value === null || value === undefined || value === '') return '-'
|
|
||||||
return value === '1' || value === 'true' ? '是' : '否'
|
|
||||||
}
|
|
||||||
|
|
||||||
// 查询
|
// 查询
|
||||||
const handleSearch = () => {
|
const handleSearch = () => {
|
||||||
getDataList()
|
getDataList()
|
||||||
@@ -315,7 +260,6 @@ const handleReset = () => {
|
|||||||
|
|
||||||
// 编辑
|
// 编辑
|
||||||
const handleEdit = (row: any) => {
|
const handleEdit = (row: any) => {
|
||||||
// TODO: 实现编辑功能
|
|
||||||
useMessage().info('编辑功能待实现')
|
useMessage().info('编辑功能待实现')
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,8 +303,6 @@ const getClassListData = async () => {
|
|||||||
// 获取点名类型字典
|
// 获取点名类型字典
|
||||||
const getOrderTypeDict = async () => {
|
const getOrderTypeDict = async () => {
|
||||||
try {
|
try {
|
||||||
// 根据业务逻辑,点名类型可能是固定的几个值
|
|
||||||
// 这里先使用常见的值,如果不对可以根据实际情况调整
|
|
||||||
orderTypeList.value = [
|
orderTypeList.value = [
|
||||||
{ label: '早读', value: '1' },
|
{ label: '早读', value: '1' },
|
||||||
{ label: '上午', value: '2' },
|
{ label: '上午', value: '2' },
|
||||||
@@ -407,13 +349,14 @@ const getAttendanceTypeDict = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
loadSavedConfig()
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getClassListData()
|
getClassListData()
|
||||||
getOrderTypeDict()
|
getOrderTypeDict()
|
||||||
getStudentStatusDict()
|
getStudentStatusDict()
|
||||||
getAttendanceTypeDict()
|
getAttendanceTypeDict()
|
||||||
// 不自动加载数据,需要先选择班级和点名类型
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,9 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form
|
||||||
|
:model="searchForm"
|
||||||
|
ref="searchFormRef"
|
||||||
|
:inline="true"
|
||||||
|
@keyup.enter="handleSearch"
|
||||||
|
class="search-form">
|
||||||
<el-form-item label="学年" prop="schoolYear">
|
<el-form-item label="学年" prop="schoolYear">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="searchForm.schoolYear"
|
v-model="searchForm.schoolYear"
|
||||||
@@ -65,117 +78,136 @@
|
|||||||
style="width: 200px" />
|
style="width: 200px" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" plain icon="Search" @click="handleSearch">查询</el-button>
|
<el-button type="primary" icon="Search" @click="handleSearch">查询</el-button>
|
||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="FolderAdd"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
class="ml10"
|
日常巡检列表
|
||||||
@click="formDialogRef.openDialog()">
|
</span>
|
||||||
新 增
|
<div class="header-actions">
|
||||||
</el-button>
|
<el-button
|
||||||
<el-button
|
icon="FolderAdd"
|
||||||
icon="Download"
|
type="primary"
|
||||||
type="success"
|
@click="formDialogRef.openDialog()">
|
||||||
class="ml10"
|
新增
|
||||||
@click="handleExport">
|
</el-button>
|
||||||
导 出
|
<el-button
|
||||||
</el-button>
|
icon="Download"
|
||||||
<el-button
|
type="success"
|
||||||
icon="DataAnalysis"
|
class="ml10"
|
||||||
type="info"
|
@click="handleExport">
|
||||||
class="ml10"
|
导出
|
||||||
@click="handleRank">
|
</el-button>
|
||||||
学期统计
|
<el-button
|
||||||
</el-button>
|
icon="DataAnalysis"
|
||||||
<right-toolbar
|
type="info"
|
||||||
v-model:showSearch="showSearch"
|
class="ml10"
|
||||||
class="ml10 mr20"
|
@click="handleRank">
|
||||||
style="float: right;"
|
学期统计
|
||||||
@queryTable="getDataList">
|
</el-button>
|
||||||
<TableColumnControl
|
<right-toolbar
|
||||||
ref="columnControlRef"
|
v-model:showSearch="showSearch"
|
||||||
:columns="tableColumns"
|
class="ml10"
|
||||||
v-model="visibleColumns"
|
@queryTable="getDataList">
|
||||||
trigger-type="default"
|
<TableColumnControl
|
||||||
trigger-circle
|
ref="columnControlRef"
|
||||||
@change="handleColumnChange"
|
:columns="tableColumns"
|
||||||
@order-change="handleColumnOrderChange"
|
v-model="visibleColumns"
|
||||||
>
|
trigger-type="default"
|
||||||
<template #trigger>
|
trigger-circle
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
@change="handleColumnChange"
|
||||||
<el-button circle style="margin-left: 0;">
|
@order-change="handleColumnOrderChange"
|
||||||
<el-icon><Menu /></el-icon>
|
>
|
||||||
</el-button>
|
<template #trigger>
|
||||||
</el-tooltip>
|
<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"
|
||||||
|
@sort-change="sortChangeHandle">
|
||||||
|
<el-table-column type="index" label="序号" 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 !== '操作'"
|
||||||
|
: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>
|
||||||
</TableColumnControl>
|
<!-- 分数列特殊模板 -->
|
||||||
</right-toolbar>
|
<template v-if="col.prop === 'score'" #default="scope">
|
||||||
|
<el-tag v-if="scope.row.score !== undefined && scope.row.score !== null" size="small" :type="scope.row.score >= 0 ? 'success' : 'danger'" effect="plain" round>
|
||||||
|
{{ scope.row.score }}
|
||||||
|
</el-tag>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column label="操作" width="100" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
icon="Delete"
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
@click="handleDelete([scope.row.id])">
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<template #empty>
|
||||||
|
<el-empty description="暂无数据" :image-size="120">
|
||||||
|
<el-button type="primary" icon="FolderAdd" @click="formDialogRef.openDialog()">新增记录</el-button>
|
||||||
|
</el-empty>
|
||||||
|
</template>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<div class="pagination-wrapper">
|
||||||
|
<pagination
|
||||||
|
@size-change="sizeChangeHandle"
|
||||||
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
</div>
|
</div>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 表格 -->
|
|
||||||
<el-table
|
|
||||||
:data="state.dataList"
|
|
||||||
v-loading="state.loading"
|
|
||||||
border
|
|
||||||
:cell-style="tableStyle.cellStyle"
|
|
||||||
:header-cell-style="tableStyle.headerCellStyle"
|
|
||||||
@sort-change="sortChangeHandle">
|
|
||||||
<el-table-column type="index" label="序号" align="center">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
v-for="col in visibleColumnsSorted"
|
|
||||||
:key="col.prop"
|
|
||||||
:prop="col.prop"
|
|
||||||
:label="col.label"
|
|
||||||
:width="col.width"
|
|
||||||
:min-width="col.minWidth"
|
|
||||||
:show-overflow-tooltip="col.showOverflowTooltip !== false"
|
|
||||||
:align="col.align">
|
|
||||||
<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 === 'score'">
|
|
||||||
<el-tag v-if="scope.row.score !== undefined && scope.row.score !== null" size="small" :type="scope.row.score >= 0 ? 'success' : 'danger'" effect="plain">
|
|
||||||
{{ scope.row.score }}
|
|
||||||
</el-tag>
|
|
||||||
<span v-else>-</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="操作" align="center" fixed="right">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><Setting /></el-icon>
|
|
||||||
<span style="margin-left: 4px">操作</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
icon="Delete"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete([scope.row.id])">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
|
||||||
<pagination
|
|
||||||
@size-change="sizeChangeHandle"
|
|
||||||
@current-change="currentChangeHandle"
|
|
||||||
v-bind="state.pagination" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 编辑、新增 -->
|
<!-- 编辑、新增 -->
|
||||||
@@ -237,27 +269,23 @@
|
|||||||
|
|
||||||
<script setup lang="ts" name="ClassCheckDaily">
|
<script setup lang="ts" name="ClassCheckDaily">
|
||||||
import { ref, reactive, defineAsyncComponent, computed, onMounted } from 'vue'
|
import { ref, reactive, defineAsyncComponent, computed, onMounted } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||||
import { fetchList, delObjs, exportData, getRank } from "/@/api/stuwork/classcheckdaily";
|
import { fetchList, delObjs, exportData, getRank } from "/@/api/stuwork/classcheckdaily";
|
||||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||||
import { getDeptList } from '/@/api/basic/basicclass'
|
import { getDeptList } from '/@/api/basic/basicclass'
|
||||||
import { getClassListByRole } from '/@/api/basic/basicclass'
|
import { getClassListByRole } from '/@/api/basic/basicclass'
|
||||||
import { queryAllSchoolYear } from '/@/api/basic/basicyear'
|
import { queryAllSchoolYear } from '/@/api/basic/basicyear'
|
||||||
import request from "/@/utils/request";
|
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
|
import { List, OfficeBuilding, Grid, Avatar, Calendar, DataAnalysis, Document, CircleCheck, Setting, Menu, Search, Document as DocIcon, FolderAdd, Download } from '@element-plus/icons-vue'
|
||||||
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
|
||||||
// 引入组件
|
// 引入组件
|
||||||
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
||||||
import { List, OfficeBuilding, Grid, Avatar, Calendar, DataAnalysis, Document, CircleCheck, Setting, Menu } from '@element-plus/icons-vue'
|
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
|
||||||
const formDialogRef = ref()
|
const formDialogRef = ref()
|
||||||
const searchFormRef = ref()
|
const searchFormRef = ref()
|
||||||
const columnControlRef = ref()
|
const columnControlRef = ref()
|
||||||
// 搜索变量
|
|
||||||
const showSearch = ref(true)
|
const showSearch = ref(true)
|
||||||
const deptList = ref<any[]>([])
|
const deptList = ref<any[]>([])
|
||||||
const classList = ref<any[]>([])
|
const classList = ref<any[]>([])
|
||||||
@@ -274,94 +302,14 @@ const tableColumns = [
|
|||||||
{ prop: 'handleResult', label: '处理结果', icon: CircleCheck }
|
{ prop: 'handleResult', label: '处理结果', icon: CircleCheck }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制 Hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列变化处理
|
|
||||||
const handleColumnChange = (value: string[]) => {
|
|
||||||
visibleColumns.value = value
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = value.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列排序变化处理
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const visibleColumnsSorted = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: any[] = []
|
|
||||||
const unorderedColumns: any[] = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 搜索表单
|
// 搜索表单
|
||||||
const searchForm = reactive({
|
const searchForm = reactive({
|
||||||
@@ -411,7 +359,6 @@ const rankForm = reactive({
|
|||||||
|
|
||||||
// 学院选择变化
|
// 学院选择变化
|
||||||
const handleDeptChange = () => {
|
const handleDeptChange = () => {
|
||||||
// 清空班号选择
|
|
||||||
searchForm.classCode = ''
|
searchForm.classCode = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -422,7 +369,7 @@ const handleSearch = () => {
|
|||||||
|
|
||||||
// 重置
|
// 重置
|
||||||
const handleReset = () => {
|
const handleReset = () => {
|
||||||
searchFormRef.value?.formRef?.resetFields()
|
searchFormRef.value?.resetFields()
|
||||||
searchForm.schoolYear = ''
|
searchForm.schoolYear = ''
|
||||||
searchForm.schoolTerm = ''
|
searchForm.schoolTerm = ''
|
||||||
searchForm.deptCode = ''
|
searchForm.deptCode = ''
|
||||||
@@ -456,7 +403,6 @@ const handleDelete = async (ids: string[]) => {
|
|||||||
const handleExport = async () => {
|
const handleExport = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await exportData(searchForm)
|
const res = await exportData(searchForm)
|
||||||
// 处理返回的文件流
|
|
||||||
const blob = new Blob([res.data])
|
const blob = new Blob([res.data])
|
||||||
const elink = document.createElement('a')
|
const elink = document.createElement('a')
|
||||||
elink.download = '日常巡检.xlsx'
|
elink.download = '日常巡检.xlsx'
|
||||||
@@ -543,10 +489,13 @@ const getSchoolYearList = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
loadSavedConfig()
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getDeptListData()
|
getDeptListData()
|
||||||
getClassListData()
|
getClassListData()
|
||||||
getSchoolYearList()
|
getSchoolYearList()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,9 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="state.queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form
|
||||||
|
:model="state.queryForm"
|
||||||
|
ref="searchFormRef"
|
||||||
|
:inline="true"
|
||||||
|
@keyup.enter="getDataList"
|
||||||
|
class="search-form">
|
||||||
<el-form-item label="学院" prop="deptCode">
|
<el-form-item label="学院" prop="deptCode">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="state.queryForm.deptCode"
|
v-model="state.queryForm.deptCode"
|
||||||
@@ -43,120 +56,139 @@
|
|||||||
style="width: 200px" />
|
style="width: 200px" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" plain icon="Search" @click="getDataList">查询</el-button>
|
<el-button type="primary" icon="Search" @click="getDataList">查询</el-button>
|
||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="Plus"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
class="ml10"
|
班级建设方案列表
|
||||||
@click="formDialogRef.openDialog()">
|
</span>
|
||||||
新增
|
<div class="header-actions">
|
||||||
</el-button>
|
<el-button
|
||||||
<el-button
|
icon="Plus"
|
||||||
icon="RefreshLeft"
|
type="primary"
|
||||||
type="warning"
|
@click="formDialogRef.openDialog()">
|
||||||
class="ml10"
|
新增
|
||||||
@click="handleInit">
|
</el-button>
|
||||||
初始化
|
<el-button
|
||||||
</el-button>
|
icon="RefreshLeft"
|
||||||
<right-toolbar
|
type="warning"
|
||||||
v-model:showSearch="showSearch"
|
class="ml10"
|
||||||
class="ml10 mr20"
|
@click="handleInit">
|
||||||
style="float: right;"
|
初始化
|
||||||
@queryTable="getDataList">
|
</el-button>
|
||||||
<TableColumnControl
|
<right-toolbar
|
||||||
ref="columnControlRef"
|
v-model:showSearch="showSearch"
|
||||||
:columns="tableColumns"
|
class="ml10"
|
||||||
v-model="visibleColumns"
|
@queryTable="getDataList">
|
||||||
trigger-type="default"
|
<TableColumnControl
|
||||||
trigger-circle
|
ref="columnControlRef"
|
||||||
@change="handleColumnChange"
|
:columns="tableColumns"
|
||||||
@order-change="handleColumnOrderChange"
|
v-model="visibleColumns"
|
||||||
>
|
trigger-type="default"
|
||||||
<template #trigger>
|
trigger-circle
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
@change="handleColumnChange"
|
||||||
<el-button circle style="margin-left: 0;">
|
@order-change="handleColumnOrderChange"
|
||||||
<el-icon><Menu /></el-icon>
|
>
|
||||||
</el-button>
|
<template #trigger>
|
||||||
</el-tooltip>
|
<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="序号" 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 !== '操作'"
|
||||||
|
: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>
|
||||||
</TableColumnControl>
|
<!-- 创建时间列特殊模板 -->
|
||||||
</right-toolbar>
|
<template v-if="col.prop === 'createTime'" #default="scope">
|
||||||
|
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column label="操作" width="250" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
icon="EditPen"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="handleEdit(scope.row)">
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="Document"
|
||||||
|
link
|
||||||
|
type="info"
|
||||||
|
@click="handleFileList(scope.row)">
|
||||||
|
附件列表
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="Delete"
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
@click="handleDelete(scope.row)">
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<template #empty>
|
||||||
|
<el-empty description="暂无数据" :image-size="120">
|
||||||
|
<el-button type="primary" icon="FolderAdd" @click="formDialogRef.openDialog()">新增方案</el-button>
|
||||||
|
</el-empty>
|
||||||
|
</template>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<div class="pagination-wrapper">
|
||||||
|
<pagination
|
||||||
|
@size-change="sizeChangeHandle"
|
||||||
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
</div>
|
</div>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 表格 -->
|
|
||||||
<el-table
|
|
||||||
:data="state.dataList"
|
|
||||||
v-loading="state.loading"
|
|
||||||
border
|
|
||||||
:cell-style="tableStyle.cellStyle"
|
|
||||||
:header-cell-style="tableStyle.headerCellStyle">
|
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
v-for="col in visibleColumnsSorted"
|
|
||||||
:key="col.prop"
|
|
||||||
: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 === 'createTime'">
|
|
||||||
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="操作" width="250" align="center" fixed="right">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><Setting /></el-icon>
|
|
||||||
<span style="margin-left: 4px">操作</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
icon="Edit"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleEdit(scope.row)">
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Document"
|
|
||||||
text
|
|
||||||
type="info"
|
|
||||||
@click="handleFileList(scope.row)">
|
|
||||||
附件列表
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Delete"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row)">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
|
||||||
<pagination
|
|
||||||
@size-change="sizeChangeHandle"
|
|
||||||
@current-change="currentChangeHandle"
|
|
||||||
v-bind="state.pagination" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 新增/编辑表单弹窗 -->
|
<!-- 新增/编辑表单弹窗 -->
|
||||||
@@ -168,8 +200,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="ClassConstruction">
|
<script setup lang="ts" name="ClassConstruction">
|
||||||
import { reactive, ref, onMounted, computed } from 'vue'
|
import { reactive, ref, onMounted } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||||
import { fetchList, delObj, initObj } from "/@/api/stuwork/classconstruction";
|
import { fetchList, delObj, initObj } from "/@/api/stuwork/classconstruction";
|
||||||
import { getDeptList } from "/@/api/basic/basicclass";
|
import { getDeptList } from "/@/api/basic/basicclass";
|
||||||
@@ -179,11 +210,10 @@ import { parseTime } from "/@/utils/formatTime"
|
|||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import FormDialog from './form.vue'
|
import FormDialog from './form.vue'
|
||||||
import FileListDialog from './fileList.vue'
|
import FileListDialog from './fileList.vue'
|
||||||
import { List, OfficeBuilding, Grid, Document, Calendar, EditPen, Setting, Menu } from '@element-plus/icons-vue'
|
import { List, OfficeBuilding, Grid, Document, Calendar, EditPen, Setting, Menu, Search, FolderAdd, RefreshLeft, EditPen as EditIcon, Document as DocIcon } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
|
||||||
const formDialogRef = ref()
|
const formDialogRef = ref()
|
||||||
const fileListDialogRef = ref()
|
const fileListDialogRef = ref()
|
||||||
const searchFormRef = ref()
|
const searchFormRef = ref()
|
||||||
@@ -201,94 +231,14 @@ const tableColumns = [
|
|||||||
{ prop: 'remarks', label: '备注', icon: EditPen, minWidth: 200 }
|
{ prop: 'remarks', label: '备注', icon: EditPen, minWidth: 200 }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制 Hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列变化处理
|
|
||||||
const handleColumnChange = (value: string[]) => {
|
|
||||||
visibleColumns.value = value
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = value.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列排序变化处理
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const visibleColumnsSorted = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: any[] = []
|
|
||||||
const unorderedColumns: any[] = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 配置 useTable
|
// 配置 useTable
|
||||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||||
@@ -402,11 +352,11 @@ const getDeptListData = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
loadSavedConfig()
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getDeptListData()
|
getDeptListData()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,9 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="state.queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</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-form-item label="学年" prop="schoolYear">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="state.queryForm.schoolYear"
|
v-model="state.queryForm.schoolYear"
|
||||||
@@ -79,141 +92,164 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" plain icon="Search" @click="getDataList">查询</el-button>
|
<el-button type="primary" icon="Search" @click="getDataList">查询</el-button>
|
||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="Plus"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
class="ml10"
|
班费记录列表
|
||||||
@click="formDialogRef.openDialog()">
|
</span>
|
||||||
新增
|
<div class="header-actions">
|
||||||
</el-button>
|
<el-button
|
||||||
<el-button
|
icon="Plus"
|
||||||
icon="Download"
|
type="primary"
|
||||||
type="primary"
|
@click="formDialogRef.openDialog()">
|
||||||
class="ml10"
|
新增
|
||||||
@click="handleExport">
|
</el-button>
|
||||||
导出
|
<el-button
|
||||||
</el-button>
|
icon="Download"
|
||||||
<right-toolbar
|
type="success"
|
||||||
v-model:showSearch="showSearch"
|
class="ml10"
|
||||||
class="ml10 mr20"
|
@click="handleExport">
|
||||||
style="float: right;"
|
导出
|
||||||
@queryTable="getDataList">
|
</el-button>
|
||||||
<TableColumnControl
|
<right-toolbar
|
||||||
ref="columnControlRef"
|
v-model:showSearch="showSearch"
|
||||||
:columns="tableColumns"
|
class="ml10"
|
||||||
v-model="visibleColumns"
|
@queryTable="getDataList">
|
||||||
trigger-type="default"
|
<TableColumnControl
|
||||||
trigger-circle
|
ref="columnControlRef"
|
||||||
@change="handleColumnChange"
|
:columns="tableColumns"
|
||||||
@order-change="handleColumnOrderChange"
|
v-model="visibleColumns"
|
||||||
>
|
trigger-type="default"
|
||||||
<template #trigger>
|
trigger-circle
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
@change="handleColumnChange"
|
||||||
<el-button circle style="margin-left: 0;">
|
@order-change="handleColumnOrderChange"
|
||||||
<el-icon><Menu /></el-icon>
|
>
|
||||||
</el-button>
|
<template #trigger>
|
||||||
</el-tooltip>
|
<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="序号" 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 !== '操作'"
|
||||||
|
: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>
|
||||||
</TableColumnControl>
|
<!-- 学期列特殊模板 -->
|
||||||
</right-toolbar>
|
<template v-if="col.prop === 'schoolTerm'" #default="scope">
|
||||||
|
<el-tag size="small" type="primary" effect="plain" round>
|
||||||
|
{{ formatSchoolTerm(scope.row.schoolTerm) }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
<!-- 发生时间列特殊模板 -->
|
||||||
|
<template v-else-if="col.prop === 'operatTime'" #default="scope">
|
||||||
|
<span>{{ scope.row.operatTime || '-' }}</span>
|
||||||
|
</template>
|
||||||
|
<!-- 类型列特殊模板 -->
|
||||||
|
<template v-else-if="col.prop === 'type'" #default="scope">
|
||||||
|
<el-tag size="small" type="info" effect="plain" round>
|
||||||
|
{{ formatType(scope.row.type) }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
<!-- 金额列特殊模板 -->
|
||||||
|
<template v-else-if="col.prop === 'money'" #default="scope">
|
||||||
|
<el-tag v-if="scope.row.money !== null && scope.row.money !== undefined" size="small" type="success" effect="plain" round>
|
||||||
|
¥{{ scope.row.money.toFixed(2) }}
|
||||||
|
</el-tag>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
<!-- 附件列特殊模板 -->
|
||||||
|
<template v-else-if="col.prop === 'attachment'" #default="scope">
|
||||||
|
<el-button
|
||||||
|
v-if="scope.row.attachment"
|
||||||
|
icon="Document"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
@click="handleViewAttachment(scope.row)">
|
||||||
|
查看
|
||||||
|
</el-button>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
icon="EditPen"
|
||||||
|
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>
|
||||||
|
<template #empty>
|
||||||
|
<el-empty description="暂无数据" :image-size="120">
|
||||||
|
<el-button type="primary" icon="FolderAdd" @click="formDialogRef.openDialog()">新增记录</el-button>
|
||||||
|
</el-empty>
|
||||||
|
</template>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<div class="pagination-wrapper">
|
||||||
|
<pagination
|
||||||
|
@size-change="sizeChangeHandle"
|
||||||
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
</div>
|
</div>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 表格 -->
|
|
||||||
<el-table
|
|
||||||
:data="state.dataList"
|
|
||||||
v-loading="state.loading"
|
|
||||||
border
|
|
||||||
:cell-style="tableStyle.cellStyle"
|
|
||||||
:header-cell-style="tableStyle.headerCellStyle">
|
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
v-for="col in visibleColumnsSorted"
|
|
||||||
:key="col.prop"
|
|
||||||
: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 === 'operatTime'">
|
|
||||||
<span>{{ scope.row.operatTime || '-' }}</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope" v-else-if="col.prop === 'type'">
|
|
||||||
<el-tag size="small" type="info" effect="plain">
|
|
||||||
{{ formatType(scope.row.type) }}
|
|
||||||
</el-tag>
|
|
||||||
</template>
|
|
||||||
<template #default="scope" v-else-if="col.prop === 'money'">
|
|
||||||
<el-tag v-if="scope.row.money !== null && scope.row.money !== undefined" size="small" type="success" effect="plain">
|
|
||||||
¥{{ scope.row.money.toFixed(2) }}
|
|
||||||
</el-tag>
|
|
||||||
<span v-else>-</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope" v-else-if="col.prop === 'attachment'">
|
|
||||||
<el-button
|
|
||||||
v-if="scope.row.attachment"
|
|
||||||
icon="Document"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
size="small"
|
|
||||||
@click="handleViewAttachment(scope.row)">
|
|
||||||
查看
|
|
||||||
</el-button>
|
|
||||||
<span v-else>-</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><Setting /></el-icon>
|
|
||||||
<span style="margin-left: 4px">操作</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
icon="Edit"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleEdit(scope.row)">
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Delete"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row)">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
|
||||||
<pagination
|
|
||||||
@size-change="sizeChangeHandle"
|
|
||||||
@current-change="currentChangeHandle"
|
|
||||||
v-bind="state.pagination" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 新增/编辑表单弹窗 -->
|
<!-- 新增/编辑表单弹窗 -->
|
||||||
@@ -222,8 +258,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="ClassFeeLog">
|
<script setup lang="ts" name="ClassFeeLog">
|
||||||
import { reactive, ref, onMounted, computed } from 'vue'
|
import { reactive, ref, onMounted } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||||
import { fetchList, delObj, exportExcel } from "/@/api/stuwork/classfeelog";
|
import { fetchList, delObj, exportExcel } from "/@/api/stuwork/classfeelog";
|
||||||
import { getDeptList } from "/@/api/basic/basicclass";
|
import { getDeptList } from "/@/api/basic/basicclass";
|
||||||
@@ -233,11 +268,10 @@ import { getDicts } from "/@/api/admin/dict";
|
|||||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import FormDialog from './form.vue'
|
import FormDialog from './form.vue'
|
||||||
import { List, Calendar, Clock, OfficeBuilding, Grid, Collection, Money, User, Document, Setting, Menu } from '@element-plus/icons-vue'
|
import { List, Calendar, Clock, OfficeBuilding, Grid, Collection, Money, User, Document, Setting, Menu, Search, FolderAdd, EditPen } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
|
||||||
const formDialogRef = ref()
|
const formDialogRef = ref()
|
||||||
const searchFormRef = ref()
|
const searchFormRef = ref()
|
||||||
const columnControlRef = ref()
|
const columnControlRef = ref()
|
||||||
@@ -262,94 +296,14 @@ const tableColumns = [
|
|||||||
{ prop: 'attachment', label: '附件', width: 100 }
|
{ prop: 'attachment', label: '附件', width: 100 }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制 Hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列变化处理
|
|
||||||
const handleColumnChange = (value: string[]) => {
|
|
||||||
visibleColumns.value = value
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = value.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列排序变化处理
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const visibleColumnsSorted = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: any[] = []
|
|
||||||
const unorderedColumns: any[] = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 配置 useTable
|
// 配置 useTable
|
||||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||||
@@ -424,29 +378,18 @@ const handleViewAttachment = (row: any) => {
|
|||||||
const handleExport = async () => {
|
const handleExport = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await exportExcel(state.queryForm)
|
const res = await exportExcel(state.queryForm)
|
||||||
|
|
||||||
// 创建blob对象
|
|
||||||
const blob = new Blob([res], {
|
const blob = new Blob([res], {
|
||||||
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||||
})
|
})
|
||||||
|
|
||||||
// 创建下载链接
|
|
||||||
const url = window.URL.createObjectURL(blob)
|
const url = window.URL.createObjectURL(blob)
|
||||||
const link = document.createElement('a')
|
const link = document.createElement('a')
|
||||||
link.href = url
|
link.href = url
|
||||||
|
|
||||||
// 设置文件名
|
|
||||||
const fileName = `班费记录_${new Date().getTime()}.xlsx`
|
const fileName = `班费记录_${new Date().getTime()}.xlsx`
|
||||||
link.setAttribute('download', fileName)
|
link.setAttribute('download', fileName)
|
||||||
|
|
||||||
// 触发下载
|
|
||||||
document.body.appendChild(link)
|
document.body.appendChild(link)
|
||||||
link.click()
|
link.click()
|
||||||
|
|
||||||
// 清理
|
|
||||||
document.body.removeChild(link)
|
document.body.removeChild(link)
|
||||||
window.URL.revokeObjectURL(url)
|
window.URL.revokeObjectURL(url)
|
||||||
|
|
||||||
useMessage().success('导出成功')
|
useMessage().success('导出成功')
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
useMessage().error(err.msg || '导出失败')
|
useMessage().error(err.msg || '导出失败')
|
||||||
@@ -550,7 +493,6 @@ const getTypeDict = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
loadSavedConfig()
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getSchoolYearList()
|
getSchoolYearList()
|
||||||
getSchoolTermDict()
|
getSchoolTermDict()
|
||||||
@@ -561,5 +503,5 @@ onMounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="state.queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</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-form-item label="学年" prop="schoolYear">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="state.queryForm.schoolYear"
|
v-model="state.queryForm.schoolYear"
|
||||||
@@ -65,140 +78,157 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" plain icon="Search" @click="getDataList">查询</el-button>
|
<el-button type="primary" icon="Search" @click="getDataList">查询</el-button>
|
||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="Plus"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
class="ml10"
|
班级荣誉列表
|
||||||
@click="formDialogRef.openDialog()">
|
</span>
|
||||||
新增
|
<div class="header-actions">
|
||||||
</el-button>
|
|
||||||
<right-toolbar
|
|
||||||
v-model:showSearch="showSearch"
|
|
||||||
class="ml10"
|
|
||||||
style="float: right;"
|
|
||||||
@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>
|
|
||||||
</el-row>
|
|
||||||
|
|
||||||
<!-- 表格 -->
|
|
||||||
<el-table
|
|
||||||
:data="state.dataList"
|
|
||||||
v-loading="state.loading"
|
|
||||||
border
|
|
||||||
:cell-style="tableStyle.cellStyle"
|
|
||||||
:header-cell-style="tableStyle.headerCellStyle">
|
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<template v-for="col in sortedTableColumns" :key="col.prop || col.label">
|
|
||||||
<el-table-column
|
|
||||||
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
|
||||||
:prop="col.prop"
|
|
||||||
:label="col.label"
|
|
||||||
show-overflow-tooltip
|
|
||||||
align="center"
|
|
||||||
:min-width="col.minWidth"
|
|
||||||
:width="col.width">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
|
||||||
<span style="margin-left: 4px">{{ col.label }}</span>
|
|
||||||
</template>
|
|
||||||
<!-- 学期列特殊模板 -->
|
|
||||||
<template v-if="col.prop === 'schoolTerm'" #default="scope">
|
|
||||||
<el-tag size="small" type="primary" effect="plain">
|
|
||||||
{{ formatSchoolTerm(scope.row.schoolTerm) }}
|
|
||||||
</el-tag>
|
|
||||||
</template>
|
|
||||||
<!-- 更新时间列特殊模板 -->
|
|
||||||
<template v-else-if="col.prop === 'updateTime'" #default="scope">
|
|
||||||
<span>{{ parseTime(scope.row.updateTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
|
||||||
</template>
|
|
||||||
<!-- 归档级别列特殊模板 -->
|
|
||||||
<template v-else-if="col.prop === 'belong'" #default="scope">
|
|
||||||
<el-tag size="small" type="info" effect="plain">
|
|
||||||
{{ formatBelong(scope.row.belong) }}
|
|
||||||
</el-tag>
|
|
||||||
</template>
|
|
||||||
<!-- 附件列特殊模板 -->
|
|
||||||
<template v-else-if="col.prop === 'attachment'" #default="scope">
|
|
||||||
<el-button
|
<el-button
|
||||||
v-if="scope.row.attachment"
|
icon="Plus"
|
||||||
icon="Document"
|
|
||||||
text
|
|
||||||
type="primary"
|
type="primary"
|
||||||
size="small"
|
@click="formDialogRef.openDialog()">
|
||||||
@click="handleViewAttachment(scope.row)">
|
新增
|
||||||
查看
|
|
||||||
</el-button>
|
</el-button>
|
||||||
<span v-else>-</span>
|
<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="序号" 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>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</template>
|
<template v-for="col in visibleColumnsSorted" :key="col.prop || col.label">
|
||||||
<el-table-column label="操作" width="250" align="center" fixed="right">
|
<el-table-column
|
||||||
<template #header>
|
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
||||||
<el-icon><Setting /></el-icon>
|
:prop="col.prop"
|
||||||
<span style="margin-left: 4px">操作</span>
|
: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 v-if="col.prop === 'schoolTerm'" #default="scope">
|
||||||
|
<el-tag size="small" type="primary" effect="plain" round>
|
||||||
|
{{ formatSchoolTerm(scope.row.schoolTerm) }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
<!-- 更新时间列特殊模板 -->
|
||||||
|
<template v-else-if="col.prop === 'updateTime'" #default="scope">
|
||||||
|
<span>{{ parseTime(scope.row.updateTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
||||||
|
</template>
|
||||||
|
<!-- 归档级别列特殊模板 -->
|
||||||
|
<template v-else-if="col.prop === 'belong'" #default="scope">
|
||||||
|
<el-tag size="small" type="info" effect="plain" round>
|
||||||
|
{{ formatBelong(scope.row.belong) }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
<!-- 附件列特殊模板 -->
|
||||||
|
<template v-else-if="col.prop === 'attachment'" #default="scope">
|
||||||
|
<el-button
|
||||||
|
v-if="scope.row.attachment"
|
||||||
|
icon="Document"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
@click="handleViewAttachment(scope.row)">
|
||||||
|
查看
|
||||||
|
</el-button>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
</template>
|
</template>
|
||||||
<template #default="scope">
|
<el-table-column label="操作" width="250" align="center" fixed="right">
|
||||||
<el-button
|
<template #header>
|
||||||
icon="Folder"
|
<el-icon><Setting /></el-icon>
|
||||||
text
|
<span style="margin-left: 4px">操作</span>
|
||||||
type="warning"
|
</template>
|
||||||
@click="handleBelong(scope.row)">
|
<template #default="scope">
|
||||||
归档
|
<el-button
|
||||||
</el-button>
|
icon="Folder"
|
||||||
<el-button
|
link
|
||||||
icon="Edit"
|
type="warning"
|
||||||
text
|
@click="handleBelong(scope.row)">
|
||||||
type="primary"
|
归档
|
||||||
@click="handleEdit(scope.row)">
|
</el-button>
|
||||||
编辑
|
<el-button
|
||||||
</el-button>
|
icon="EditPen"
|
||||||
<el-button
|
link
|
||||||
icon="Delete"
|
type="primary"
|
||||||
text
|
@click="handleEdit(scope.row)">
|
||||||
type="danger"
|
编辑
|
||||||
@click="handleDelete(scope.row)">
|
</el-button>
|
||||||
删除
|
<el-button
|
||||||
</el-button>
|
icon="Delete"
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
@click="handleDelete(scope.row)">
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<template #empty>
|
||||||
|
<el-empty description="暂无数据" :image-size="120">
|
||||||
|
<el-button type="primary" icon="FolderAdd" @click="formDialogRef.openDialog()">新增荣誉</el-button>
|
||||||
|
</el-empty>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table>
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<pagination
|
<div class="pagination-wrapper">
|
||||||
@size-change="sizeChangeHandle"
|
<pagination
|
||||||
@current-change="currentChangeHandle"
|
@size-change="sizeChangeHandle"
|
||||||
v-bind="state.pagination" />
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 新增/编辑表单弹窗 -->
|
<!-- 新增/编辑表单弹窗 -->
|
||||||
@@ -210,8 +240,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="ClassHonor">
|
<script setup lang="ts" name="ClassHonor">
|
||||||
import { reactive, ref, onMounted, computed, nextTick } from 'vue'
|
import { reactive, ref, onMounted } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||||
import { fetchList, delObj } from "/@/api/stuwork/classhonor";
|
import { fetchList, delObj } from "/@/api/stuwork/classhonor";
|
||||||
import { getDeptList } from "/@/api/basic/basicclass";
|
import { getDeptList } from "/@/api/basic/basicclass";
|
||||||
@@ -223,15 +252,14 @@ import { parseTime } from "/@/utils/formatTime";
|
|||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import FormDialog from './form.vue'
|
import FormDialog from './form.vue'
|
||||||
import BelongDialog from './belong.vue'
|
import BelongDialog from './belong.vue'
|
||||||
import { List, Calendar, Clock, OfficeBuilding, Grid, Document, User, Collection, Setting, Menu } from '@element-plus/icons-vue'
|
import { List, Calendar, Clock, OfficeBuilding, Grid, Document, User, Collection, Setting, Menu, Search, FolderAdd, Folder, EditPen } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
|
||||||
const formDialogRef = ref()
|
const formDialogRef = ref()
|
||||||
const columnControlRef = ref<any>()
|
|
||||||
const belongDialogRef = ref()
|
const belongDialogRef = ref()
|
||||||
const searchFormRef = ref()
|
const searchFormRef = ref()
|
||||||
|
const columnControlRef = ref()
|
||||||
const showSearch = ref(true)
|
const showSearch = ref(true)
|
||||||
const schoolYearList = ref<any[]>([])
|
const schoolYearList = ref<any[]>([])
|
||||||
const schoolTermList = ref<any[]>([])
|
const schoolTermList = ref<any[]>([])
|
||||||
@@ -241,128 +269,25 @@ const belongList = ref<any[]>([])
|
|||||||
|
|
||||||
// 表格列配置
|
// 表格列配置
|
||||||
const tableColumns = [
|
const tableColumns = [
|
||||||
{ prop: 'schoolYear', label: '学年' },
|
{ prop: 'schoolYear', label: '学年', icon: Calendar },
|
||||||
{ prop: 'schoolTerm', label: '学期' },
|
{ prop: 'schoolTerm', label: '学期', icon: Clock },
|
||||||
{ prop: 'deptName', label: '学院' },
|
{ prop: 'deptName', label: '学院', icon: OfficeBuilding },
|
||||||
{ prop: 'classNo', label: '班号' },
|
{ prop: 'classNo', label: '班号', icon: Grid },
|
||||||
{ prop: 'title', label: '标题', minWidth: 200 },
|
{ prop: 'title', label: '标题', icon: Document, minWidth: 200 },
|
||||||
{ prop: 'author', label: '作者' },
|
{ prop: 'author', label: '作者', icon: User },
|
||||||
{ prop: 'updateTime', label: '更新时间', width: 180 },
|
{ prop: 'updateTime', label: '更新时间', icon: Clock, width: 180 },
|
||||||
{ prop: 'belong', label: '归档级别' },
|
{ prop: 'belong', label: '归档级别', icon: Collection },
|
||||||
{ prop: 'attachment', label: '附件', width: 100 }
|
{ prop: 'attachment', label: '附件', icon: Document, width: 100 }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 列配置映射(用于图标)
|
// 使用表格列控制 Hook
|
||||||
const columnConfigMap: Record<string, { icon: any }> = {
|
const {
|
||||||
schoolYear: { icon: Calendar },
|
visibleColumns,
|
||||||
schoolTerm: { icon: Clock },
|
visibleColumnsSorted,
|
||||||
deptName: { icon: OfficeBuilding },
|
checkColumnVisible,
|
||||||
classNo: { icon: Grid },
|
handleColumnChange,
|
||||||
title: { icon: Document },
|
handleColumnOrderChange
|
||||||
author: { icon: User },
|
} = useTableColumnControl(tableColumns)
|
||||||
updateTime: { icon: Clock },
|
|
||||||
belong: { icon: Collection },
|
|
||||||
attachment: { icon: Document }
|
|
||||||
}
|
|
||||||
|
|
||||||
// 当前显示的列
|
|
||||||
const visibleColumns = ref<string[]>([])
|
|
||||||
// 列排序顺序
|
|
||||||
const columnOrder = ref<string[]>([])
|
|
||||||
|
|
||||||
// 从本地统一存储加载配置
|
|
||||||
const loadSavedConfig = () => {
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadSavedConfig()
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const sortedTableColumns = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: typeof tableColumns = []
|
|
||||||
const unorderedColumns: typeof tableColumns = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 列显示控制函数
|
|
||||||
const checkColumnVisible = (prop: string): boolean => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return visibleColumns.value.includes(prop)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列变化
|
|
||||||
const handleColumnChange = (columns: string[]) => {
|
|
||||||
visibleColumns.value = columns
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = columns.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列排序变化
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 配置 useTable
|
// 配置 useTable
|
||||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||||
@@ -539,14 +464,9 @@ onMounted(() => {
|
|||||||
getDeptListData()
|
getDeptListData()
|
||||||
getClassListData()
|
getClassListData()
|
||||||
getBelongDict()
|
getBelongDict()
|
||||||
nextTick(() => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
loadSavedConfig()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form
|
||||||
|
:model="searchForm"
|
||||||
|
ref="searchFormRef"
|
||||||
|
:inline="true"
|
||||||
|
@keyup.enter="handleSearch"
|
||||||
|
class="search-form">
|
||||||
<el-form-item label="学院" prop="deptCode">
|
<el-form-item label="学院" prop="deptCode">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="searchForm.deptCode"
|
v-model="searchForm.deptCode"
|
||||||
@@ -54,110 +67,129 @@
|
|||||||
style="width: 200px" />
|
style="width: 200px" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" plain icon="Search" @click="handleSearch">查询</el-button>
|
<el-button type="primary" icon="Search" @click="handleSearch">查询</el-button>
|
||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="FolderAdd"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
class="ml10"
|
班级卫生日常检查列表
|
||||||
@click="formDialogRef.openDialog()">
|
</span>
|
||||||
新 增
|
<div class="header-actions">
|
||||||
</el-button>
|
<el-button
|
||||||
<right-toolbar
|
icon="FolderAdd"
|
||||||
v-model:showSearch="showSearch"
|
type="primary"
|
||||||
class="ml10 mr20"
|
@click="formDialogRef.openDialog()">
|
||||||
style="float: right;"
|
新增
|
||||||
@queryTable="getDataList">
|
</el-button>
|
||||||
<TableColumnControl
|
<right-toolbar
|
||||||
ref="columnControlRef"
|
v-model:showSearch="showSearch"
|
||||||
:columns="tableColumns"
|
class="ml10"
|
||||||
v-model="visibleColumns"
|
@queryTable="getDataList">
|
||||||
trigger-type="default"
|
<TableColumnControl
|
||||||
trigger-circle
|
ref="columnControlRef"
|
||||||
@change="handleColumnChange"
|
:columns="tableColumns"
|
||||||
@order-change="handleColumnOrderChange"
|
v-model="visibleColumns"
|
||||||
>
|
trigger-type="default"
|
||||||
<template #trigger>
|
trigger-circle
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
@change="handleColumnChange"
|
||||||
<el-button circle style="margin-left: 0;">
|
@order-change="handleColumnOrderChange"
|
||||||
<el-icon><Menu /></el-icon>
|
>
|
||||||
</el-button>
|
<template #trigger>
|
||||||
</el-tooltip>
|
<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"
|
||||||
|
@sort-change="sortChangeHandle">
|
||||||
|
<el-table-column type="index" label="序号" 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 !== '操作'"
|
||||||
|
: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>
|
||||||
</TableColumnControl>
|
<!-- 扣分列特殊模板 -->
|
||||||
</right-toolbar>
|
<template v-if="col.prop === 'score'" #default="scope">
|
||||||
|
<el-tag v-if="scope.row.score !== undefined && scope.row.score !== null" size="small" type="danger" effect="plain" round>
|
||||||
|
{{ scope.row.score }}
|
||||||
|
</el-tag>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
icon="EditPen"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="formDialogRef.openDialog(scope.row.id)">
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="Delete"
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
@click="handleDelete([scope.row.id])">
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<template #empty>
|
||||||
|
<el-empty description="暂无数据" :image-size="120">
|
||||||
|
<el-button type="primary" icon="FolderAdd" @click="formDialogRef.openDialog()">新增记录</el-button>
|
||||||
|
</el-empty>
|
||||||
|
</template>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<div class="pagination-wrapper">
|
||||||
|
<pagination
|
||||||
|
@size-change="sizeChangeHandle"
|
||||||
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
</div>
|
</div>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 表格 -->
|
|
||||||
<el-table
|
|
||||||
:data="state.dataList"
|
|
||||||
v-loading="state.loading"
|
|
||||||
border
|
|
||||||
:cell-style="tableStyle.cellStyle"
|
|
||||||
:header-cell-style="tableStyle.headerCellStyle"
|
|
||||||
@sort-change="sortChangeHandle">
|
|
||||||
<el-table-column type="index" label="序号" align="center">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
v-for="col in visibleColumnsSorted"
|
|
||||||
:key="col.prop"
|
|
||||||
:prop="col.prop"
|
|
||||||
:label="col.label"
|
|
||||||
:width="col.width"
|
|
||||||
:min-width="col.minWidth"
|
|
||||||
:show-overflow-tooltip="col.showOverflowTooltip !== false"
|
|
||||||
:align="col.align">
|
|
||||||
<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 === 'score'">
|
|
||||||
<el-tag v-if="scope.row.score !== undefined && scope.row.score !== null" size="small" type="danger" effect="plain">
|
|
||||||
{{ scope.row.score }}
|
|
||||||
</el-tag>
|
|
||||||
<span v-else>-</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="操作" align="center" fixed="right">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><Setting /></el-icon>
|
|
||||||
<span style="margin-left: 4px">操作</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
icon="Edit"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="formDialogRef.openDialog(scope.row.id)">
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Delete"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete([scope.row.id])">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
|
||||||
<pagination
|
|
||||||
@size-change="sizeChangeHandle"
|
|
||||||
@current-change="currentChangeHandle"
|
|
||||||
v-bind="state.pagination" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 编辑、新增 -->
|
<!-- 编辑、新增 -->
|
||||||
@@ -167,25 +199,22 @@
|
|||||||
|
|
||||||
<script setup lang="ts" name="ClassHygieneDaily">
|
<script setup lang="ts" name="ClassHygieneDaily">
|
||||||
import { ref, reactive, defineAsyncComponent, computed, onMounted } from 'vue'
|
import { ref, reactive, defineAsyncComponent, computed, onMounted } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||||
import { fetchList, delObjs } from "/@/api/stuwork/classhygienedaily";
|
import { fetchList, delObjs } from "/@/api/stuwork/classhygienedaily";
|
||||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||||
import { getDeptList } from '/@/api/basic/basicclass'
|
import { getDeptList } from '/@/api/basic/basicclass'
|
||||||
import { getClassListByRole } from '/@/api/basic/basicclass'
|
import { getClassListByRole } from '/@/api/basic/basicclass'
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import { List, OfficeBuilding, Grid, Calendar, Minus, Document, Setting, Menu } from '@element-plus/icons-vue'
|
import { List, OfficeBuilding, Grid, Calendar, Minus, Document, Setting, Menu, Search, FolderAdd, EditPen } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
|
||||||
// 引入组件
|
// 引入组件
|
||||||
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
|
||||||
const formDialogRef = ref()
|
const formDialogRef = ref()
|
||||||
const searchFormRef = ref()
|
const searchFormRef = ref()
|
||||||
const columnControlRef = ref()
|
const columnControlRef = ref()
|
||||||
// 搜索变量
|
|
||||||
const showSearch = ref(true)
|
const showSearch = ref(true)
|
||||||
const deptList = ref<any[]>([])
|
const deptList = ref<any[]>([])
|
||||||
const classList = ref<any[]>([])
|
const classList = ref<any[]>([])
|
||||||
@@ -199,94 +228,14 @@ const tableColumns = [
|
|||||||
{ prop: 'note', label: '检查记录', icon: Document }
|
{ prop: 'note', label: '检查记录', icon: Document }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制 Hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列变化处理
|
|
||||||
const handleColumnChange = (value: string[]) => {
|
|
||||||
visibleColumns.value = value
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = value.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列排序变化处理
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const visibleColumnsSorted = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: any[] = []
|
|
||||||
const unorderedColumns: any[] = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 搜索表单
|
// 搜索表单
|
||||||
const searchForm = reactive({
|
const searchForm = reactive({
|
||||||
@@ -325,7 +274,6 @@ const {
|
|||||||
|
|
||||||
// 学院选择变化
|
// 学院选择变化
|
||||||
const handleDeptChange = () => {
|
const handleDeptChange = () => {
|
||||||
// 清空班号选择
|
|
||||||
searchForm.classCode = ''
|
searchForm.classCode = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -336,7 +284,7 @@ const handleSearch = () => {
|
|||||||
|
|
||||||
// 重置
|
// 重置
|
||||||
const handleReset = () => {
|
const handleReset = () => {
|
||||||
searchFormRef.value?.formRef?.resetFields()
|
searchFormRef.value?.resetFields()
|
||||||
searchForm.deptCode = ''
|
searchForm.deptCode = ''
|
||||||
searchForm.classCode = ''
|
searchForm.classCode = ''
|
||||||
searchForm.startTime = ''
|
searchForm.startTime = ''
|
||||||
@@ -390,9 +338,12 @@ const getClassListData = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
loadSavedConfig()
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getDeptListData()
|
getDeptListData()
|
||||||
getClassListData()
|
getClassListData()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,9 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form
|
||||||
|
:model="searchForm"
|
||||||
|
ref="searchFormRef"
|
||||||
|
:inline="true"
|
||||||
|
@keyup.enter="handleSearch"
|
||||||
|
class="search-form">
|
||||||
<el-form-item label="楼号" prop="buildingNo">
|
<el-form-item label="楼号" prop="buildingNo">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="searchForm.buildingNo"
|
v-model="searchForm.buildingNo"
|
||||||
@@ -45,120 +58,135 @@
|
|||||||
style="width: 200px" />
|
style="width: 200px" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" plain icon="Search" @click="handleSearch">查询</el-button>
|
<el-button type="primary" icon="Search" @click="handleSearch">查询</el-button>
|
||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<right-toolbar
|
<div class="card-header">
|
||||||
v-model:showSearch="showSearch"
|
<span class="card-title">
|
||||||
class="ml10"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
style="float: right;"
|
班级卫生日常分析列表
|
||||||
@queryTable="getDataList">
|
</span>
|
||||||
<TableColumnControl
|
<div class="header-actions">
|
||||||
ref="columnControlRef"
|
<right-toolbar
|
||||||
:columns="tableColumns"
|
v-model:showSearch="showSearch"
|
||||||
v-model="visibleColumns"
|
class="ml10"
|
||||||
trigger-type="default"
|
@queryTable="getDataList">
|
||||||
trigger-circle
|
<TableColumnControl
|
||||||
@change="handleColumnChange"
|
ref="columnControlRef"
|
||||||
@order-change="handleColumnOrderChange"
|
:columns="tableColumns"
|
||||||
>
|
v-model="visibleColumns"
|
||||||
<template #trigger>
|
trigger-type="default"
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
trigger-circle
|
||||||
<el-button circle style="margin-left: 0;">
|
@change="handleColumnChange"
|
||||||
<el-icon><Menu /></el-icon>
|
@order-change="handleColumnOrderChange"
|
||||||
</el-button>
|
>
|
||||||
</el-tooltip>
|
<template #trigger>
|
||||||
</template>
|
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
||||||
</TableColumnControl>
|
<el-button circle style="margin-left: 0;">
|
||||||
</right-toolbar>
|
<el-icon><Menu /></el-icon>
|
||||||
</div>
|
</el-button>
|
||||||
</el-row>
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
</TableColumnControl>
|
||||||
|
</right-toolbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table
|
<el-table
|
||||||
:data="state.dataList"
|
:data="state.dataList"
|
||||||
v-loading="state.loading"
|
v-loading="state.loading"
|
||||||
border
|
stripe
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle"
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
@sort-change="sortChangeHandle">
|
class="modern-table"
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
@sort-change="sortChangeHandle">
|
||||||
<template #header>
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<template v-for="col in sortedTableColumns" :key="col.prop || col.label">
|
|
||||||
<el-table-column
|
|
||||||
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作' && !col.prop?.startsWith('day')"
|
|
||||||
:prop="col.prop"
|
|
||||||
:label="col.label"
|
|
||||||
show-overflow-tooltip
|
|
||||||
align="center">
|
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
<el-icon><List /></el-icon>
|
||||||
<span style="margin-left: 4px">{{ col.label }}</span>
|
|
||||||
</template>
|
</template>
|
||||||
<!-- 总分列特殊模板 -->
|
<template #default="{ $index }">
|
||||||
<template v-if="col.prop === 'totalScore'" #default="scope">
|
{{ $index + 1 }}
|
||||||
<el-tag v-if="scope.row.totalScore !== undefined && scope.row.totalScore !== null" size="small" type="success" effect="plain">
|
|
||||||
{{ scope.row.totalScore }}
|
|
||||||
</el-tag>
|
|
||||||
<span v-else>-</span>
|
|
||||||
</template>
|
|
||||||
<!-- 排名列特殊模板 -->
|
|
||||||
<template v-else-if="col.prop === 'rank'" #default="scope">
|
|
||||||
<el-tag v-if="scope.row.rank" size="small" :type="scope.row.rank <= 3 ? 'warning' : 'info'" effect="plain">
|
|
||||||
{{ scope.row.rank }}
|
|
||||||
</el-tag>
|
|
||||||
<span v-else>-</span>
|
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</template>
|
<template v-for="col in visibleColumnsSorted" :key="col.prop || col.label">
|
||||||
<!-- 动态日期列(不受列控制影响,始终显示) -->
|
<el-table-column
|
||||||
<el-table-column
|
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作' && !col.prop?.startsWith('day')"
|
||||||
v-for="day in dayColumns"
|
:prop="col.prop"
|
||||||
:key="day"
|
:label="col.label"
|
||||||
:prop="`day${day}`"
|
:width="col.width"
|
||||||
:label="`${day}日`"
|
:min-width="col.minWidth"
|
||||||
show-overflow-tooltip
|
:show-overflow-tooltip="col.showOverflowTooltip !== false"
|
||||||
align="center"
|
:align="col.align || 'center'">
|
||||||
width="80">
|
<template #header>
|
||||||
<template #header>
|
<el-icon v-if="col.icon"><component :is="col.icon" /></el-icon>
|
||||||
<el-icon><Calendar /></el-icon>
|
<span :style="{ marginLeft: col.icon ? '4px' : '0' }">{{ col.label }}</span>
|
||||||
<span style="margin-left: 4px">{{ day }}日</span>
|
</template>
|
||||||
|
<!-- 总分列特殊模板 -->
|
||||||
|
<template v-if="col.prop === 'totalScore'" #default="scope">
|
||||||
|
<el-tag v-if="scope.row.totalScore !== undefined && scope.row.totalScore !== null" size="small" type="success" effect="plain" round>
|
||||||
|
{{ scope.row.totalScore }}
|
||||||
|
</el-tag>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
<!-- 排名列特殊模板 -->
|
||||||
|
<template v-else-if="col.prop === 'rank'" #default="scope">
|
||||||
|
<el-tag v-if="scope.row.rank" size="small" :type="scope.row.rank <= 3 ? 'warning' : 'info'" effect="plain" round>
|
||||||
|
{{ scope.row.rank }}
|
||||||
|
</el-tag>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
<!-- 动态日期列(不受列控制影响,始终显示) -->
|
||||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
<el-table-column
|
||||||
<template #header>
|
v-for="day in dayColumns"
|
||||||
<el-icon><Setting /></el-icon>
|
:key="day"
|
||||||
<span style="margin-left: 4px">操作</span>
|
:prop="`day${day}`"
|
||||||
|
:label="`${day}日`"
|
||||||
|
show-overflow-tooltip
|
||||||
|
align="center"
|
||||||
|
width="80">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Calendar /></el-icon>
|
||||||
|
<span style="margin-left: 4px">{{ day }}日</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
icon="Plus"
|
||||||
|
link
|
||||||
|
type="success"
|
||||||
|
@click="handleAddScore(scope.row)">
|
||||||
|
加分
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="Minus"
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
@click="handleSubtractScore(scope.row)">
|
||||||
|
减分
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<template #empty>
|
||||||
|
<el-empty description="暂无数据" :image-size="120">
|
||||||
|
</el-empty>
|
||||||
</template>
|
</template>
|
||||||
<template #default="scope">
|
</el-table>
|
||||||
<el-button
|
</el-card>
|
||||||
icon="Plus"
|
|
||||||
text
|
|
||||||
type="success"
|
|
||||||
@click="handleAddScore(scope.row)">
|
|
||||||
加分
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Minus"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleSubtractScore(scope.row)">
|
|
||||||
减分
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 此接口不支持分页,不显示分页组件 -->
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 加分/减分对话框 -->
|
<!-- 加分/减分对话框 -->
|
||||||
@@ -216,142 +244,42 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="ClassHygieneDailyAnalysis">
|
<script setup lang="ts" name="ClassHygieneDailyAnalysis">
|
||||||
import { ref, reactive, defineAsyncComponent, onMounted, computed, nextTick } from 'vue'
|
import { ref, reactive, onMounted, computed } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||||
import { fetchList } from "/@/api/stuwork/classhygienedailyanalysis";
|
import { fetchList } from "/@/api/stuwork/classhygienedailyanalysis";
|
||||||
import { useMessage } from "/@/hooks/message";
|
import { useMessage } from "/@/hooks/message";
|
||||||
import { getBuildingList } from '/@/api/stuwork/dormbuilding'
|
import { getBuildingList } from '/@/api/stuwork/dormbuilding'
|
||||||
import { getClassListByRole } from '/@/api/basic/basicclass'
|
import { getClassListByRole } from '/@/api/basic/basicclass'
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import { List, DataAnalysis, Trophy, Grid, Calendar, Setting, Menu } from '@element-plus/icons-vue'
|
import { List, DataAnalysis, Trophy, Grid, Calendar, Setting, Menu, Search, Document } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
|
||||||
const searchFormRef = ref()
|
const searchFormRef = ref()
|
||||||
const columnControlRef = ref<any>()
|
const columnControlRef = ref<any>()
|
||||||
const scoreFormRef = ref()
|
const scoreFormRef = ref()
|
||||||
// 搜索变量
|
|
||||||
const showSearch = ref(true)
|
const showSearch = ref(true)
|
||||||
const buildingList = ref<any[]>([])
|
const buildingList = ref<any[]>([])
|
||||||
const classList = ref<any[]>([])
|
const classList = ref<any[]>([])
|
||||||
const scoreDialogVisible = ref(false)
|
const scoreDialogVisible = ref(false)
|
||||||
const scoreDialogTitle = ref('加分')
|
const scoreDialogTitle = ref('加分')
|
||||||
const isAddScore = ref(true) // true: 加分, false: 减分
|
const isAddScore = ref(true)
|
||||||
|
|
||||||
// 表格列配置(只包含固定列,动态日期列不包含在内)
|
// 表格列配置(只包含固定列,动态日期列不包含在内)
|
||||||
const tableColumns = [
|
const tableColumns = [
|
||||||
{ prop: 'totalScore', label: '总分' },
|
{ prop: 'totalScore', label: '总分', icon: DataAnalysis },
|
||||||
{ prop: 'rank', label: '排名' },
|
{ prop: 'rank', label: '排名', icon: Trophy },
|
||||||
{ prop: 'classNo', label: '班级' }
|
{ prop: 'classNo', label: '班级', icon: Grid }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 列配置映射(用于图标)
|
// 使用表格列控制 Hook
|
||||||
const columnConfigMap: Record<string, { icon: any }> = {
|
const {
|
||||||
totalScore: { icon: DataAnalysis },
|
visibleColumns,
|
||||||
rank: { icon: Trophy },
|
visibleColumnsSorted,
|
||||||
classNo: { icon: Grid }
|
checkColumnVisible,
|
||||||
}
|
handleColumnChange,
|
||||||
|
handleColumnOrderChange
|
||||||
// 当前显示的列
|
} = useTableColumnControl(tableColumns)
|
||||||
const visibleColumns = ref<string[]>([])
|
|
||||||
// 列排序顺序
|
|
||||||
const columnOrder = ref<string[]>([])
|
|
||||||
|
|
||||||
// 从本地统一存储加载配置
|
|
||||||
const loadSavedConfig = () => {
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadSavedConfig()
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const sortedTableColumns = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: typeof tableColumns = []
|
|
||||||
const unorderedColumns: typeof tableColumns = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 列显示控制函数
|
|
||||||
const checkColumnVisible = (prop: string): boolean => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return visibleColumns.value.includes(prop)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列变化
|
|
||||||
const handleColumnChange = (columns: string[]) => {
|
|
||||||
visibleColumns.value = columns
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = columns.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列排序变化
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 加分/减分表单
|
// 加分/减分表单
|
||||||
const scoreForm = reactive({
|
const scoreForm = reactive({
|
||||||
@@ -382,11 +310,10 @@ const dayColumns = computed(() => {
|
|||||||
// 配置 useTable - 接口参数为 buildingNo 和 month(数组格式)
|
// 配置 useTable - 接口参数为 buildingNo 和 month(数组格式)
|
||||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||||
queryForm: searchForm,
|
queryForm: searchForm,
|
||||||
isPage: false, // 接口不支持分页
|
isPage: false,
|
||||||
pageList: async (queryParams: any) => {
|
pageList: async (queryParams: any) => {
|
||||||
const params: any = {}
|
const params: any = {}
|
||||||
|
|
||||||
// 接口文档要求 buildingNo 和 month 都是数组格式
|
|
||||||
if (searchForm.buildingNo) {
|
if (searchForm.buildingNo) {
|
||||||
params.buildingNo = Array.isArray(searchForm.buildingNo) ? searchForm.buildingNo : [searchForm.buildingNo]
|
params.buildingNo = Array.isArray(searchForm.buildingNo) ? searchForm.buildingNo : [searchForm.buildingNo]
|
||||||
}
|
}
|
||||||
@@ -399,8 +326,6 @@ const state: BasicTableProps = reactive<BasicTableProps>({
|
|||||||
|
|
||||||
const res = await fetchList(params)
|
const res = await fetchList(params)
|
||||||
|
|
||||||
// 接口返回的数据结构:数组,每个元素包含日期字段(如 2025-12-01)和班级信息
|
|
||||||
// 需要确保返回数组格式给 useTable
|
|
||||||
let dataList = []
|
let dataList = []
|
||||||
|
|
||||||
if (Array.isArray(res.data)) {
|
if (Array.isArray(res.data)) {
|
||||||
@@ -410,11 +335,9 @@ const state: BasicTableProps = reactive<BasicTableProps>({
|
|||||||
} else if (res.data && Array.isArray(res.data.list)) {
|
} else if (res.data && Array.isArray(res.data.list)) {
|
||||||
dataList = res.data.list
|
dataList = res.data.list
|
||||||
} else if (res.data && typeof res.data === 'object') {
|
} else if (res.data && typeof res.data === 'object') {
|
||||||
// 如果 res.data 是单个对象,转换为数组
|
|
||||||
dataList = [res.data]
|
dataList = [res.data]
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理数据:将日期字段转换为 day1, day2 等格式,方便表格显示
|
|
||||||
const processedData = dataList.map((item: any) => {
|
const processedData = dataList.map((item: any) => {
|
||||||
const processed: any = {
|
const processed: any = {
|
||||||
classCode: item.classCode || '',
|
classCode: item.classCode || '',
|
||||||
@@ -424,18 +347,15 @@ const state: BasicTableProps = reactive<BasicTableProps>({
|
|||||||
isAddScore: item.isAddScore || 0
|
isAddScore: item.isAddScore || 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提取所有日期字段(格式为 YYYY-MM-DD)
|
|
||||||
const datePattern = /^\d{4}-\d{2}-\d{2}$/
|
const datePattern = /^\d{4}-\d{2}-\d{2}$/
|
||||||
Object.keys(item).forEach(key => {
|
Object.keys(item).forEach(key => {
|
||||||
if (datePattern.test(key)) {
|
if (datePattern.test(key)) {
|
||||||
// 将日期转换为 day1, day2 等格式(根据日期中的天数)
|
|
||||||
const date = new Date(key)
|
const date = new Date(key)
|
||||||
const day = date.getDate()
|
const day = date.getDate()
|
||||||
processed[`day${day}`] = item[key]
|
processed[`day${day}`] = item[key]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 确保所有日期字段都存在(即使值为0或null),避免表格列无法显示
|
|
||||||
if (searchForm.month) {
|
if (searchForm.month) {
|
||||||
const [year, month] = searchForm.month.split('-').map(Number)
|
const [year, month] = searchForm.month.split('-').map(Number)
|
||||||
const daysInMonth = new Date(year, month, 0).getDate()
|
const daysInMonth = new Date(year, month, 0).getDate()
|
||||||
@@ -458,7 +378,7 @@ const state: BasicTableProps = reactive<BasicTableProps>({
|
|||||||
item: 'records',
|
item: 'records',
|
||||||
totalCount: 'total'
|
totalCount: 'total'
|
||||||
},
|
},
|
||||||
createdIsNeed: false // 不自动加载,需要手动调用
|
createdIsNeed: false
|
||||||
})
|
})
|
||||||
|
|
||||||
// table hook
|
// table hook
|
||||||
@@ -529,8 +449,6 @@ const submitScore = async () => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// TODO: 调用加分/减分接口
|
// TODO: 调用加分/减分接口
|
||||||
// const api = isAddScore.value ? addScore : subtractScore
|
|
||||||
// await api(scoreForm)
|
|
||||||
useMessage().success(isAddScore.value ? '加分成功' : '减分成功')
|
useMessage().success(isAddScore.value ? '加分成功' : '减分成功')
|
||||||
scoreDialogVisible.value = false
|
scoreDialogVisible.value = false
|
||||||
getDataList()
|
getDataList()
|
||||||
@@ -544,7 +462,6 @@ const getBuildingListData = async () => {
|
|||||||
try {
|
try {
|
||||||
const res = await getBuildingList()
|
const res = await getBuildingList()
|
||||||
if (res.data) {
|
if (res.data) {
|
||||||
// 处理返回的数据,可能是分页数据
|
|
||||||
if (res.data.records) {
|
if (res.data.records) {
|
||||||
buildingList.value = Array.isArray(res.data.records) ? res.data.records : []
|
buildingList.value = Array.isArray(res.data.records) ? res.data.records : []
|
||||||
} else if (Array.isArray(res.data)) {
|
} else if (Array.isArray(res.data)) {
|
||||||
@@ -582,10 +499,9 @@ const getClassListData = async () => {
|
|||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getBuildingListData()
|
getBuildingListData()
|
||||||
getClassListData()
|
getClassListData()
|
||||||
nextTick(() => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
loadSavedConfig()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,9 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form
|
||||||
|
:model="searchForm"
|
||||||
|
ref="searchFormRef"
|
||||||
|
:inline="true"
|
||||||
|
@keyup.enter="handleSearch"
|
||||||
|
class="search-form">
|
||||||
<el-form-item label="学年" prop="schoolYear">
|
<el-form-item label="学年" prop="schoolYear">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="searchForm.schoolYear"
|
v-model="searchForm.schoolYear"
|
||||||
@@ -92,165 +105,193 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" plain icon="Search" @click="handleSearch">查询</el-button>
|
<el-button type="primary" icon="Search" @click="handleSearch">查询</el-button>
|
||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="Plus"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
class="ml10"
|
班级请假申请列表
|
||||||
@click="formDialogRef.openDialog()">
|
</span>
|
||||||
批量新增
|
<div class="header-actions">
|
||||||
</el-button>
|
<el-button
|
||||||
<el-button
|
icon="Plus"
|
||||||
icon="Delete"
|
type="primary"
|
||||||
type="danger"
|
@click="formDialogRef.openDialog()">
|
||||||
class="ml10"
|
批量新增
|
||||||
@click="handleDeleteByClass">
|
</el-button>
|
||||||
整班删除
|
<el-button
|
||||||
</el-button>
|
icon="Delete"
|
||||||
<right-toolbar
|
type="danger"
|
||||||
v-model:showSearch="showSearch"
|
class="ml10"
|
||||||
class="ml10 mr20"
|
@click="handleDeleteByClass">
|
||||||
style="float: right;"
|
整班删除
|
||||||
@queryTable="getDataList">
|
</el-button>
|
||||||
<TableColumnControl
|
<right-toolbar
|
||||||
ref="columnControlRef"
|
v-model:showSearch="showSearch"
|
||||||
:columns="tableColumns"
|
class="ml10"
|
||||||
v-model="visibleColumns"
|
@queryTable="getDataList">
|
||||||
trigger-type="default"
|
<TableColumnControl
|
||||||
trigger-circle
|
ref="columnControlRef"
|
||||||
@change="handleColumnChange"
|
:columns="tableColumns"
|
||||||
@order-change="handleColumnOrderChange"
|
v-model="visibleColumns"
|
||||||
>
|
trigger-type="default"
|
||||||
<template #trigger>
|
trigger-circle
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
@change="handleColumnChange"
|
||||||
<el-button circle style="margin-left: 0;">
|
@order-change="handleColumnOrderChange"
|
||||||
<el-icon><Menu /></el-icon>
|
>
|
||||||
</el-button>
|
<template #trigger>
|
||||||
</el-tooltip>
|
<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="序号" 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 !== '操作'"
|
||||||
|
: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>
|
||||||
</TableColumnControl>
|
<!-- 学期列特殊模板 -->
|
||||||
</right-toolbar>
|
<template v-if="col.prop === 'schoolTerm'" #default="scope">
|
||||||
|
<el-tag size="small" type="primary" effect="plain" round>
|
||||||
|
{{ formatSchoolTerm(scope.row.schoolTerm) }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
<!-- 人数列特殊模板 -->
|
||||||
|
<template v-else-if="col.prop === 'num'" #default="scope">
|
||||||
|
<el-tag v-if="scope.row.num" size="small" type="success" effect="plain" round>
|
||||||
|
{{ scope.row.num }}
|
||||||
|
</el-tag>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
<!-- 开始时间列特殊模板 -->
|
||||||
|
<template v-else-if="col.prop === 'startTime'" #default="scope">
|
||||||
|
<span>{{ scope.row.startTime ? formatDateTime(scope.row.startTime) : '-' }}</span>
|
||||||
|
</template>
|
||||||
|
<!-- 结束时间列特殊模板 -->
|
||||||
|
<template v-else-if="col.prop === 'endTime'" #default="scope">
|
||||||
|
<span>{{ scope.row.endTime ? formatDateTime(scope.row.endTime) : '-' }}</span>
|
||||||
|
</template>
|
||||||
|
<!-- 请假类型列特殊模板 -->
|
||||||
|
<template v-else-if="col.prop === 'leaveType'" #default="scope">
|
||||||
|
<el-tag size="small" type="info" effect="plain" round>
|
||||||
|
{{ formatLeaveType(scope.row.leaveType) }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
<!-- 是否住宿列特殊模板 -->
|
||||||
|
<template v-else-if="col.prop === 'stayDorm'" #default="scope">
|
||||||
|
<StatusTag
|
||||||
|
:value="scope.row.stayDorm"
|
||||||
|
:options="[{ label: '是', value: '1' }, { label: '否', value: '0' }]"
|
||||||
|
:type-map="{ '1': { type: 'success', effect: 'light' }, '0': { type: 'info', effect: 'light' } }"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<!-- 时段请假列特殊模板 -->
|
||||||
|
<template v-else-if="col.prop === 'isSegment'" #default="scope">
|
||||||
|
<StatusTag
|
||||||
|
:value="scope.row.isSegment"
|
||||||
|
:options="[{ label: '是', value: '1' }, { label: '否', value: '0' }]"
|
||||||
|
:type-map="{ '1': { type: 'success', effect: 'light' }, '0': { type: 'info', effect: 'light' } }"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<!-- 请假校门列特殊模板 -->
|
||||||
|
<template v-else-if="col.prop === 'schoolDoor'" #default="scope">
|
||||||
|
<el-tag size="small" type="warning" effect="plain" round>
|
||||||
|
{{ formatSchoolDoor(scope.row.schoolDoor) }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
<!-- 基础部审核列特殊模板 -->
|
||||||
|
<template v-else-if="col.prop === 'deptAudit'" #default="scope">
|
||||||
|
<StatusTag
|
||||||
|
:value="scope.row.deptAudit"
|
||||||
|
:options="[{ label: '待审核', value: '0' }, { label: '通过', value: '1' }, { label: '驳回', value: '2' }]"
|
||||||
|
:type-map="{ '0': { type: 'warning', effect: 'light' }, '1': { type: 'success', effect: 'light' }, '2': { type: 'danger', effect: 'light' } }"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<!-- 学工处审批列特殊模板 -->
|
||||||
|
<template v-else-if="col.prop === 'schoolAudit'" #default="scope">
|
||||||
|
<StatusTag
|
||||||
|
:value="scope.row.schoolAudit"
|
||||||
|
:options="[{ label: '待审核', value: '0' }, { label: '通过', value: '1' }, { label: '驳回', value: '2' }]"
|
||||||
|
:type-map="{ '0': { type: 'warning', effect: 'light' }, '1': { type: 'success', effect: 'light' }, '2': { type: 'danger', effect: 'light' } }"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
icon="View"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="handleView(scope.row)">
|
||||||
|
查看
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="Delete"
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
@click="handleDelete(scope.row)">
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<template #empty>
|
||||||
|
<el-empty description="暂无数据" :image-size="120">
|
||||||
|
<el-button type="primary" icon="FolderAdd" @click="formDialogRef.openDialog()">批量新增</el-button>
|
||||||
|
</el-empty>
|
||||||
|
</template>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<div class="pagination-wrapper">
|
||||||
|
<pagination
|
||||||
|
@size-change="sizeChangeHandle"
|
||||||
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
</div>
|
</div>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 表格 -->
|
|
||||||
<el-table
|
|
||||||
:data="state.dataList"
|
|
||||||
v-loading="state.loading"
|
|
||||||
border
|
|
||||||
:cell-style="tableStyle.cellStyle"
|
|
||||||
:header-cell-style="tableStyle.headerCellStyle">
|
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
v-for="col in visibleColumnsSorted"
|
|
||||||
:key="col.prop"
|
|
||||||
: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 === 'num'">
|
|
||||||
<el-tag v-if="scope.row.num" size="small" type="success" effect="plain">
|
|
||||||
{{ scope.row.num }}
|
|
||||||
</el-tag>
|
|
||||||
<span v-else>-</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope" v-else-if="col.prop === 'startTime'">
|
|
||||||
<span>{{ scope.row.startTime ? formatDateTime(scope.row.startTime) : '-' }}</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope" v-else-if="col.prop === 'endTime'">
|
|
||||||
<span>{{ scope.row.endTime ? formatDateTime(scope.row.endTime) : '-' }}</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope" v-else-if="col.prop === 'leaveType'">
|
|
||||||
<el-tag size="small" type="info" effect="plain">
|
|
||||||
{{ formatLeaveType(scope.row.leaveType) }}
|
|
||||||
</el-tag>
|
|
||||||
</template>
|
|
||||||
<template #default="scope" v-else-if="col.prop === 'stayDorm'">
|
|
||||||
<StatusTag
|
|
||||||
:value="scope.row.stayDorm"
|
|
||||||
:options="[{ label: '是', value: '1' }, { label: '否', value: '0' }]"
|
|
||||||
:type-map="{ '1': { type: 'success', effect: 'light' }, '0': { type: 'info', effect: 'light' } }"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #default="scope" v-else-if="col.prop === 'isSegment'">
|
|
||||||
<StatusTag
|
|
||||||
:value="scope.row.isSegment"
|
|
||||||
:options="[{ label: '是', value: '1' }, { label: '否', value: '0' }]"
|
|
||||||
:type-map="{ '1': { type: 'success', effect: 'light' }, '0': { type: 'info', effect: 'light' } }"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #default="scope" v-else-if="col.prop === 'schoolDoor'">
|
|
||||||
<el-tag size="small" type="warning" effect="plain">
|
|
||||||
{{ formatSchoolDoor(scope.row.schoolDoor) }}
|
|
||||||
</el-tag>
|
|
||||||
</template>
|
|
||||||
<template #default="scope" v-else-if="col.prop === 'deptAudit'">
|
|
||||||
<StatusTag
|
|
||||||
:value="scope.row.deptAudit"
|
|
||||||
:options="[{ label: '待审核', value: '0' }, { label: '通过', value: '1' }, { label: '驳回', value: '2' }]"
|
|
||||||
:type-map="{ '0': { type: 'warning', effect: 'light' }, '1': { type: 'success', effect: 'light' }, '2': { type: 'danger', effect: 'light' } }"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #default="scope" v-else-if="col.prop === 'schoolAudit'">
|
|
||||||
<StatusTag
|
|
||||||
:value="scope.row.schoolAudit"
|
|
||||||
:options="[{ label: '待审核', value: '0' }, { label: '通过', value: '1' }, { label: '驳回', value: '2' }]"
|
|
||||||
:type-map="{ '0': { type: 'warning', effect: 'light' }, '1': { type: 'success', effect: 'light' }, '2': { type: 'danger', effect: 'light' } }"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><Setting /></el-icon>
|
|
||||||
<span style="margin-left: 4px">操作</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
icon="View"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleView(scope.row)">
|
|
||||||
查看
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Delete"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row)">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
|
||||||
<pagination
|
|
||||||
@size-change="sizeChangeHandle"
|
|
||||||
@current-change="currentChangeHandle"
|
|
||||||
v-bind="state.pagination" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 批量新增表单弹窗 -->
|
<!-- 批量新增表单弹窗 -->
|
||||||
@@ -262,8 +303,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="ClassLeaveApply">
|
<script setup lang="ts" name="ClassLeaveApply">
|
||||||
import { reactive, ref, onMounted, computed } from 'vue'
|
import { reactive, ref, onMounted, defineAsyncComponent } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||||
import { fetchList, delObj, deleteByClass } from "/@/api/stuwork/classleaveapply";
|
import { fetchList, delObj, deleteByClass } from "/@/api/stuwork/classleaveapply";
|
||||||
import { queryAllSchoolYear } from "/@/api/basic/basicyear";
|
import { queryAllSchoolYear } from "/@/api/basic/basicyear";
|
||||||
@@ -274,13 +314,12 @@ import { useMessage, useMessageBox } from "/@/hooks/message";
|
|||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import FormDialog from './form.vue'
|
import FormDialog from './form.vue'
|
||||||
import DetailDialog from './detail.vue'
|
import DetailDialog from './detail.vue'
|
||||||
import { List, Calendar, Clock, OfficeBuilding, Grid, UserFilled, Collection, Document, House, Location, CircleCheck, Warning, Setting, Menu } from '@element-plus/icons-vue'
|
import { List, Calendar, Clock, OfficeBuilding, Grid, UserFilled, Collection, Document, House, Location, CircleCheck, Warning, Setting, Menu, Search, FolderAdd, View } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
import { defineAsyncComponent } from 'vue'
|
|
||||||
const StatusTag = defineAsyncComponent(() => import('/@/components/StatusTag/index.vue'))
|
const StatusTag = defineAsyncComponent(() => import('/@/components/StatusTag/index.vue'))
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
|
||||||
const searchFormRef = ref()
|
const searchFormRef = ref()
|
||||||
const columnControlRef = ref()
|
const columnControlRef = ref()
|
||||||
const showSearch = ref(true)
|
const showSearch = ref(true)
|
||||||
@@ -316,94 +355,14 @@ const tableColumns = [
|
|||||||
{ prop: 'rejectReason', label: '驳回原因', icon: Warning, minWidth: 150 }
|
{ prop: 'rejectReason', label: '驳回原因', icon: Warning, minWidth: 150 }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制 Hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列变化处理
|
|
||||||
const handleColumnChange = (value: string[]) => {
|
|
||||||
visibleColumns.value = value
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = value.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列排序变化处理
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const visibleColumnsSorted = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: any[] = []
|
|
||||||
const unorderedColumns: any[] = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 校门列表
|
// 校门列表
|
||||||
const schoolDoorListData = [
|
const schoolDoorListData = [
|
||||||
@@ -466,15 +425,6 @@ const formatLeaveType = (value: string) => {
|
|||||||
return item ? item.label : value
|
return item ? item.label : value
|
||||||
}
|
}
|
||||||
|
|
||||||
// 格式化是否
|
|
||||||
const formatYesNo = (value: string) => {
|
|
||||||
if (value === null || value === undefined || value === '') {
|
|
||||||
return '-'
|
|
||||||
}
|
|
||||||
const dictItem = yesNoList.value.find(item => item.value == value)
|
|
||||||
return dictItem ? dictItem.label : value
|
|
||||||
}
|
|
||||||
|
|
||||||
// 格式化校门
|
// 格式化校门
|
||||||
const formatSchoolDoor = (value: string) => {
|
const formatSchoolDoor = (value: string) => {
|
||||||
if (!value) return '-'
|
if (!value) return '-'
|
||||||
@@ -482,15 +432,6 @@ const formatSchoolDoor = (value: string) => {
|
|||||||
return item ? item.label : value
|
return item ? item.label : value
|
||||||
}
|
}
|
||||||
|
|
||||||
// 格式化审核类型
|
|
||||||
const formatAuditType = (value: string) => {
|
|
||||||
if (value === null || value === undefined || value === '') {
|
|
||||||
return '-'
|
|
||||||
}
|
|
||||||
const dictItem = auditTypeList.value.find(item => item.value == value)
|
|
||||||
return dictItem ? dictItem.label : value
|
|
||||||
}
|
|
||||||
|
|
||||||
// 查询
|
// 查询
|
||||||
const handleSearch = () => {
|
const handleSearch = () => {
|
||||||
getDataList()
|
getDataList()
|
||||||
@@ -639,7 +580,6 @@ const getAuditTypeDict = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
loadSavedConfig()
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
schoolDoorList.value = schoolDoorListData
|
schoolDoorList.value = schoolDoorListData
|
||||||
getSchoolYearListData()
|
getSchoolYearListData()
|
||||||
@@ -653,5 +593,5 @@ onMounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form
|
||||||
|
:model="searchForm"
|
||||||
|
ref="searchFormRef"
|
||||||
|
:inline="true"
|
||||||
|
@keyup.enter="handleSearch"
|
||||||
|
class="search-form">
|
||||||
<el-form-item label="学院" prop="deptCode">
|
<el-form-item label="学院" prop="deptCode">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="searchForm.deptCode"
|
v-model="searchForm.deptCode"
|
||||||
@@ -94,144 +107,164 @@
|
|||||||
style="width: 200px" />
|
style="width: 200px" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" plain icon="Search" @click="handleSearch">查询</el-button>
|
<el-button type="primary" icon="Search" @click="handleSearch">查询</el-button>
|
||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="FolderAdd"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
class="ml10"
|
班主任考核列表
|
||||||
@click="formDialogRef.openDialog()">
|
</span>
|
||||||
新 增
|
<div class="header-actions">
|
||||||
</el-button>
|
<el-button
|
||||||
<el-button
|
icon="FolderAdd"
|
||||||
icon="Upload"
|
type="primary"
|
||||||
type="success"
|
@click="formDialogRef.openDialog()">
|
||||||
class="ml10"
|
新增
|
||||||
@click="handleImport">
|
</el-button>
|
||||||
导 入
|
<el-button
|
||||||
</el-button>
|
icon="Upload"
|
||||||
<el-button
|
type="success"
|
||||||
icon="Download"
|
class="ml10"
|
||||||
type="warning"
|
@click="handleImport">
|
||||||
class="ml10"
|
导入
|
||||||
@click="handleExport">
|
</el-button>
|
||||||
导 出
|
<el-button
|
||||||
</el-button>
|
icon="Download"
|
||||||
<right-toolbar
|
type="warning"
|
||||||
v-model:showSearch="showSearch"
|
class="ml10"
|
||||||
class="ml10"
|
@click="handleExport">
|
||||||
style="float: right;"
|
导出
|
||||||
@queryTable="getDataList">
|
</el-button>
|
||||||
<TableColumnControl
|
<right-toolbar
|
||||||
ref="columnControlRef"
|
v-model:showSearch="showSearch"
|
||||||
:columns="tableColumns"
|
class="ml10"
|
||||||
v-model="visibleColumns"
|
@queryTable="getDataList">
|
||||||
trigger-type="default"
|
<TableColumnControl
|
||||||
trigger-circle
|
ref="columnControlRef"
|
||||||
@change="handleColumnChange"
|
:columns="tableColumns"
|
||||||
@order-change="handleColumnOrderChange"
|
v-model="visibleColumns"
|
||||||
>
|
trigger-type="default"
|
||||||
<template #trigger>
|
trigger-circle
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
@change="handleColumnChange"
|
||||||
<el-button circle style="margin-left: 0;">
|
@order-change="handleColumnOrderChange"
|
||||||
<el-icon><Menu /></el-icon>
|
>
|
||||||
</el-button>
|
<template #trigger>
|
||||||
</el-tooltip>
|
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
||||||
</template>
|
<el-button circle style="margin-left: 0;">
|
||||||
</TableColumnControl>
|
<el-icon><Menu /></el-icon>
|
||||||
</right-toolbar>
|
</el-button>
|
||||||
</div>
|
</el-tooltip>
|
||||||
</el-row>
|
</template>
|
||||||
|
</TableColumnControl>
|
||||||
|
</right-toolbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table
|
<el-table
|
||||||
:data="state.dataList"
|
:data="state.dataList"
|
||||||
v-loading="state.loading"
|
v-loading="state.loading"
|
||||||
border
|
stripe
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle"
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
@sort-change="sortChangeHandle">
|
class="modern-table"
|
||||||
<el-table-column type="index" label="序号" align="center">
|
@sort-change="sortChangeHandle">
|
||||||
<template #header>
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<template v-for="col in sortedTableColumns" :key="col.prop || col.label">
|
|
||||||
<el-table-column
|
|
||||||
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
|
||||||
:prop="col.prop"
|
|
||||||
:label="col.label"
|
|
||||||
show-overflow-tooltip>
|
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
<el-icon><List /></el-icon>
|
||||||
<span style="margin-left: 4px">{{ col.label }}</span>
|
|
||||||
</template>
|
</template>
|
||||||
<!-- 考核项目列特殊模板 -->
|
<template #default="{ $index }">
|
||||||
<template v-if="col.prop === 'assessmentCategory'" #default="scope">
|
{{ $index + 1 + ((state.pagination?.current || 1) - 1) * (state.pagination?.size || 10) }}
|
||||||
<span>{{ getAssessmentCategoryName(scope.row.assessmentCategory) || '-' }}</span>
|
|
||||||
</template>
|
|
||||||
<!-- 考核指标列特殊模板 -->
|
|
||||||
<template v-else-if="col.prop === 'assessmentPoint'" #default="scope">
|
|
||||||
<span>{{ getAssessmentPointName(scope.row.assessmentPoint) || '-' }}</span>
|
|
||||||
</template>
|
|
||||||
<!-- 类型列特殊模板 -->
|
|
||||||
<template v-else-if="col.prop === 'type'" #default="scope">
|
|
||||||
<el-tag size="small" :type="scope.row.type === '1' ? 'success' : scope.row.type === '2' ? 'danger' : 'info'" effect="plain">
|
|
||||||
{{ scope.row.type === '1' ? '加分' : scope.row.type === '2' ? '减分' : '-' }}
|
|
||||||
</el-tag>
|
|
||||||
</template>
|
|
||||||
<!-- 分数列特殊模板 -->
|
|
||||||
<template v-else-if="col.prop === 'score'" #default="scope">
|
|
||||||
<el-tag v-if="scope.row.score !== undefined && scope.row.score !== null" size="small" :type="scope.row.score >= 0 ? 'success' : 'danger'" effect="plain">
|
|
||||||
{{ scope.row.score }}
|
|
||||||
</el-tag>
|
|
||||||
<span v-else>-</span>
|
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</template>
|
<template v-for="col in visibleColumnsSorted" :key="col.prop || col.label">
|
||||||
<el-table-column label="操作" align="center" fixed="right" width="200">
|
<el-table-column
|
||||||
<template #header>
|
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
||||||
<el-icon><Setting /></el-icon>
|
:prop="col.prop"
|
||||||
<span style="margin-left: 4px">操作</span>
|
: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 v-if="col.prop === 'assessmentCategory'" #default="scope">
|
||||||
|
<span>{{ getAssessmentCategoryName(scope.row.assessmentCategory) || '-' }}</span>
|
||||||
|
</template>
|
||||||
|
<!-- 考核指标列特殊模板 -->
|
||||||
|
<template v-else-if="col.prop === 'assessmentPoint'" #default="scope">
|
||||||
|
<span>{{ getAssessmentPointName(scope.row.assessmentPoint) || '-' }}</span>
|
||||||
|
</template>
|
||||||
|
<!-- 类型列特殊模板 -->
|
||||||
|
<template v-else-if="col.prop === 'type'" #default="scope">
|
||||||
|
<el-tag size="small" :type="scope.row.type === '1' ? 'success' : scope.row.type === '2' ? 'danger' : 'info'" effect="plain" round>
|
||||||
|
{{ scope.row.type === '1' ? '加分' : scope.row.type === '2' ? '减分' : '-' }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
<!-- 分数列特殊模板 -->
|
||||||
|
<template v-else-if="col.prop === 'score'" #default="scope">
|
||||||
|
<el-tag v-if="scope.row.score !== undefined && scope.row.score !== null" size="small" :type="scope.row.score >= 0 ? 'success' : 'danger'" effect="plain" round>
|
||||||
|
{{ scope.row.score }}
|
||||||
|
</el-tag>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
</template>
|
</template>
|
||||||
<template #default="scope">
|
<el-table-column label="操作" align="center" fixed="right" width="200">
|
||||||
<el-button
|
<template #header>
|
||||||
icon="Edit"
|
<el-icon><Setting /></el-icon>
|
||||||
text
|
<span style="margin-left: 4px">操作</span>
|
||||||
type="primary"
|
</template>
|
||||||
@click="handleAppeal(scope.row)">
|
<template #default="scope">
|
||||||
申诉
|
<el-button
|
||||||
</el-button>
|
icon="EditPen"
|
||||||
<el-button
|
link
|
||||||
icon="Edit"
|
type="primary"
|
||||||
text
|
@click="handleAppeal(scope.row)">
|
||||||
type="primary"
|
申诉
|
||||||
@click="formDialogRef.openDialog(scope.row.id)">
|
</el-button>
|
||||||
编辑
|
<el-button
|
||||||
</el-button>
|
icon="EditPen"
|
||||||
<el-button
|
link
|
||||||
icon="Delete"
|
type="primary"
|
||||||
text
|
@click="formDialogRef.openDialog(scope.row.id)">
|
||||||
type="danger"
|
编辑
|
||||||
@click="handleDelete([scope.row.id])">
|
</el-button>
|
||||||
删除
|
<el-button
|
||||||
</el-button>
|
icon="Delete"
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
@click="handleDelete([scope.row.id])">
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<template #empty>
|
||||||
|
<el-empty description="暂无数据" :image-size="120">
|
||||||
|
<el-button type="primary" icon="FolderAdd" @click="formDialogRef.openDialog()">新增考核</el-button>
|
||||||
|
</el-empty>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table>
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<pagination
|
<div class="pagination-wrapper">
|
||||||
@size-change="sizeChangeHandle"
|
<pagination
|
||||||
@current-change="currentChangeHandle"
|
@size-change="sizeChangeHandle"
|
||||||
v-bind="state.pagination" />
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 编辑、新增 -->
|
<!-- 编辑、新增 -->
|
||||||
@@ -288,8 +321,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="ClassMasterEvaluation">
|
<script setup lang="ts" name="ClassMasterEvaluation">
|
||||||
import { ref, reactive, defineAsyncComponent, computed, onMounted, nextTick } from 'vue'
|
import { ref, reactive, defineAsyncComponent, computed, onMounted } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||||
import { fetchList, delObjs, exportData } from "/@/api/stuwork/classmasterevaluation";
|
import { fetchList, delObjs, exportData } from "/@/api/stuwork/classmasterevaluation";
|
||||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||||
@@ -298,26 +330,23 @@ import { getClassListByRole } from '/@/api/basic/basicclass'
|
|||||||
import { getList as getAssessmentCategoryList } from '/@/api/stuwork/assessmentcategory'
|
import { getList as getAssessmentCategoryList } from '/@/api/stuwork/assessmentcategory'
|
||||||
import { getList as getAssessmentPointList } from '/@/api/stuwork/assessmentpoint'
|
import { getList as getAssessmentPointList } from '/@/api/stuwork/assessmentpoint'
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
|
import { List, Grid, UserFilled, Tickets, DataAnalysis, Collection, Calendar, User, Document, Setting, Menu, Search, FolderAdd, EditPen } from '@element-plus/icons-vue'
|
||||||
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
|
||||||
// 引入组件
|
// 引入组件
|
||||||
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
||||||
import { List, Grid, UserFilled, Tickets, DataAnalysis, Collection, Calendar, User, Document, Setting, Menu } from '@element-plus/icons-vue'
|
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
|
||||||
const formDialogRef = ref()
|
const formDialogRef = ref()
|
||||||
const columnControlRef = ref<any>()
|
const columnControlRef = ref<any>()
|
||||||
const searchFormRef = ref()
|
const searchFormRef = ref()
|
||||||
const uploadExcelRef = ref()
|
const uploadExcelRef = ref()
|
||||||
// 搜索变量
|
|
||||||
const showSearch = ref(true)
|
const showSearch = ref(true)
|
||||||
const deptList = ref<any[]>([])
|
const deptList = ref<any[]>([])
|
||||||
const classList = ref<any[]>([])
|
const classList = ref<any[]>([])
|
||||||
const assessmentCategoryList = ref<any[]>([])
|
const assessmentCategoryList = ref<any[]>([])
|
||||||
const assessmentPointList = ref<any[]>([])
|
const assessmentPointList = ref<any[]>([])
|
||||||
const appealDialogVisible = ref(false)
|
const appealDialogVisible = ref(false)
|
||||||
// 模板文件URL - 标记为 assets 文件夹下的文件
|
|
||||||
const templateUrl = ref('assets/file/班主任考核导入模板.xlsx')
|
const templateUrl = ref('assets/file/班主任考核导入模板.xlsx')
|
||||||
const appealForm = reactive({
|
const appealForm = reactive({
|
||||||
id: '',
|
id: '',
|
||||||
@@ -334,128 +363,25 @@ const appealForm = reactive({
|
|||||||
|
|
||||||
// 表格列配置
|
// 表格列配置
|
||||||
const tableColumns = [
|
const tableColumns = [
|
||||||
{ prop: 'virtualClassNo', label: '班号' },
|
{ prop: 'virtualClassNo', label: '班号', icon: Grid },
|
||||||
{ prop: 'realName', label: '班主任' },
|
{ prop: 'realName', label: '班主任', icon: UserFilled },
|
||||||
{ prop: 'assessmentCategory', label: '考核项目' },
|
{ prop: 'assessmentCategory', label: '考核项目', icon: Tickets },
|
||||||
{ prop: 'assessmentPoint', label: '考核指标' },
|
{ prop: 'assessmentPoint', label: '考核指标', icon: DataAnalysis },
|
||||||
{ prop: 'type', label: '类型' },
|
{ prop: 'type', label: '类型', icon: Collection },
|
||||||
{ prop: 'score', label: '分数' },
|
{ prop: 'score', label: '分数', icon: DataAnalysis },
|
||||||
{ prop: 'recordDate', label: '考核日期' },
|
{ prop: 'recordDate', label: '考核日期', icon: Calendar },
|
||||||
{ prop: 'createBy', label: '考核人' },
|
{ prop: 'createBy', label: '考核人', icon: User },
|
||||||
{ prop: 'remarks', label: '情况说明' }
|
{ prop: 'remarks', label: '情况说明', icon: Document, minWidth: 150 }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 列配置映射(用于图标)
|
// 使用表格列控制 Hook
|
||||||
const columnConfigMap: Record<string, { icon: any }> = {
|
const {
|
||||||
virtualClassNo: { icon: Grid },
|
visibleColumns,
|
||||||
realName: { icon: UserFilled },
|
visibleColumnsSorted,
|
||||||
assessmentCategory: { icon: Tickets },
|
checkColumnVisible,
|
||||||
assessmentPoint: { icon: DataAnalysis },
|
handleColumnChange,
|
||||||
type: { icon: Collection },
|
handleColumnOrderChange
|
||||||
score: { icon: DataAnalysis },
|
} = useTableColumnControl(tableColumns)
|
||||||
recordDate: { icon: Calendar },
|
|
||||||
createBy: { icon: User },
|
|
||||||
remarks: { icon: Document }
|
|
||||||
}
|
|
||||||
|
|
||||||
// 当前显示的列
|
|
||||||
const visibleColumns = ref<string[]>([])
|
|
||||||
// 列排序顺序
|
|
||||||
const columnOrder = ref<string[]>([])
|
|
||||||
|
|
||||||
// 从本地统一存储加载配置
|
|
||||||
const loadSavedConfig = () => {
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadSavedConfig()
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const sortedTableColumns = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: typeof tableColumns = []
|
|
||||||
const unorderedColumns: typeof tableColumns = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 列显示控制函数
|
|
||||||
const checkColumnVisible = (prop: string): boolean => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return visibleColumns.value.includes(prop)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列变化
|
|
||||||
const handleColumnChange = (columns: string[]) => {
|
|
||||||
visibleColumns.value = columns
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = columns.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列排序变化
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 搜索表单
|
// 搜索表单
|
||||||
const searchForm = reactive({
|
const searchForm = reactive({
|
||||||
@@ -498,7 +424,6 @@ const {
|
|||||||
|
|
||||||
// 学院选择变化
|
// 学院选择变化
|
||||||
const handleDeptChange = () => {
|
const handleDeptChange = () => {
|
||||||
// 清空班号选择
|
|
||||||
searchForm.classCode = ''
|
searchForm.classCode = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -548,7 +473,6 @@ const handleImport = () => {
|
|||||||
const handleExport = async () => {
|
const handleExport = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await exportData(searchForm)
|
const res = await exportData(searchForm)
|
||||||
// 处理返回的文件流
|
|
||||||
const blob = new Blob([res.data])
|
const blob = new Blob([res.data])
|
||||||
const elink = document.createElement('a')
|
const elink = document.createElement('a')
|
||||||
elink.download = '班主任考核.xlsx'
|
elink.download = '班主任考核.xlsx'
|
||||||
@@ -598,7 +522,6 @@ const submitAppeal = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 获取学院列表
|
// 获取学院列表
|
||||||
const getDeptListData = async () => {
|
const getDeptListData = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -667,10 +590,9 @@ onMounted(() => {
|
|||||||
getClassListData()
|
getClassListData()
|
||||||
getAssessmentCategoryListData()
|
getAssessmentCategoryListData()
|
||||||
getAssessmentPointListData()
|
getAssessmentPointListData()
|
||||||
nextTick(() => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
loadSavedConfig()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch" class="search-form">
|
||||||
<el-form-item label="学院" prop="deptCode">
|
<el-form-item label="学院" prop="deptCode">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="searchForm.deptCode"
|
v-model="searchForm.deptCode"
|
||||||
@@ -55,16 +63,21 @@
|
|||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<right-toolbar
|
<div class="card-header">
|
||||||
v-model:showSearch="showSearch"
|
<span class="card-title">
|
||||||
class="ml10 mr20"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
style="float: right;"
|
班主任考核申诉列表
|
||||||
@queryTable="getDataList">
|
</span>
|
||||||
|
<div class="header-actions">
|
||||||
|
<right-toolbar
|
||||||
|
v-model:showSearch="showSearch"
|
||||||
|
class="ml10"
|
||||||
|
@queryTable="getDataList">
|
||||||
<TableColumnControl
|
<TableColumnControl
|
||||||
ref="columnControlRef"
|
ref="columnControlRef"
|
||||||
:columns="tableColumns"
|
:columns="tableColumns"
|
||||||
@@ -83,73 +96,85 @@
|
|||||||
</template>
|
</template>
|
||||||
</TableColumnControl>
|
</TableColumnControl>
|
||||||
</right-toolbar>
|
</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"
|
||||||
|
@sort-change="sortChangeHandle">
|
||||||
|
<el-table-column type="index" label="序号" 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 !== '操作'"
|
||||||
|
: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 === 'appealStatus'">
|
||||||
|
<StatusTag
|
||||||
|
:value="scope.row.appealStatus"
|
||||||
|
:options="[{ label: '待审核', value: '0' }, { label: '通过', value: '1' }, { label: '驳回', value: '2' }]"
|
||||||
|
:type-map="{ '0': { type: 'warning', effect: 'light' }, '1': { type: 'success', effect: 'light' }, '2': { type: 'danger', effect: 'light' } }"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
v-if="!scope.row.appealStatus || scope.row.appealStatus === '0'"
|
||||||
|
icon="Edit"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="handleAudit(scope.row)">
|
||||||
|
审核
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="Delete"
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
@click="handleDelete(scope.row)">
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<template #empty>
|
||||||
|
<el-empty description="暂无数据" :image-size="120" />
|
||||||
|
</template>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<div class="pagination-wrapper">
|
||||||
|
<pagination
|
||||||
|
@size-change="sizeChangeHandle"
|
||||||
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
</div>
|
</div>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 表格 -->
|
|
||||||
<el-table
|
|
||||||
:data="state.dataList"
|
|
||||||
v-loading="state.loading"
|
|
||||||
border
|
|
||||||
:cell-style="tableStyle.cellStyle"
|
|
||||||
:header-cell-style="tableStyle.headerCellStyle"
|
|
||||||
@sort-change="sortChangeHandle">
|
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
v-for="col in visibleColumnsSorted"
|
|
||||||
:key="col.prop"
|
|
||||||
: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 === 'appealStatus'">
|
|
||||||
<StatusTag
|
|
||||||
:value="scope.row.appealStatus"
|
|
||||||
:options="[{ label: '待审核', value: '0' }, { label: '通过', value: '1' }, { label: '驳回', value: '2' }]"
|
|
||||||
:type-map="{ '0': { type: 'warning', effect: 'light' }, '1': { type: 'success', effect: 'light' }, '2': { type: 'danger', effect: 'light' } }"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><Setting /></el-icon>
|
|
||||||
<span style="margin-left: 4px">操作</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
v-if="!scope.row.appealStatus || scope.row.appealStatus === '0'"
|
|
||||||
icon="Edit"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleAudit(scope.row)">
|
|
||||||
审核
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Delete"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row)">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
|
||||||
<pagination
|
|
||||||
@size-change="sizeChangeHandle"
|
|
||||||
@current-change="currentChangeHandle"
|
|
||||||
v-bind="state.pagination" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 审核对话框 -->
|
<!-- 审核对话框 -->
|
||||||
@@ -219,13 +244,12 @@ import { useMessage, useMessageBox } from "/@/hooks/message";
|
|||||||
import { getDeptList, getClassListByRole } from '/@/api/basic/basicclass'
|
import { getDeptList, getClassListByRole } from '/@/api/basic/basicclass'
|
||||||
import { getTypeValue } from '/@/api/admin/dict'
|
import { getTypeValue } from '/@/api/admin/dict'
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import { List, Grid, UserFilled, Calendar, Tickets, DataAnalysis, Document, Warning, User, CircleCheck, EditPen, Setting, Menu } from '@element-plus/icons-vue'
|
import { List, Grid, UserFilled, Calendar, Tickets, DataAnalysis, Document, Warning, User, CircleCheck, EditPen, Setting, Menu, Search } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
import { defineAsyncComponent } from 'vue'
|
import { defineAsyncComponent } from 'vue'
|
||||||
const StatusTag = defineAsyncComponent(() => import('/@/components/StatusTag/index.vue'))
|
const StatusTag = defineAsyncComponent(() => import('/@/components/StatusTag/index.vue'))
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
|
||||||
const searchFormRef = ref()
|
const searchFormRef = ref()
|
||||||
const auditFormRef = ref()
|
const auditFormRef = ref()
|
||||||
const columnControlRef = ref()
|
const columnControlRef = ref()
|
||||||
@@ -252,94 +276,13 @@ const tableColumns = [
|
|||||||
{ prop: 'appealReply', label: '反馈意见', icon: EditPen }
|
{ prop: 'appealReply', label: '反馈意见', icon: EditPen }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制 Hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumnsSorted,
|
||||||
const columnOrder = ref<string[]>([])
|
checkColumnVisible,
|
||||||
|
handleColumnChange,
|
||||||
// 从本地统一存储加载配置
|
handleColumnOrderChange
|
||||||
const loadSavedConfig = () => {
|
} = useTableColumnControl(tableColumns)
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列变化处理
|
|
||||||
const handleColumnChange = (value: string[]) => {
|
|
||||||
visibleColumns.value = value
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = value.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列排序变化处理
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const visibleColumnsSorted = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: any[] = []
|
|
||||||
const unorderedColumns: any[] = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 审核表单
|
// 审核表单
|
||||||
const auditForm = reactive({
|
const auditForm = reactive({
|
||||||
@@ -510,10 +453,13 @@ const getAppealStatusListData = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
loadSavedConfig()
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getDeptListData()
|
getDeptListData()
|
||||||
getClassListData()
|
getClassListData()
|
||||||
getAppealStatusListData()
|
getAppealStatusListData()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,9 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form
|
||||||
|
:model="searchForm"
|
||||||
|
ref="searchFormRef"
|
||||||
|
:inline="true"
|
||||||
|
@keyup.enter="handleSearch"
|
||||||
|
class="search-form">
|
||||||
<el-form-item label="教师" prop="teacherNo">
|
<el-form-item label="教师" prop="teacherNo">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="searchForm.teacherNo"
|
v-model="searchForm.teacherNo"
|
||||||
@@ -27,99 +40,118 @@
|
|||||||
style="width: 200px" />
|
style="width: 200px" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" plain icon="Search" @click="handleSearch">查询</el-button>
|
<el-button type="primary" icon="Search" @click="handleSearch">查询</el-button>
|
||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="FolderAdd"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
@click="formDialogRef.openDialog()"
|
班主任履历列表
|
||||||
v-auth="'stuwork_classmasterresume_add'">
|
</span>
|
||||||
新 增
|
<div class="header-actions">
|
||||||
</el-button>
|
<el-button
|
||||||
<right-toolbar
|
icon="FolderAdd"
|
||||||
v-model:showSearch="showSearch"
|
type="primary"
|
||||||
:export="'stuwork_classmasterresume_export'"
|
@click="formDialogRef.openDialog()"
|
||||||
@exportExcel="exportExcel"
|
v-auth="'stuwork_classmasterresume_add'">
|
||||||
class="ml10 mr20"
|
新增
|
||||||
style="float: right;"
|
</el-button>
|
||||||
@queryTable="getDataList">
|
<right-toolbar
|
||||||
<TableColumnControl
|
v-model:showSearch="showSearch"
|
||||||
ref="columnControlRef"
|
:export="'stuwork_classmasterresume_export'"
|
||||||
:columns="tableColumns"
|
@exportExcel="exportExcel"
|
||||||
v-model="visibleColumns"
|
class="ml10"
|
||||||
trigger-type="default"
|
@queryTable="getDataList">
|
||||||
trigger-circle
|
<TableColumnControl
|
||||||
@change="handleColumnChange"
|
ref="columnControlRef"
|
||||||
@order-change="handleColumnOrderChange"
|
:columns="tableColumns"
|
||||||
>
|
v-model="visibleColumns"
|
||||||
<template #trigger>
|
trigger-type="default"
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
trigger-circle
|
||||||
<el-button circle style="margin-left: 0;">
|
@change="handleColumnChange"
|
||||||
<el-icon><Menu /></el-icon>
|
@order-change="handleColumnOrderChange"
|
||||||
</el-button>
|
>
|
||||||
</el-tooltip>
|
<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"
|
||||||
|
@sort-change="sortChangeHandle">
|
||||||
|
<el-table-column type="index" label="序号" 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 !== '操作'"
|
||||||
|
: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>
|
||||||
</TableColumnControl>
|
</el-table-column>
|
||||||
</right-toolbar>
|
</template>
|
||||||
|
<el-table-column label="操作" width="180" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
icon="View"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="handleViewDetail(scope.row)">
|
||||||
|
查看履历详情
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<template #empty>
|
||||||
|
<el-empty description="暂无数据" :image-size="120">
|
||||||
|
<el-button type="primary" icon="FolderAdd" @click="formDialogRef.openDialog()">新增履历</el-button>
|
||||||
|
</el-empty>
|
||||||
|
</template>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<div class="pagination-wrapper">
|
||||||
|
<pagination
|
||||||
|
@size-change="sizeChangeHandle"
|
||||||
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
</div>
|
</div>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 表格 -->
|
|
||||||
<el-table
|
|
||||||
:data="state.dataList"
|
|
||||||
v-loading="state.loading"
|
|
||||||
border
|
|
||||||
:cell-style="tableStyle.cellStyle"
|
|
||||||
:header-cell-style="tableStyle.headerCellStyle"
|
|
||||||
@sort-change="sortChangeHandle">
|
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
v-for="col in visibleColumnsSorted"
|
|
||||||
:key="col.prop"
|
|
||||||
: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>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><Setting /></el-icon>
|
|
||||||
<span style="margin-left: 4px">操作</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
icon="View"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleViewDetail(scope.row)">
|
|
||||||
查看履历详情
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
|
||||||
<pagination
|
|
||||||
@size-change="sizeChangeHandle"
|
|
||||||
@current-change="currentChangeHandle"
|
|
||||||
v-bind="state.pagination" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 编辑、新增 -->
|
<!-- 编辑、新增 -->
|
||||||
@@ -131,22 +163,19 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="ClassMasterResume">
|
<script setup lang="ts" name="ClassMasterResume">
|
||||||
import { ref, reactive, defineAsyncComponent, onMounted, nextTick, computed } from 'vue'
|
import { ref, reactive, defineAsyncComponent, onMounted, nextTick } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||||
import { fetchList } from "/@/api/stuwork/classmasterresume";
|
import { fetchList } from "/@/api/stuwork/classmasterresume";
|
||||||
import request from "/@/utils/request";
|
import request from "/@/utils/request";
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
|
import { List, User, CreditCard, Phone, Setting, Menu, Search, Document } from '@element-plus/icons-vue'
|
||||||
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
|
||||||
// 引入组件
|
// 引入组件
|
||||||
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
||||||
// 尝试直接导入看看是否能解决问题
|
|
||||||
import DetailDialog from './detail.vue';
|
import DetailDialog from './detail.vue';
|
||||||
import { List, User, CreditCard, Phone, Setting, Menu } from '@element-plus/icons-vue'
|
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
|
||||||
const formDialogRef = ref()
|
const formDialogRef = ref()
|
||||||
const searchFormRef = ref()
|
const searchFormRef = ref()
|
||||||
const detailDialogRef = ref()
|
const detailDialogRef = ref()
|
||||||
@@ -163,94 +192,14 @@ const tableColumns = [
|
|||||||
{ prop: 'telPhone', label: '联系方式', icon: Phone }
|
{ prop: 'telPhone', label: '联系方式', icon: Phone }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制 Hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列变化处理
|
|
||||||
const handleColumnChange = (value: string[]) => {
|
|
||||||
visibleColumns.value = value
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = value.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列排序变化处理
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const visibleColumnsSorted = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: any[] = []
|
|
||||||
const unorderedColumns: any[] = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 搜索表单
|
// 搜索表单
|
||||||
const searchForm = reactive({
|
const searchForm = reactive({
|
||||||
@@ -265,7 +214,8 @@ const state: BasicTableProps = reactive<BasicTableProps>({
|
|||||||
props: {
|
props: {
|
||||||
item: 'records',
|
item: 'records',
|
||||||
totalCount: 'total'
|
totalCount: 'total'
|
||||||
}
|
},
|
||||||
|
createdIsNeed: true
|
||||||
})
|
})
|
||||||
|
|
||||||
// table hook
|
// table hook
|
||||||
@@ -297,7 +247,7 @@ const handleSearch = () => {
|
|||||||
|
|
||||||
// 重置
|
// 重置
|
||||||
const handleReset = () => {
|
const handleReset = () => {
|
||||||
searchFormRef.value?.formRef?.resetFields()
|
searchFormRef.value?.resetFields()
|
||||||
searchForm.teacherNo = ''
|
searchForm.teacherNo = ''
|
||||||
searchForm.telPhone = ''
|
searchForm.telPhone = ''
|
||||||
getDataList()
|
getDataList()
|
||||||
@@ -314,7 +264,6 @@ const exportExcel = () => {
|
|||||||
|
|
||||||
// 查看履历详情
|
// 查看履历详情
|
||||||
const handleViewDetail = async (row: any) => {
|
const handleViewDetail = async (row: any) => {
|
||||||
|
|
||||||
if (!row.teacherNo) {
|
if (!row.teacherNo) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -324,17 +273,15 @@ const handleViewDetail = async (row: any) => {
|
|||||||
|
|
||||||
if (detailDialogRef.value && typeof detailDialogRef.value.openDialog === 'function') {
|
if (detailDialogRef.value && typeof detailDialogRef.value.openDialog === 'function') {
|
||||||
detailDialogRef.value.openDialog(row.teacherNo)
|
detailDialogRef.value.openDialog(row.teacherNo)
|
||||||
} else {
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
loadSavedConfig()
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getTeacherList()
|
getTeacherList()
|
||||||
// 调试:检查组件是否已挂载
|
|
||||||
nextTick(() => {
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="state.queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</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-form-item label="学年" prop="schoolYear">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="state.queryForm.schoolYear"
|
v-model="state.queryForm.schoolYear"
|
||||||
@@ -89,23 +97,27 @@
|
|||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="Plus"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
class="ml10"
|
班级论文/案例列表
|
||||||
@click="formDialogRef.openDialog()">
|
</span>
|
||||||
新增
|
<div class="header-actions">
|
||||||
</el-button>
|
<el-button
|
||||||
<right-toolbar
|
icon="Plus"
|
||||||
v-model:showSearch="showSearch"
|
type="primary"
|
||||||
class="ml10"
|
@click="formDialogRef.openDialog()">
|
||||||
style="float: right;"
|
新增
|
||||||
@queryTable="getDataList">
|
</el-button>
|
||||||
|
<right-toolbar
|
||||||
|
v-model:showSearch="showSearch"
|
||||||
|
class="ml10"
|
||||||
|
@queryTable="getDataList">
|
||||||
<TableColumnControl
|
<TableColumnControl
|
||||||
ref="columnControlRef"
|
ref="columnControlRef"
|
||||||
:columns="tableColumns"
|
:columns="tableColumns"
|
||||||
@@ -124,34 +136,39 @@
|
|||||||
</template>
|
</template>
|
||||||
</TableColumnControl>
|
</TableColumnControl>
|
||||||
</right-toolbar>
|
</right-toolbar>
|
||||||
</div>
|
</div>
|
||||||
</el-row>
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table
|
<el-table
|
||||||
:data="state.dataList"
|
:data="state.dataList"
|
||||||
v-loading="state.loading"
|
v-loading="state.loading"
|
||||||
border
|
stripe
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle">
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
class="modern-table">
|
||||||
<template #header>
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<template v-for="col in sortedTableColumns" :key="col.prop || col.label">
|
|
||||||
<el-table-column
|
|
||||||
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
|
||||||
:prop="col.prop"
|
|
||||||
:label="col.label"
|
|
||||||
show-overflow-tooltip
|
|
||||||
align="center"
|
|
||||||
:min-width="col.minWidth"
|
|
||||||
:width="col.width">
|
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
<el-icon><List /></el-icon>
|
||||||
<span style="margin-left: 4px">{{ col.label }}</span>
|
|
||||||
</template>
|
</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 !== '操作'"
|
||||||
|
: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 v-if="col.prop === 'schoolTerm'" #default="scope">
|
<template v-if="col.prop === 'schoolTerm'" #default="scope">
|
||||||
<el-tag size="small" type="primary" effect="plain">
|
<el-tag size="small" type="primary" effect="plain">
|
||||||
@@ -183,46 +200,56 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</template>
|
</template>
|
||||||
<el-table-column label="操作" width="250" align="center" fixed="right">
|
<el-table-column label="操作" width="250" align="center" fixed="right">
|
||||||
<template #default="scope">
|
<template #header>
|
||||||
<el-button
|
<el-icon><Setting /></el-icon>
|
||||||
icon="View"
|
<span style="margin-left: 4px">操作</span>
|
||||||
text
|
</template>
|
||||||
type="primary"
|
<template #default="scope">
|
||||||
@click="handleView(scope.row)">
|
<el-button
|
||||||
查看
|
icon="View"
|
||||||
</el-button>
|
link
|
||||||
<el-button
|
type="primary"
|
||||||
icon="Edit"
|
@click="handleView(scope.row)">
|
||||||
text
|
查看
|
||||||
type="primary"
|
</el-button>
|
||||||
@click="handleEdit(scope.row)">
|
<el-button
|
||||||
编辑
|
icon="Edit"
|
||||||
</el-button>
|
link
|
||||||
<el-button
|
type="primary"
|
||||||
v-if="scope.row.isAddScore !== '1'"
|
@click="handleEdit(scope.row)">
|
||||||
icon="Plus"
|
编辑
|
||||||
text
|
</el-button>
|
||||||
type="success"
|
<el-button
|
||||||
@click="handleAddScore(scope.row)">
|
v-if="scope.row.isAddScore !== '1'"
|
||||||
加分
|
icon="Plus"
|
||||||
</el-button>
|
link
|
||||||
<el-button
|
type="success"
|
||||||
icon="Delete"
|
@click="handleAddScore(scope.row)">
|
||||||
text
|
加分
|
||||||
type="danger"
|
</el-button>
|
||||||
@click="handleDelete(scope.row)">
|
<el-button
|
||||||
删除
|
icon="Delete"
|
||||||
</el-button>
|
link
|
||||||
|
type="danger"
|
||||||
|
@click="handleDelete(scope.row)">
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<template #empty>
|
||||||
|
<el-empty description="暂无数据" :image-size="120" />
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table>
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<pagination
|
<div class="pagination-wrapper">
|
||||||
@size-change="sizeChangeHandle"
|
<pagination
|
||||||
@current-change="currentChangeHandle"
|
@size-change="sizeChangeHandle"
|
||||||
v-bind="state.pagination" />
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 新增/编辑表单弹窗 -->
|
<!-- 新增/编辑表单弹窗 -->
|
||||||
@@ -234,8 +261,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="ClassPaper">
|
<script setup lang="ts" name="ClassPaper">
|
||||||
import { reactive, ref, onMounted, computed, nextTick } from 'vue'
|
import { reactive, ref, onMounted } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||||
import { fetchList, delObj, addScore } from "/@/api/stuwork/classpaper";
|
import { fetchList, delObj, addScore } from "/@/api/stuwork/classpaper";
|
||||||
import { getDeptList } from "/@/api/basic/basicclass";
|
import { getDeptList } from "/@/api/basic/basicclass";
|
||||||
@@ -246,11 +272,10 @@ import { useMessage, useMessageBox } from "/@/hooks/message";
|
|||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import FormDialog from './form.vue'
|
import FormDialog from './form.vue'
|
||||||
import DetailDialog from './detail.vue'
|
import DetailDialog from './detail.vue'
|
||||||
import { List, Document, Calendar, Clock, OfficeBuilding, Grid, UserFilled, Collection, Setting, Menu } from '@element-plus/icons-vue'
|
import { List, Document, Calendar, Clock, OfficeBuilding, Grid, UserFilled, Collection, Setting, Menu, Search } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
|
||||||
const formDialogRef = ref()
|
const formDialogRef = ref()
|
||||||
const columnControlRef = ref<any>()
|
const columnControlRef = ref<any>()
|
||||||
const detailDialogRef = ref()
|
const detailDialogRef = ref()
|
||||||
@@ -267,132 +292,27 @@ const typeList = ref<any[]>([
|
|||||||
|
|
||||||
// 表格列配置
|
// 表格列配置
|
||||||
const tableColumns = [
|
const tableColumns = [
|
||||||
{ prop: 'title', label: '标题', minWidth: 200 },
|
{ prop: 'title', label: '标题', icon: Document, minWidth: 200 },
|
||||||
{ prop: 'schoolYear', label: '学年' },
|
{ prop: 'schoolYear', label: '学年', icon: Calendar },
|
||||||
{ prop: 'schoolTerm', label: '学期' },
|
{ prop: 'schoolTerm', label: '学期', icon: Clock },
|
||||||
{ prop: 'deptName', label: '学院' },
|
{ prop: 'deptName', label: '学院', icon: OfficeBuilding },
|
||||||
{ prop: 'classNo', label: '班号' },
|
{ prop: 'classNo', label: '班号', icon: Grid },
|
||||||
{ prop: 'teacherRealName', label: '班主任' },
|
{ prop: 'teacherRealName', label: '班主任', icon: UserFilled },
|
||||||
{ prop: 'type', label: '类型' },
|
{ prop: 'type', label: '类型', icon: Collection },
|
||||||
{ prop: 'journal', label: '发表刊物', minWidth: 150 },
|
{ prop: 'journal', label: '发表刊物', icon: Document, minWidth: 150 },
|
||||||
{ prop: 'page', label: '发表页码' },
|
{ prop: 'page', label: '发表页码', icon: Document },
|
||||||
{ prop: 'isAddScore', label: '加分' },
|
{ prop: 'isAddScore', label: '加分', icon: Document },
|
||||||
{ prop: 'attachment', label: '附件', width: 100 }
|
{ prop: 'attachment', label: '附件', icon: Document, width: 100 }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 列配置映射(用于图标)
|
// 使用表格列控制 Hook
|
||||||
const columnConfigMap: Record<string, { icon: any }> = {
|
const {
|
||||||
title: { icon: Document },
|
visibleColumns,
|
||||||
schoolYear: { icon: Calendar },
|
visibleColumnsSorted,
|
||||||
schoolTerm: { icon: Clock },
|
checkColumnVisible,
|
||||||
deptName: { icon: OfficeBuilding },
|
handleColumnChange,
|
||||||
classNo: { icon: Grid },
|
handleColumnOrderChange
|
||||||
teacherRealName: { icon: UserFilled },
|
} = useTableColumnControl(tableColumns)
|
||||||
type: { icon: Collection },
|
|
||||||
journal: { icon: Document },
|
|
||||||
page: { icon: Document },
|
|
||||||
isAddScore: { icon: Document },
|
|
||||||
attachment: { icon: Document }
|
|
||||||
}
|
|
||||||
|
|
||||||
// 当前显示的列
|
|
||||||
const visibleColumns = ref<string[]>([])
|
|
||||||
// 列排序顺序
|
|
||||||
const columnOrder = ref<string[]>([])
|
|
||||||
|
|
||||||
// 从本地统一存储加载配置
|
|
||||||
const loadSavedConfig = () => {
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadSavedConfig()
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const sortedTableColumns = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: typeof tableColumns = []
|
|
||||||
const unorderedColumns: typeof tableColumns = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 列显示控制函数
|
|
||||||
const checkColumnVisible = (prop: string): boolean => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return visibleColumns.value.includes(prop)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列变化
|
|
||||||
const handleColumnChange = (columns: string[]) => {
|
|
||||||
visibleColumns.value = columns
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = columns.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列排序变化
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 配置 useTable
|
// 配置 useTable
|
||||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||||
@@ -568,13 +488,9 @@ onMounted(() => {
|
|||||||
getSchoolTermDict()
|
getSchoolTermDict()
|
||||||
getDeptListData()
|
getDeptListData()
|
||||||
getClassListData()
|
getClassListData()
|
||||||
nextTick(() => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
loadSavedConfig()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -151,10 +151,10 @@
|
|||||||
<el-icon><List /></el-icon>
|
<el-icon><List /></el-icon>
|
||||||
</template>
|
</template>
|
||||||
<template #default="{ $index }">
|
<template #default="{ $index }">
|
||||||
<el-tag size="small" effect="plain" type="info">{{ $index + 1 + (state.pagination.current - 1) * state.pagination.size }}</el-tag>
|
{{ $index + 1 + (state.pagination.current - 1) * state.pagination.size }}
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<template v-for="col in sortedTableColumns" :key="col.prop || col.label">
|
<template v-for="col in visibleColumnsSorted" :key="col.prop || col.label">
|
||||||
<el-table-column
|
<el-table-column
|
||||||
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
||||||
:prop="col.prop"
|
:prop="col.prop"
|
||||||
@@ -169,7 +169,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<!-- 状态列特殊模板 -->
|
<!-- 状态列特殊模板 -->
|
||||||
<template v-if="col.prop === 'status'" #default="scope">
|
<template v-if="col.prop === 'status'" #default="scope">
|
||||||
<el-tag size="small" :type="scope.row.status === '1' ? 'success' : 'warning'" effect="plain" round>
|
<el-tag size="small" :type="getStatusTagType(scope.row.status)" effect="plain" round>
|
||||||
{{ formatStatus(scope.row.status) }}
|
{{ formatStatus(scope.row.status) }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</template>
|
</template>
|
||||||
@@ -243,7 +243,8 @@ import { parseTime } from "/@/utils/formatTime";
|
|||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import FormDialog from './form.vue'
|
import FormDialog from './form.vue'
|
||||||
import { List, OfficeBuilding, Calendar, CircleCheck, Clock, Grid, Document, User, Setting, Menu, Search, EditPen } from '@element-plus/icons-vue'
|
import { List, OfficeBuilding, Calendar, CircleCheck, Clock, Grid, Document, User, Setting, Menu, Search, EditPen } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
import '/@/styles/modern-page.scss'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -281,104 +282,14 @@ const columnConfigMap: Record<string, { icon: any }> = {
|
|||||||
createBy: { icon: User }
|
createBy: { icon: User }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadSavedConfig()
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const sortedTableColumns = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: typeof tableColumns = []
|
|
||||||
const unorderedColumns: typeof tableColumns = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 列显示控制函数
|
|
||||||
const checkColumnVisible = (prop: string): boolean => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return visibleColumns.value.includes(prop)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列变化
|
|
||||||
const handleColumnChange = (columns: string[]) => {
|
|
||||||
visibleColumns.value = columns
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = columns.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列排序变化
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 配置 useTable
|
// 配置 useTable
|
||||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||||
@@ -421,6 +332,25 @@ const formatStatus = (value: string) => {
|
|||||||
return item ? item.label : value
|
return item ? item.label : value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取状态标签类型
|
||||||
|
const getStatusTagType = (value: string) => {
|
||||||
|
if (!value) return 'info'
|
||||||
|
const item = statusList.value.find((item: any) => item.value === value)
|
||||||
|
if (!item) return 'info'
|
||||||
|
|
||||||
|
const label = item.label || item.dictLabel || item.name || ''
|
||||||
|
// 已提交用蓝色
|
||||||
|
if (label.includes('已提交') || label.includes('提交')) {
|
||||||
|
return 'primary'
|
||||||
|
}
|
||||||
|
// 审核通过用绿色
|
||||||
|
if (label.includes('审核通过') || label.includes('通过')) {
|
||||||
|
return 'success'
|
||||||
|
}
|
||||||
|
// 其他状态默认用灰色
|
||||||
|
return 'info'
|
||||||
|
}
|
||||||
|
|
||||||
// 重置
|
// 重置
|
||||||
const handleReset = () => {
|
const handleReset = () => {
|
||||||
searchFormRef.value?.resetFields()
|
searchFormRef.value?.resetFields()
|
||||||
@@ -537,7 +467,6 @@ onMounted(() => {
|
|||||||
getStatusDict()
|
getStatusDict()
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (visibleColumns.value.length === 0) {
|
if (visibleColumns.value.length === 0) {
|
||||||
loadSavedConfig()
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="state.queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</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-form-item label="学年" prop="schoolYear">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="state.queryForm.schoolYear"
|
v-model="state.queryForm.schoolYear"
|
||||||
@@ -69,23 +77,27 @@
|
|||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="Plus"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
class="ml10"
|
班级宣传列表
|
||||||
@click="formDialogRef.openDialog()">
|
</span>
|
||||||
新增
|
<div class="header-actions">
|
||||||
</el-button>
|
<el-button
|
||||||
<right-toolbar
|
icon="Plus"
|
||||||
v-model:showSearch="showSearch"
|
type="primary"
|
||||||
class="ml10"
|
@click="formDialogRef.openDialog()">
|
||||||
style="float: right;"
|
新增
|
||||||
@queryTable="getDataList">
|
</el-button>
|
||||||
|
<right-toolbar
|
||||||
|
v-model:showSearch="showSearch"
|
||||||
|
class="ml10"
|
||||||
|
@queryTable="getDataList">
|
||||||
<TableColumnControl
|
<TableColumnControl
|
||||||
ref="columnControlRef"
|
ref="columnControlRef"
|
||||||
:columns="tableColumns"
|
:columns="tableColumns"
|
||||||
@@ -104,34 +116,39 @@
|
|||||||
</template>
|
</template>
|
||||||
</TableColumnControl>
|
</TableColumnControl>
|
||||||
</right-toolbar>
|
</right-toolbar>
|
||||||
</div>
|
</div>
|
||||||
</el-row>
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table
|
<el-table
|
||||||
:data="state.dataList"
|
:data="state.dataList"
|
||||||
v-loading="state.loading"
|
v-loading="state.loading"
|
||||||
border
|
stripe
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle">
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
class="modern-table">
|
||||||
<template #header>
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<template v-for="col in sortedTableColumns" :key="col.prop || col.label">
|
|
||||||
<el-table-column
|
|
||||||
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
|
||||||
:prop="col.prop"
|
|
||||||
:label="col.label"
|
|
||||||
show-overflow-tooltip
|
|
||||||
align="center"
|
|
||||||
:min-width="col.minWidth"
|
|
||||||
:width="col.width">
|
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
<el-icon><List /></el-icon>
|
||||||
<span style="margin-left: 4px">{{ col.label }}</span>
|
|
||||||
</template>
|
</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 !== '操作'"
|
||||||
|
: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 v-if="col.prop === 'schoolTerm'" #default="scope">
|
<template v-if="col.prop === 'schoolTerm'" #default="scope">
|
||||||
<el-tag size="small" type="primary" effect="plain">
|
<el-tag size="small" type="primary" effect="plain">
|
||||||
@@ -158,57 +175,63 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</template>
|
</template>
|
||||||
<el-table-column label="操作" width="300" align="center" fixed="right">
|
<el-table-column label="操作" width="300" align="center" fixed="right">
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><Setting /></el-icon>
|
<el-icon><Setting /></el-icon>
|
||||||
<span style="margin-left: 4px">操作</span>
|
<span style="margin-left: 4px">操作</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
icon="View"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="handleView(scope.row)">
|
||||||
|
查看
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
v-if="scope.row.isAddScore !== '1'"
|
||||||
|
icon="Plus"
|
||||||
|
link
|
||||||
|
type="success"
|
||||||
|
@click="handleAddScore(scope.row)">
|
||||||
|
加分
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="Folder"
|
||||||
|
link
|
||||||
|
type="warning"
|
||||||
|
@click="handleBelong(scope.row)">
|
||||||
|
归档
|
||||||
|
</el-button>
|
||||||
|
<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>
|
||||||
|
<template #empty>
|
||||||
|
<el-empty description="暂无数据" :image-size="120" />
|
||||||
</template>
|
</template>
|
||||||
<template #default="scope">
|
</el-table>
|
||||||
<el-button
|
|
||||||
icon="View"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleView(scope.row)">
|
|
||||||
查看
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
v-if="scope.row.isAddScore !== '1'"
|
|
||||||
icon="Plus"
|
|
||||||
text
|
|
||||||
type="success"
|
|
||||||
@click="handleAddScore(scope.row)">
|
|
||||||
加分
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Folder"
|
|
||||||
text
|
|
||||||
type="warning"
|
|
||||||
@click="handleBelong(scope.row)">
|
|
||||||
归档
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Edit"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleEdit(scope.row)">
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Delete"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row)">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<pagination
|
<div class="pagination-wrapper">
|
||||||
@size-change="sizeChangeHandle"
|
<pagination
|
||||||
@current-change="currentChangeHandle"
|
@size-change="sizeChangeHandle"
|
||||||
v-bind="state.pagination" />
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 新增/编辑表单弹窗 -->
|
<!-- 新增/编辑表单弹窗 -->
|
||||||
@@ -220,8 +243,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="ClassPublicity">
|
<script setup lang="ts" name="ClassPublicity">
|
||||||
import { reactive, ref, onMounted, computed, nextTick } from 'vue'
|
import { reactive, ref, onMounted } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||||
import { fetchList, delObj, addScore } from "/@/api/stuwork/classpublicity";
|
import { fetchList, delObj, addScore } from "/@/api/stuwork/classpublicity";
|
||||||
import { getDeptList } from "/@/api/basic/basicclass";
|
import { getDeptList } from "/@/api/basic/basicclass";
|
||||||
@@ -233,13 +255,12 @@ import { parseTime } from "/@/utils/formatTime";
|
|||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import FormDialog from './form.vue'
|
import FormDialog from './form.vue'
|
||||||
import BelongDialog from './belong.vue'
|
import BelongDialog from './belong.vue'
|
||||||
import { List, Calendar, Clock, OfficeBuilding, Grid, Document, User, Collection, Plus, Setting, Menu } from '@element-plus/icons-vue'
|
import { List, Calendar, Clock, OfficeBuilding, Grid, Document, User, Collection, Plus, Setting, Menu, Search } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
import { defineAsyncComponent } from 'vue'
|
import { defineAsyncComponent } from 'vue'
|
||||||
const StatusTag = defineAsyncComponent(() => import('/@/components/StatusTag/index.vue'))
|
const StatusTag = defineAsyncComponent(() => import('/@/components/StatusTag/index.vue'))
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
|
||||||
const formDialogRef = ref()
|
const formDialogRef = ref()
|
||||||
const columnControlRef = ref<any>()
|
const columnControlRef = ref<any>()
|
||||||
const belongDialogRef = ref()
|
const belongDialogRef = ref()
|
||||||
@@ -253,128 +274,24 @@ const belongList = ref<any[]>([])
|
|||||||
|
|
||||||
// 表格列配置
|
// 表格列配置
|
||||||
const tableColumns = [
|
const tableColumns = [
|
||||||
{ prop: 'schoolYear', label: '学年' },
|
{ prop: 'schoolYear', label: '学年', icon: Calendar },
|
||||||
{ prop: 'schoolTerm', label: '学期' },
|
{ prop: 'schoolTerm', label: '学期', icon: Clock },
|
||||||
{ prop: 'deptName', label: '学院' },
|
{ prop: 'deptName', label: '学院', icon: OfficeBuilding },
|
||||||
{ prop: 'classNo', label: '班号' },
|
{ prop: 'classNo', label: '班号', icon: Grid },
|
||||||
{ prop: 'title', label: '标题', minWidth: 200 },
|
{ prop: 'title', label: '标题', icon: Document, minWidth: 200 },
|
||||||
{ prop: 'author', label: '作者' },
|
{ prop: 'author', label: '作者', icon: User },
|
||||||
{ prop: 'updateTime', label: '更新时间', width: 180 },
|
{ prop: 'updateTime', label: '更新时间', icon: Clock, width: 180 },
|
||||||
{ prop: 'belong', label: '归档级别' },
|
{ prop: 'belong', label: '归档级别', icon: Collection },
|
||||||
{ prop: 'isAddScore', label: '加分' }
|
{ prop: 'isAddScore', label: '加分', icon: Plus }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 列配置映射(用于图标)
|
// 使用表格列控制 Hook
|
||||||
const columnConfigMap: Record<string, { icon: any }> = {
|
const {
|
||||||
schoolYear: { icon: Calendar },
|
visibleColumnsSorted,
|
||||||
schoolTerm: { icon: Clock },
|
checkColumnVisible,
|
||||||
deptName: { icon: OfficeBuilding },
|
handleColumnChange,
|
||||||
classNo: { icon: Grid },
|
handleColumnOrderChange
|
||||||
title: { icon: Document },
|
} = useTableColumnControl(tableColumns)
|
||||||
author: { icon: User },
|
|
||||||
updateTime: { icon: Clock },
|
|
||||||
belong: { icon: Collection },
|
|
||||||
isAddScore: { icon: Plus }
|
|
||||||
}
|
|
||||||
|
|
||||||
// 当前显示的列
|
|
||||||
const visibleColumns = ref<string[]>([])
|
|
||||||
// 列排序顺序
|
|
||||||
const columnOrder = ref<string[]>([])
|
|
||||||
|
|
||||||
// 从本地统一存储加载配置
|
|
||||||
const loadSavedConfig = () => {
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadSavedConfig()
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const sortedTableColumns = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: typeof tableColumns = []
|
|
||||||
const unorderedColumns: typeof tableColumns = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 列显示控制函数
|
|
||||||
const checkColumnVisible = (prop: string): boolean => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return visibleColumns.value.includes(prop)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列变化
|
|
||||||
const handleColumnChange = (columns: string[]) => {
|
|
||||||
visibleColumns.value = columns
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = columns.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列排序变化
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 配置 useTable
|
// 配置 useTable
|
||||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||||
@@ -566,14 +483,10 @@ onMounted(() => {
|
|||||||
getDeptListData()
|
getDeptListData()
|
||||||
getClassListData()
|
getClassListData()
|
||||||
getBelongDict()
|
getBelongDict()
|
||||||
nextTick(() => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
loadSavedConfig()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch" class="search-form">
|
||||||
<el-form-item label="学院" prop="deptName">
|
<el-form-item label="学院" prop="deptName">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="searchForm.deptName"
|
v-model="searchForm.deptName"
|
||||||
@@ -60,30 +68,34 @@
|
|||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="Download"
|
<span class="card-title">
|
||||||
type="success"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
class="ml10"
|
教室安排及公物管理列表
|
||||||
@click="handleExport">
|
</span>
|
||||||
导出
|
<div class="header-actions">
|
||||||
</el-button>
|
<el-button
|
||||||
<el-button
|
icon="Download"
|
||||||
icon="Refresh"
|
type="success"
|
||||||
type="primary"
|
@click="handleExport">
|
||||||
class="ml10"
|
导出
|
||||||
@click="handleSync">
|
</el-button>
|
||||||
同步教室安排
|
<el-button
|
||||||
</el-button>
|
icon="Refresh"
|
||||||
<right-toolbar
|
type="primary"
|
||||||
v-model:showSearch="showSearch"
|
class="ml10"
|
||||||
class="ml10"
|
@click="handleSync">
|
||||||
style="float: right;"
|
同步教室安排
|
||||||
@queryTable="getDataList">
|
</el-button>
|
||||||
|
<right-toolbar
|
||||||
|
v-model:showSearch="showSearch"
|
||||||
|
class="ml10"
|
||||||
|
@queryTable="getDataList">
|
||||||
<TableColumnControl
|
<TableColumnControl
|
||||||
ref="columnControlRef"
|
ref="columnControlRef"
|
||||||
:columns="tableColumns"
|
:columns="tableColumns"
|
||||||
@@ -102,33 +114,40 @@
|
|||||||
</template>
|
</template>
|
||||||
</TableColumnControl>
|
</TableColumnControl>
|
||||||
</right-toolbar>
|
</right-toolbar>
|
||||||
</div>
|
</div>
|
||||||
</el-row>
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table
|
<el-table
|
||||||
:data="state.dataList"
|
:data="state.dataList"
|
||||||
v-loading="state.loading"
|
v-loading="state.loading"
|
||||||
border
|
stripe
|
||||||
row-key="id"
|
row-key="id"
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle">
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
class="modern-table">
|
||||||
<template #header>
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<template v-for="col in sortedTableColumns" :key="col.prop || col.label">
|
|
||||||
<el-table-column
|
|
||||||
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
|
||||||
:prop="col.prop"
|
|
||||||
:label="col.label"
|
|
||||||
show-overflow-tooltip
|
|
||||||
align="center">
|
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
<el-icon><List /></el-icon>
|
||||||
<span style="margin-left: 4px">{{ col.label }}</span>
|
|
||||||
</template>
|
</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 !== '操作'"
|
||||||
|
: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 v-if="col.prop === 'classStatus'" #default="scope">
|
<template v-if="col.prop === 'classStatus'" #default="scope">
|
||||||
<el-tag size="small" type="info" effect="plain">
|
<el-tag size="small" type="info" effect="plain">
|
||||||
@@ -162,24 +181,34 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</template>
|
</template>
|
||||||
<el-table-column label="操作" width="120" align="center" fixed="right">
|
<el-table-column label="操作" width="120" align="center" fixed="right">
|
||||||
<template #default="scope">
|
<template #header>
|
||||||
<el-button
|
<el-icon><Setting /></el-icon>
|
||||||
icon="Setting"
|
<span style="margin-left: 4px">操作</span>
|
||||||
text
|
</template>
|
||||||
type="primary"
|
<template #default="scope">
|
||||||
@click="handleArrange(scope.row)">
|
<el-button
|
||||||
教室安排
|
icon="Setting"
|
||||||
</el-button>
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="handleArrange(scope.row)">
|
||||||
|
教室安排
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<template #empty>
|
||||||
|
<el-empty description="暂无数据" :image-size="120" />
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table>
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<pagination
|
<div class="pagination-wrapper">
|
||||||
@size-change="sizeChangeHandle"
|
<pagination
|
||||||
@current-change="currentChangeHandle"
|
@size-change="sizeChangeHandle"
|
||||||
v-bind="state.pagination" />
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 教室安排表单弹窗 -->
|
<!-- 教室安排表单弹窗 -->
|
||||||
@@ -188,8 +217,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="ClassroomBase">
|
<script setup lang="ts" name="ClassroomBase">
|
||||||
import { reactive, ref, onMounted, computed, nextTick } from 'vue'
|
import { reactive, ref, onMounted } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||||
import { fetchList, exportData, syncClassroomArrangement } from "/@/api/stuwork/classroombase";
|
import { fetchList, exportData, syncClassroomArrangement } from "/@/api/stuwork/classroombase";
|
||||||
import { getDeptListByLevelTwo } from "/@/api/basic/basicdept";
|
import { getDeptListByLevelTwo } from "/@/api/basic/basicdept";
|
||||||
@@ -198,11 +226,10 @@ import { getDicts } from "/@/api/admin/dict";
|
|||||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import ArrangeDialog from './arrange.vue'
|
import ArrangeDialog from './arrange.vue'
|
||||||
import { List, OfficeBuilding, CircleCheck, Location, UserFilled, Collection, Setting, Menu, Calendar } from '@element-plus/icons-vue'
|
import { List, OfficeBuilding, CircleCheck, Location, UserFilled, Collection, Setting, Menu, Calendar, Search, Document } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
|
||||||
const searchFormRef = ref()
|
const searchFormRef = ref()
|
||||||
const columnControlRef = ref<any>()
|
const columnControlRef = ref<any>()
|
||||||
const showSearch = ref(true)
|
const showSearch = ref(true)
|
||||||
@@ -216,134 +243,27 @@ const arrangeDialogRef = ref()
|
|||||||
|
|
||||||
// 表格列配置
|
// 表格列配置
|
||||||
const tableColumns = [
|
const tableColumns = [
|
||||||
{ prop: 'buildingNo', label: '楼号' },
|
{ prop: 'buildingNo', label: '楼号', icon: OfficeBuilding },
|
||||||
{ prop: 'deptName', label: '学院' },
|
{ prop: 'deptName', label: '学院', icon: OfficeBuilding },
|
||||||
{ prop: 'classStatus', label: '班级状态' },
|
{ prop: 'classStatus', label: '班级状态', icon: CircleCheck },
|
||||||
{ prop: 'position', label: '教室位置' },
|
{ prop: 'position', label: '教室位置', icon: Location },
|
||||||
{ prop: 'stuNum', label: '人数' },
|
{ prop: 'stuNum', label: '人数', icon: UserFilled },
|
||||||
{ prop: 'teacherRealName', label: '班主任' },
|
{ prop: 'teacherRealName', label: '班主任', icon: UserFilled },
|
||||||
{ prop: 'platformType', label: '讲台类型' },
|
{ prop: 'platformType', label: '讲台类型', icon: Collection },
|
||||||
{ prop: 'tyType', label: '投影类型' },
|
{ prop: 'tyType', label: '投影类型', icon: Collection },
|
||||||
{ prop: 'tvType', label: '电视机' },
|
{ prop: 'tvType', label: '电视机', icon: Collection },
|
||||||
{ prop: 'chairCnt', label: '方凳数量' },
|
{ prop: 'chairCnt', label: '方凳数量', icon: Calendar },
|
||||||
{ prop: 'tableCnt', label: '课桌数量' },
|
{ prop: 'tableCnt', label: '课桌数量', icon: Calendar },
|
||||||
{ prop: 'password', label: '锁密码' }
|
{ prop: 'password', label: '锁密码', icon: Calendar }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 列配置映射(用于图标)
|
// 使用表格列控制 Hook
|
||||||
const columnConfigMap: Record<string, { icon: any }> = {
|
const {
|
||||||
buildingNo: { icon: OfficeBuilding },
|
visibleColumnsSorted,
|
||||||
deptName: { icon: OfficeBuilding },
|
checkColumnVisible,
|
||||||
classStatus: { icon: CircleCheck },
|
handleColumnChange,
|
||||||
position: { icon: Location },
|
handleColumnOrderChange
|
||||||
stuNum: { icon: UserFilled },
|
} = useTableColumnControl(tableColumns)
|
||||||
teacherRealName: { icon: UserFilled },
|
|
||||||
platformType: { icon: Collection },
|
|
||||||
tyType: { icon: Collection },
|
|
||||||
tvType: { icon: Collection },
|
|
||||||
chairCnt: { icon: Calendar },
|
|
||||||
tableCnt: { icon: Calendar },
|
|
||||||
password: { icon: Calendar }
|
|
||||||
}
|
|
||||||
|
|
||||||
// 当前显示的列
|
|
||||||
const visibleColumns = ref<string[]>([])
|
|
||||||
// 列排序顺序
|
|
||||||
const columnOrder = ref<string[]>([])
|
|
||||||
|
|
||||||
// 从本地统一存储加载配置
|
|
||||||
const loadSavedConfig = () => {
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadSavedConfig()
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const sortedTableColumns = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: typeof tableColumns = []
|
|
||||||
const unorderedColumns: typeof tableColumns = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 列显示控制函数
|
|
||||||
const checkColumnVisible = (prop: string): boolean => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return visibleColumns.value.includes(prop)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列变化
|
|
||||||
const handleColumnChange = (columns: string[]) => {
|
|
||||||
visibleColumns.value = columns
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = columns.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列排序变化
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 班级状态列表
|
// 班级状态列表
|
||||||
const classStatusListData = [
|
const classStatusListData = [
|
||||||
@@ -573,14 +493,10 @@ onMounted(() => {
|
|||||||
getPlatformTypeDict()
|
getPlatformTypeDict()
|
||||||
getTyTypeDict()
|
getTyTypeDict()
|
||||||
getTvTypeDict()
|
getTvTypeDict()
|
||||||
nextTick(() => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
loadSavedConfig()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form
|
||||||
|
:model="searchForm"
|
||||||
|
ref="searchFormRef"
|
||||||
|
:inline="true"
|
||||||
|
@keyup.enter="handleSearch"
|
||||||
|
class="search-form">
|
||||||
<el-form-item label="学院" prop="deptCode">
|
<el-form-item label="学院" prop="deptCode">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="searchForm.deptCode"
|
v-model="searchForm.deptCode"
|
||||||
@@ -53,111 +66,131 @@
|
|||||||
style="width: 200px" />
|
style="width: 200px" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" plain icon="Search" @click="handleSearch">查询</el-button>
|
<el-button type="primary" icon="Search" @click="handleSearch">查询</el-button>
|
||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="FolderAdd"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
@click="formDialogRef.openDialog()">
|
教室日常卫生列表
|
||||||
新 增
|
</span>
|
||||||
</el-button>
|
<div class="header-actions">
|
||||||
<right-toolbar
|
<el-button
|
||||||
v-model:showSearch="showSearch"
|
icon="FolderAdd"
|
||||||
:export="'stuwork_classRoomHygieneDaily_export'"
|
type="primary"
|
||||||
@exportExcel="exportExcel"
|
@click="formDialogRef.openDialog()">
|
||||||
class="ml10 mr20"
|
新增
|
||||||
style="float: right;"
|
</el-button>
|
||||||
@queryTable="getDataList">
|
<right-toolbar
|
||||||
<TableColumnControl
|
v-model:showSearch="showSearch"
|
||||||
ref="columnControlRef"
|
:export="'stuwork_classRoomHygieneDaily_export'"
|
||||||
:columns="tableColumns"
|
@exportExcel="exportExcel"
|
||||||
v-model="visibleColumns"
|
class="ml10"
|
||||||
trigger-type="default"
|
@queryTable="getDataList">
|
||||||
trigger-circle
|
<TableColumnControl
|
||||||
@change="handleColumnChange"
|
ref="columnControlRef"
|
||||||
@order-change="handleColumnOrderChange"
|
:columns="tableColumns"
|
||||||
>
|
v-model="visibleColumns"
|
||||||
<template #trigger>
|
trigger-type="default"
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
trigger-circle
|
||||||
<el-button circle style="margin-left: 0;">
|
@change="handleColumnChange"
|
||||||
<el-icon><Menu /></el-icon>
|
@order-change="handleColumnOrderChange"
|
||||||
</el-button>
|
>
|
||||||
</el-tooltip>
|
<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"
|
||||||
|
@sort-change="sortChangeHandle">
|
||||||
|
<el-table-column type="index" label="序号" 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 !== '操作'"
|
||||||
|
: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>
|
||||||
</TableColumnControl>
|
<!-- 扣分列特殊模板 -->
|
||||||
</right-toolbar>
|
<template v-if="col.prop === 'score'" #default="scope">
|
||||||
|
<el-tag v-if="scope.row.score !== undefined && scope.row.score !== null" size="small" type="danger" effect="plain" round>
|
||||||
|
{{ scope.row.score }}
|
||||||
|
</el-tag>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column label="操作" width="180" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
icon="EditPen"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="formDialogRef.openDialog(scope.row.id)">
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="Delete"
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
@click="handleDelete([scope.row.id])">
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<template #empty>
|
||||||
|
<el-empty description="暂无数据" :image-size="120">
|
||||||
|
<el-button type="primary" icon="FolderAdd" @click="formDialogRef.openDialog()">新增记录</el-button>
|
||||||
|
</el-empty>
|
||||||
|
</template>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<div class="pagination-wrapper">
|
||||||
|
<pagination
|
||||||
|
@size-change="sizeChangeHandle"
|
||||||
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
</div>
|
</div>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 表格 -->
|
|
||||||
<el-table
|
|
||||||
:data="state.dataList"
|
|
||||||
v-loading="state.loading"
|
|
||||||
border
|
|
||||||
:cell-style="tableStyle.cellStyle"
|
|
||||||
:header-cell-style="tableStyle.headerCellStyle"
|
|
||||||
@sort-change="sortChangeHandle">
|
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
v-for="col in visibleColumnsSorted"
|
|
||||||
:key="col.prop"
|
|
||||||
:prop="col.prop"
|
|
||||||
:label="col.label"
|
|
||||||
:width="col.width"
|
|
||||||
:min-width="col.minWidth"
|
|
||||||
:show-overflow-tooltip="col.showOverflowTooltip !== false"
|
|
||||||
:align="col.align">
|
|
||||||
<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 === 'score'">
|
|
||||||
<el-tag v-if="scope.row.score !== undefined && scope.row.score !== null" size="small" type="danger" effect="plain">
|
|
||||||
{{ scope.row.score }}
|
|
||||||
</el-tag>
|
|
||||||
<span v-else>-</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><Setting /></el-icon>
|
|
||||||
<span style="margin-left: 4px">操作</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
icon="Edit"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="formDialogRef.openDialog(scope.row.id)">
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Delete"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete([scope.row.id])">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
|
||||||
<pagination
|
|
||||||
@size-change="sizeChangeHandle"
|
|
||||||
@current-change="currentChangeHandle"
|
|
||||||
v-bind="state.pagination" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 编辑、新增 -->
|
<!-- 编辑、新增 -->
|
||||||
@@ -166,22 +199,20 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="ClassRoomHygieneDaily">
|
<script setup lang="ts" name="ClassRoomHygieneDaily">
|
||||||
import { ref, reactive, defineAsyncComponent, computed, onMounted } from 'vue'
|
import { ref, reactive, defineAsyncComponent, onMounted } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||||
import { fetchList, delObjs } from "/@/api/stuwork/classroomhygienedaily";
|
import { fetchList, delObjs } from "/@/api/stuwork/classroomhygienedaily";
|
||||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||||
import { getDeptList } from '/@/api/basic/basicclass'
|
import { getDeptList } from '/@/api/basic/basicclass'
|
||||||
import { getClassListByRole } from '/@/api/basic/basicclass'
|
import { getClassListByRole } from '/@/api/basic/basicclass'
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import { List, OfficeBuilding, Grid, Calendar, Minus, Document, Setting, Menu } from '@element-plus/icons-vue'
|
import { List, OfficeBuilding, Grid, Calendar, Minus, Document, Setting, Menu, Search, EditPen } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
|
||||||
// 引入组件
|
// 引入组件
|
||||||
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
|
||||||
const formDialogRef = ref()
|
const formDialogRef = ref()
|
||||||
const searchFormRef = ref()
|
const searchFormRef = ref()
|
||||||
const columnControlRef = ref()
|
const columnControlRef = ref()
|
||||||
@@ -199,94 +230,14 @@ const tableColumns = [
|
|||||||
{ prop: 'note', label: '检查记录', icon: Document }
|
{ prop: 'note', label: '检查记录', icon: Document }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制 Hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列变化处理
|
|
||||||
const handleColumnChange = (value: string[]) => {
|
|
||||||
visibleColumns.value = value
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = value.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列排序变化处理
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const visibleColumnsSorted = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: any[] = []
|
|
||||||
const unorderedColumns: any[] = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 搜索表单
|
// 搜索表单
|
||||||
const searchForm = reactive({
|
const searchForm = reactive({
|
||||||
@@ -303,7 +254,8 @@ const state: BasicTableProps = reactive<BasicTableProps>({
|
|||||||
props: {
|
props: {
|
||||||
item: 'records',
|
item: 'records',
|
||||||
totalCount: 'total'
|
totalCount: 'total'
|
||||||
}
|
},
|
||||||
|
createdIsNeed: true
|
||||||
})
|
})
|
||||||
|
|
||||||
// table hook
|
// table hook
|
||||||
@@ -355,13 +307,6 @@ const getClassListData = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化
|
|
||||||
loadSavedConfig()
|
|
||||||
onMounted(() => {
|
|
||||||
getDeptListData()
|
|
||||||
getClassListData()
|
|
||||||
})
|
|
||||||
|
|
||||||
// 导出excel
|
// 导出excel
|
||||||
const exportExcel = () => {
|
const exportExcel = () => {
|
||||||
downBlobFile(
|
downBlobFile(
|
||||||
@@ -391,4 +336,14 @@ const handleDelete = async (ids: string[]) => {
|
|||||||
useMessage().error(err.msg || '删除失败')
|
useMessage().error(err.msg || '删除失败')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
onMounted(() => {
|
||||||
|
getDeptListData()
|
||||||
|
getClassListData()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch" class="search-form">
|
||||||
<el-form-item label="楼号" prop="buildingNo">
|
<el-form-item label="楼号" prop="buildingNo">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="searchForm.buildingNo"
|
v-model="searchForm.buildingNo"
|
||||||
@@ -49,16 +57,21 @@
|
|||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<right-toolbar
|
<div class="card-header">
|
||||||
v-model:showSearch="showSearch"
|
<span class="card-title">
|
||||||
class="ml10"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
style="float: right;"
|
教室卫生日分析列表
|
||||||
@queryTable="getDataList">
|
</span>
|
||||||
|
<div class="header-actions">
|
||||||
|
<right-toolbar
|
||||||
|
v-model:showSearch="showSearch"
|
||||||
|
class="ml10"
|
||||||
|
@queryTable="getDataList">
|
||||||
<TableColumnControl
|
<TableColumnControl
|
||||||
ref="columnControlRef"
|
ref="columnControlRef"
|
||||||
:columns="tableColumns"
|
:columns="tableColumns"
|
||||||
@@ -77,33 +90,40 @@
|
|||||||
</template>
|
</template>
|
||||||
</TableColumnControl>
|
</TableColumnControl>
|
||||||
</right-toolbar>
|
</right-toolbar>
|
||||||
</div>
|
</div>
|
||||||
</el-row>
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table
|
<el-table
|
||||||
:data="state.dataList"
|
:data="state.dataList"
|
||||||
v-loading="state.loading"
|
v-loading="state.loading"
|
||||||
border
|
stripe
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle"
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
@sort-change="sortChangeHandle">
|
class="modern-table"
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
@sort-change="sortChangeHandle">
|
||||||
<template #header>
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<template v-for="col in sortedTableColumns" :key="col.prop || col.label">
|
|
||||||
<el-table-column
|
|
||||||
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作' && !col.prop?.startsWith('day')"
|
|
||||||
:prop="col.prop"
|
|
||||||
:label="col.label"
|
|
||||||
show-overflow-tooltip
|
|
||||||
align="center">
|
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
<el-icon><List /></el-icon>
|
||||||
<span style="margin-left: 4px">{{ col.label }}</span>
|
|
||||||
</template>
|
</template>
|
||||||
|
<template #default="{ $index }">
|
||||||
|
{{ $index + 1 }}
|
||||||
|
</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 !== '操作' && !col.prop?.startsWith('day')"
|
||||||
|
: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 v-if="col.prop === 'totalScore'" #default="scope">
|
<template v-if="col.prop === 'totalScore'" #default="scope">
|
||||||
<el-tag v-if="scope.row.totalScore !== undefined && scope.row.totalScore !== null" size="small" type="success" effect="plain">
|
<el-tag v-if="scope.row.totalScore !== undefined && scope.row.totalScore !== null" size="small" type="success" effect="plain">
|
||||||
@@ -134,31 +154,33 @@
|
|||||||
<span style="margin-left: 4px">{{ day }}日</span>
|
<span style="margin-left: 4px">{{ day }}日</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><Setting /></el-icon>
|
<el-icon><Setting /></el-icon>
|
||||||
<span style="margin-left: 4px">操作</span>
|
<span style="margin-left: 4px">操作</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
icon="Plus"
|
||||||
|
link
|
||||||
|
type="success"
|
||||||
|
@click="handleAddScore(scope.row)">
|
||||||
|
加分
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="Minus"
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
@click="handleSubtractScore(scope.row)">
|
||||||
|
减分
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<template #empty>
|
||||||
|
<el-empty description="暂无数据" :image-size="120" />
|
||||||
</template>
|
</template>
|
||||||
<template #default="scope">
|
</el-table>
|
||||||
<el-button
|
</el-card>
|
||||||
icon="Plus"
|
|
||||||
text
|
|
||||||
type="success"
|
|
||||||
@click="handleAddScore(scope.row)">
|
|
||||||
加分
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Minus"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleSubtractScore(scope.row)">
|
|
||||||
减分
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 此接口不支持分页,不显示分页组件 -->
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 加分/减分对话框 -->
|
<!-- 加分/减分对话框 -->
|
||||||
@@ -216,19 +238,17 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="ClassRoomHygieneDailyAnalysis">
|
<script setup lang="ts" name="ClassRoomHygieneDailyAnalysis">
|
||||||
import { ref, reactive, defineAsyncComponent, onMounted, computed, nextTick } from 'vue'
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||||
import { fetchList } from "/@/api/stuwork/classroomhygienedailyanalysis";
|
import { fetchList } from "/@/api/stuwork/classroomhygienedailyanalysis";
|
||||||
import { useMessage } from "/@/hooks/message";
|
import { useMessage } from "/@/hooks/message";
|
||||||
import { getBuildingList } from '/@/api/stuwork/teachbuilding'
|
import { getBuildingList } from '/@/api/stuwork/teachbuilding'
|
||||||
import { getClassListByRole } from '/@/api/basic/basicclass'
|
import { getClassListByRole } from '/@/api/basic/basicclass'
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import { List, DataAnalysis, Trophy, Grid, Calendar, Setting, Menu } from '@element-plus/icons-vue'
|
import { List, DataAnalysis, Trophy, Grid, Calendar, Setting, Menu, Search, Document } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
|
||||||
const searchFormRef = ref()
|
const searchFormRef = ref()
|
||||||
const columnControlRef = ref<any>()
|
const columnControlRef = ref<any>()
|
||||||
const scoreFormRef = ref()
|
const scoreFormRef = ref()
|
||||||
@@ -242,116 +262,20 @@ const isAddScore = ref(true) // true: 加分, false: 减分
|
|||||||
|
|
||||||
// 表格列配置(只包含固定列,动态日期列不包含在内)
|
// 表格列配置(只包含固定列,动态日期列不包含在内)
|
||||||
const tableColumns = [
|
const tableColumns = [
|
||||||
{ prop: 'totalScore', label: '总分' },
|
{ prop: 'totalScore', label: '总分', icon: DataAnalysis },
|
||||||
{ prop: 'rank', label: '排名' },
|
{ prop: 'rank', label: '排名', icon: Trophy },
|
||||||
{ prop: 'classNo', label: '班级' }
|
{ prop: 'classNo', label: '班级', icon: Grid }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 列配置映射(用于图标)
|
// 使用表格列控制 Hook
|
||||||
const columnConfigMap: Record<string, { icon: any }> = {
|
const {
|
||||||
totalScore: { icon: DataAnalysis },
|
visibleColumns,
|
||||||
rank: { icon: Trophy },
|
visibleColumnsSorted,
|
||||||
classNo: { icon: Grid }
|
checkColumnVisible,
|
||||||
}
|
handleColumnChange,
|
||||||
|
handleColumnOrderChange
|
||||||
|
} = useTableColumnControl(tableColumns)
|
||||||
|
|
||||||
// 当前显示的列
|
|
||||||
const visibleColumns = ref<string[]>([])
|
|
||||||
// 列排序顺序
|
|
||||||
const columnOrder = ref<string[]>([])
|
|
||||||
|
|
||||||
// 从本地统一存储加载配置
|
|
||||||
const loadSavedConfig = () => {
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadSavedConfig()
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const sortedTableColumns = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: typeof tableColumns = []
|
|
||||||
const unorderedColumns: typeof tableColumns = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 列显示控制函数
|
|
||||||
const checkColumnVisible = (prop: string): boolean => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return visibleColumns.value.includes(prop)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列变化
|
|
||||||
const handleColumnChange = (columns: string[]) => {
|
|
||||||
visibleColumns.value = columns
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = columns.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列排序变化
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 加分/减分表单
|
// 加分/减分表单
|
||||||
const scoreForm = reactive({
|
const scoreForm = reactive({
|
||||||
@@ -582,11 +506,10 @@ const getClassListData = async () => {
|
|||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getBuildingListData()
|
getBuildingListData()
|
||||||
getClassListData()
|
getClassListData()
|
||||||
nextTick(() => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
loadSavedConfig()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch" class="search-form">
|
||||||
<el-form-item label="学年" prop="schoolYear">
|
<el-form-item label="学年" prop="schoolYear">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="searchForm.schoolYear"
|
v-model="searchForm.schoolYear"
|
||||||
@@ -71,75 +79,84 @@
|
|||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="Upload"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
class="ml10"
|
教室卫生月度分析列表
|
||||||
@click="handleImport">
|
</span>
|
||||||
导入
|
<div class="header-actions">
|
||||||
</el-button>
|
<el-button
|
||||||
<el-button
|
icon="Upload"
|
||||||
icon="DocumentChecked"
|
type="primary"
|
||||||
type="success"
|
@click="handleImport">
|
||||||
class="ml10"
|
导入
|
||||||
@click="handleCheck">
|
</el-button>
|
||||||
考核
|
<el-button
|
||||||
</el-button>
|
icon="DocumentChecked"
|
||||||
<right-toolbar
|
type="success"
|
||||||
v-model:showSearch="showSearch"
|
class="ml10"
|
||||||
class="ml10"
|
@click="handleCheck">
|
||||||
style="float: right;"
|
考核
|
||||||
@queryTable="getDataList">
|
</el-button>
|
||||||
<TableColumnControl
|
<right-toolbar
|
||||||
ref="columnControlRef"
|
v-model:showSearch="showSearch"
|
||||||
:columns="tableColumns"
|
class="ml10"
|
||||||
v-model="visibleColumns"
|
@queryTable="getDataList">
|
||||||
trigger-type="default"
|
<TableColumnControl
|
||||||
trigger-circle
|
ref="columnControlRef"
|
||||||
@change="handleColumnChange"
|
:columns="tableColumns"
|
||||||
@order-change="handleColumnOrderChange"
|
v-model="visibleColumns"
|
||||||
>
|
trigger-type="default"
|
||||||
<template #trigger>
|
trigger-circle
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
@change="handleColumnChange"
|
||||||
<el-button circle style="margin-left: 0;">
|
@order-change="handleColumnOrderChange"
|
||||||
<el-icon><Menu /></el-icon>
|
>
|
||||||
</el-button>
|
<template #trigger>
|
||||||
</el-tooltip>
|
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
||||||
</template>
|
<el-button circle style="margin-left: 0;">
|
||||||
</TableColumnControl>
|
<el-icon><Menu /></el-icon>
|
||||||
</right-toolbar>
|
</el-button>
|
||||||
</div>
|
</el-tooltip>
|
||||||
</el-row>
|
</template>
|
||||||
|
</TableColumnControl>
|
||||||
|
</right-toolbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table
|
<el-table
|
||||||
ref="tableRef"
|
ref="tableRef"
|
||||||
:data="state.dataList"
|
:data="state.dataList"
|
||||||
v-loading="state.loading"
|
v-loading="state.loading"
|
||||||
border
|
stripe
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle"
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
@sort-change="sortChangeHandle">
|
class="modern-table"
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
@sort-change="sortChangeHandle">
|
||||||
<template #header>
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<template v-for="col in sortedTableColumns" :key="col.prop">
|
|
||||||
<el-table-column
|
|
||||||
v-if="checkColumnVisible(col.prop || '')"
|
|
||||||
:prop="col.prop"
|
|
||||||
:label="col.label"
|
|
||||||
show-overflow-tooltip>
|
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
<el-icon><List /></el-icon>
|
||||||
<span style="margin-left: 4px">{{ col.label }}</span>
|
|
||||||
</template>
|
</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">
|
||||||
|
<el-table-column
|
||||||
|
v-if="checkColumnVisible(col.prop || '')"
|
||||||
|
:prop="col.prop"
|
||||||
|
:label="col.label"
|
||||||
|
show-overflow-tooltip>
|
||||||
|
<template #header>
|
||||||
|
<el-icon><component :is="columnConfigMap[col.prop || '']?.icon || Calendar" /></el-icon>
|
||||||
|
<span style="margin-left: 4px">{{ col.label }}</span>
|
||||||
|
</template>
|
||||||
<!-- 学期列特殊模板 -->
|
<!-- 学期列特殊模板 -->
|
||||||
<template v-if="col.prop === 'schoolTerm'" #default="scope">
|
<template v-if="col.prop === 'schoolTerm'" #default="scope">
|
||||||
<el-tag size="small" type="primary" effect="plain">
|
<el-tag size="small" type="primary" effect="plain">
|
||||||
@@ -160,30 +177,33 @@
|
|||||||
</el-tag>
|
</el-tag>
|
||||||
<span v-else>-</span>
|
<span v-else>-</span>
|
||||||
</template>
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
icon="Delete"
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
@click="handleDelete([scope.row.id])">
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</template>
|
</el-table>
|
||||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><Setting /></el-icon>
|
|
||||||
<span style="margin-left: 4px">操作</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
icon="Delete"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete([scope.row.id])">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<pagination
|
<div class="pagination-wrapper">
|
||||||
@size-change="sizeChangeHandle"
|
<pagination
|
||||||
@current-change="currentChangeHandle"
|
@size-change="sizeChangeHandle"
|
||||||
v-bind="state.pagination" />
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 导入对话框 -->
|
<!-- 导入对话框 -->
|
||||||
@@ -262,8 +282,9 @@ import type { TableInstance } from 'element-plus'
|
|||||||
|
|
||||||
// 引入组件
|
// 引入组件
|
||||||
const UploadExcel = defineAsyncComponent(() => import('/@/components/Upload/Excel.vue'));
|
const UploadExcel = defineAsyncComponent(() => import('/@/components/Upload/Excel.vue'));
|
||||||
import { List, Calendar, Clock, OfficeBuilding, Grid, UserFilled, Location, DataAnalysis, Document, Setting, Menu } from '@element-plus/icons-vue'
|
import { List, Calendar, Clock, OfficeBuilding, Grid, UserFilled, Location, DataAnalysis, Document, Setting, Menu, Search } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
import '/@/styles/modern-page.scss'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -297,126 +318,14 @@ const tableColumns = [
|
|||||||
{ prop: '操作', label: '操作', alwaysShow: true, fixed: 'right' as const }
|
{ prop: '操作', label: '操作', alwaysShow: true, fixed: 'right' as const }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 当前显示的列(从 localStorage 读取或默认全部显示)
|
// 使用表格列控制hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns.filter(col => !col.alwaysShow && !col.fixed))
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
const validColumns = tableColumns
|
|
||||||
.filter(col => !col.alwaysShow && !col.fixed)
|
|
||||||
.map(col => col.prop || col.label)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = validColumns
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = validColumns
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 立即加载保存的配置
|
|
||||||
loadSavedConfig()
|
|
||||||
|
|
||||||
// 初始化可见列(已废弃,使用 loadSavedConfig 代替)
|
|
||||||
const initVisibleColumns = () => {
|
|
||||||
// 配置已在组件创建时通过 loadSavedConfig() 加载
|
|
||||||
// 这里只做兼容性处理,不重复加载
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列显示控制函数
|
|
||||||
const checkColumnVisible = (prop: string): boolean => {
|
|
||||||
// 如果 visibleColumns 还没初始化,默认显示所有列
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查 prop 是否在可见列列表中
|
|
||||||
const isVisible = visibleColumns.value.includes(prop)
|
|
||||||
return isVisible
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列变化
|
|
||||||
const handleColumnChange = (columns: string[]) => {
|
|
||||||
visibleColumns.value = columns
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = columns.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列排序变化
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初始化列排序顺序(已废弃,使用 loadSavedConfig 代替)
|
|
||||||
const initColumnOrder = () => {
|
|
||||||
// 配置已在组件创建时通过 loadSavedConfig() 加载
|
|
||||||
// 这里只做兼容性处理,不重复加载
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取排序后的列配置
|
|
||||||
const sortedTableColumns = computed(() => {
|
|
||||||
// 获取所有可排序的列
|
|
||||||
const allSortableColumns = tableColumns.filter(col => !col.alwaysShow && !col.fixed)
|
|
||||||
|
|
||||||
if (columnOrder.value.length === 0) {
|
|
||||||
return allSortableColumns
|
|
||||||
}
|
|
||||||
|
|
||||||
const orderedColumns: typeof tableColumns = []
|
|
||||||
const unorderedColumns: typeof tableColumns = []
|
|
||||||
|
|
||||||
// 先按照保存的顺序添加列
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = tableColumns.find(c => {
|
|
||||||
const colKey = c.prop || c.label
|
|
||||||
return colKey === key && !c.alwaysShow && !c.fixed
|
|
||||||
})
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// 添加未在排序列表中的列(新增的列)
|
|
||||||
allSortableColumns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
})
|
|
||||||
|
|
||||||
// 列配置映射,包含每个列的渲染信息
|
// 列配置映射,包含每个列的渲染信息
|
||||||
const columnConfigMap: Record<string, any> = {
|
const columnConfigMap: Record<string, any> = {
|
||||||
@@ -674,15 +583,5 @@ onMounted(() => {
|
|||||||
getDeptListData()
|
getDeptListData()
|
||||||
getClassListData()
|
getClassListData()
|
||||||
getSchoolTermDict()
|
getSchoolTermDict()
|
||||||
// 配置已在组件创建时通过 loadSavedConfig() 加载
|
|
||||||
// 确保配置已同步到 TableColumnControl 组件
|
|
||||||
nextTick(() => {
|
|
||||||
// 确保 visibleColumns 已正确加载
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
// 如果 visibleColumns 为空,重新加载配置
|
|
||||||
loadSavedConfig()
|
|
||||||
}
|
|
||||||
// visibleColumns 已经通过 v-model 绑定到 TableColumnControl,应该会自动同步
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="state.queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</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-form-item label="学年" prop="schoolYear">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="state.queryForm.schoolYear"
|
v-model="state.queryForm.schoolYear"
|
||||||
@@ -62,30 +70,34 @@
|
|||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="Plus"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
class="ml10"
|
安全教育列表
|
||||||
@click="formDialogRef.openDialog()">
|
</span>
|
||||||
新增
|
<div class="header-actions">
|
||||||
</el-button>
|
<el-button
|
||||||
<el-button
|
icon="Plus"
|
||||||
icon="Download"
|
type="primary"
|
||||||
type="primary"
|
@click="formDialogRef.openDialog()">
|
||||||
class="ml10"
|
新增
|
||||||
@click="handleExport">
|
</el-button>
|
||||||
导出
|
<el-button
|
||||||
</el-button>
|
icon="Download"
|
||||||
<right-toolbar
|
type="success"
|
||||||
v-model:showSearch="showSearch"
|
class="ml10"
|
||||||
class="ml10 mr20"
|
@click="handleExport">
|
||||||
style="float: right;"
|
导出
|
||||||
@queryTable="getDataList">
|
</el-button>
|
||||||
|
<right-toolbar
|
||||||
|
v-model:showSearch="showSearch"
|
||||||
|
class="ml10"
|
||||||
|
@queryTable="getDataList">
|
||||||
<TableColumnControl
|
<TableColumnControl
|
||||||
ref="columnControlRef"
|
ref="columnControlRef"
|
||||||
:columns="tableColumns"
|
:columns="tableColumns"
|
||||||
@@ -104,106 +116,118 @@
|
|||||||
</template>
|
</template>
|
||||||
</TableColumnControl>
|
</TableColumnControl>
|
||||||
</right-toolbar>
|
</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="序号" 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 !== '操作'"
|
||||||
|
: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 === 'attendNum'">
|
||||||
|
<el-tag v-if="scope.row.attendNum" size="small" type="success" effect="plain">
|
||||||
|
{{ scope.row.attendNum }}
|
||||||
|
</el-tag>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope" v-else-if="col.prop === 'attachment'">
|
||||||
|
<el-button
|
||||||
|
v-if="scope.row.attachment"
|
||||||
|
icon="Picture"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
@click="handleViewImage(scope.row.attachment)">
|
||||||
|
查看
|
||||||
|
</el-button>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope" v-else-if="col.prop === 'attachment2'">
|
||||||
|
<el-button
|
||||||
|
v-if="scope.row.attachment2"
|
||||||
|
icon="Picture"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
@click="handleViewImage(scope.row.attachment2)">
|
||||||
|
查看
|
||||||
|
</el-button>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column label="操作" width="250" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
icon="View"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="handleView(scope.row)">
|
||||||
|
查看详情
|
||||||
|
</el-button>
|
||||||
|
<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>
|
||||||
|
<template #empty>
|
||||||
|
<el-empty description="暂无数据" :image-size="120" />
|
||||||
|
</template>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<div class="pagination-wrapper">
|
||||||
|
<pagination
|
||||||
|
@size-change="sizeChangeHandle"
|
||||||
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
</div>
|
</div>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 表格 -->
|
|
||||||
<el-table
|
|
||||||
:data="state.dataList"
|
|
||||||
v-loading="state.loading"
|
|
||||||
border
|
|
||||||
:cell-style="tableStyle.cellStyle"
|
|
||||||
:header-cell-style="tableStyle.headerCellStyle">
|
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
v-for="col in visibleColumnsSorted"
|
|
||||||
:key="col.prop"
|
|
||||||
: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 === 'attendNum'">
|
|
||||||
<el-tag v-if="scope.row.attendNum" size="small" type="success" effect="plain">
|
|
||||||
{{ scope.row.attendNum }}
|
|
||||||
</el-tag>
|
|
||||||
<span v-else>-</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope" v-else-if="col.prop === 'attachment'">
|
|
||||||
<el-button
|
|
||||||
v-if="scope.row.attachment"
|
|
||||||
icon="Picture"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
size="small"
|
|
||||||
@click="handleViewImage(scope.row.attachment)">
|
|
||||||
查看
|
|
||||||
</el-button>
|
|
||||||
<span v-else>-</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope" v-else-if="col.prop === 'attachment2'">
|
|
||||||
<el-button
|
|
||||||
v-if="scope.row.attachment2"
|
|
||||||
icon="Picture"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
size="small"
|
|
||||||
@click="handleViewImage(scope.row.attachment2)">
|
|
||||||
查看
|
|
||||||
</el-button>
|
|
||||||
<span v-else>-</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="操作" width="250" align="center" fixed="right">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><Setting /></el-icon>
|
|
||||||
<span style="margin-left: 4px">操作</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
icon="View"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleView(scope.row)">
|
|
||||||
查看详情
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Edit"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleEdit(scope.row)">
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Delete"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row)">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
|
||||||
<pagination
|
|
||||||
@size-change="sizeChangeHandle"
|
|
||||||
@current-change="currentChangeHandle"
|
|
||||||
v-bind="state.pagination" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 新增/编辑表单弹窗 -->
|
<!-- 新增/编辑表单弹窗 -->
|
||||||
@@ -215,8 +239,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="ClassSafeEdu">
|
<script setup lang="ts" name="ClassSafeEdu">
|
||||||
import { reactive, ref, onMounted, computed } from 'vue'
|
import { reactive, ref, onMounted } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||||
import { fetchList, delObj, exportExcel } from "/@/api/stuwork/classsafeedu";
|
import { fetchList, delObj, exportExcel } from "/@/api/stuwork/classsafeedu";
|
||||||
import { getClassListByRole } from "/@/api/basic/basicclass";
|
import { getClassListByRole } from "/@/api/basic/basicclass";
|
||||||
@@ -226,11 +249,10 @@ import { useMessage, useMessageBox } from "/@/hooks/message";
|
|||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import FormDialog from './form.vue'
|
import FormDialog from './form.vue'
|
||||||
import DetailDialog from './detail.vue'
|
import DetailDialog from './detail.vue'
|
||||||
import { List, Calendar, Clock, Grid, Trophy, User, Location, UserFilled, Setting, Menu } from '@element-plus/icons-vue'
|
import { List, Calendar, Clock, Grid, Trophy, User, Location, UserFilled, Setting, Menu, Search, Document } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
|
||||||
const formDialogRef = ref()
|
const formDialogRef = ref()
|
||||||
const detailDialogRef = ref()
|
const detailDialogRef = ref()
|
||||||
const searchFormRef = ref()
|
const searchFormRef = ref()
|
||||||
@@ -250,98 +272,18 @@ const tableColumns = [
|
|||||||
{ prop: 'address', label: '活动地点', icon: Location, minWidth: 150 },
|
{ prop: 'address', label: '活动地点', icon: Location, minWidth: 150 },
|
||||||
{ prop: 'recordDate', label: '活动时间', icon: Calendar, width: 120 },
|
{ prop: 'recordDate', label: '活动时间', icon: Calendar, width: 120 },
|
||||||
{ prop: 'attendNum', label: '参加人数', icon: UserFilled },
|
{ prop: 'attendNum', label: '参加人数', icon: UserFilled },
|
||||||
{ prop: 'attachment', label: '活动照片', width: 100 },
|
{ prop: 'attachment', label: '活动照片', icon: UserFilled, width: 100 },
|
||||||
{ prop: 'attachment2', label: '活动照片2', width: 100 }
|
{ prop: 'attachment2', label: '活动照片2', icon: UserFilled, width: 100 }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制 Hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列变化处理
|
|
||||||
const handleColumnChange = (value: string[]) => {
|
|
||||||
visibleColumns.value = value
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = value.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列排序变化处理
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const visibleColumnsSorted = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: any[] = []
|
|
||||||
const unorderedColumns: any[] = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 表格样式
|
// 表格样式
|
||||||
const tableStyle = {
|
const tableStyle = {
|
||||||
@@ -508,7 +450,6 @@ const getClassListData = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
loadSavedConfig()
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getSchoolYearList()
|
getSchoolYearList()
|
||||||
getSchoolTermDict()
|
getSchoolTermDict()
|
||||||
@@ -517,5 +458,6 @@ onMounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="state.queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</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-form-item label="学年" prop="schoolYear">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="state.queryForm.schoolYear"
|
v-model="state.queryForm.schoolYear"
|
||||||
@@ -82,23 +90,27 @@
|
|||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="Plus"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
class="ml10"
|
班级总结列表
|
||||||
@click="formDialogRef.openDialog()">
|
</span>
|
||||||
新增
|
<div class="header-actions">
|
||||||
</el-button>
|
<el-button
|
||||||
<right-toolbar
|
icon="Plus"
|
||||||
v-model:showSearch="showSearch"
|
type="primary"
|
||||||
class="ml10 mr20"
|
@click="formDialogRef.openDialog()">
|
||||||
style="float: right;"
|
新增
|
||||||
@queryTable="getDataList">
|
</el-button>
|
||||||
|
<right-toolbar
|
||||||
|
v-model:showSearch="showSearch"
|
||||||
|
class="ml10"
|
||||||
|
@queryTable="getDataList">
|
||||||
<TableColumnControl
|
<TableColumnControl
|
||||||
ref="columnControlRef"
|
ref="columnControlRef"
|
||||||
:columns="tableColumns"
|
:columns="tableColumns"
|
||||||
@@ -117,34 +129,39 @@
|
|||||||
</template>
|
</template>
|
||||||
</TableColumnControl>
|
</TableColumnControl>
|
||||||
</right-toolbar>
|
</right-toolbar>
|
||||||
</div>
|
</div>
|
||||||
</el-row>
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table
|
<el-table
|
||||||
:data="state.dataList"
|
:data="state.dataList"
|
||||||
v-loading="state.loading"
|
v-loading="state.loading"
|
||||||
border
|
stripe
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle">
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
class="modern-table">
|
||||||
<template #header>
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
<el-icon><List /></el-icon>
|
<template #header>
|
||||||
</template>
|
<el-icon><List /></el-icon>
|
||||||
</el-table-column>
|
</template>
|
||||||
<el-table-column
|
<template #default="{ $index }">
|
||||||
v-for="col in visibleColumnsSorted"
|
{{ $index + 1 + ((state.pagination?.current || 1) - 1) * (state.pagination?.size || 10) }}
|
||||||
:key="col.prop"
|
</template>
|
||||||
:prop="col.prop"
|
</el-table-column>
|
||||||
:label="col.label"
|
<template v-for="col in visibleColumnsSorted" :key="col.prop || col.label">
|
||||||
:width="col.width"
|
<el-table-column
|
||||||
:min-width="col.minWidth"
|
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
||||||
:show-overflow-tooltip="col.showOverflowTooltip !== false"
|
:prop="col.prop"
|
||||||
:align="col.align || 'center'">
|
:label="col.label"
|
||||||
<template #header>
|
:width="col.width"
|
||||||
<el-icon v-if="col.icon"><component :is="col.icon" /></el-icon>
|
:min-width="col.minWidth"
|
||||||
<span :style="{ marginLeft: col.icon ? '4px' : '0' }">{{ col.label }}</span>
|
:show-overflow-tooltip="col.showOverflowTooltip !== false"
|
||||||
</template>
|
: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'">
|
<template #default="scope" v-if="col.prop === 'schoolTerm'">
|
||||||
<el-tag size="small" type="primary" effect="plain">
|
<el-tag size="small" type="primary" effect="plain">
|
||||||
{{ formatSchoolTerm(scope.row.schoolTerm) }}
|
{{ formatSchoolTerm(scope.row.schoolTerm) }}
|
||||||
@@ -222,42 +239,48 @@
|
|||||||
</el-tag>
|
</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" width="200" align="center" fixed="right">
|
<el-table-column label="操作" width="200" align="center" fixed="right">
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><Setting /></el-icon>
|
<el-icon><Setting /></el-icon>
|
||||||
<span style="margin-left: 4px">操作</span>
|
<span style="margin-left: 4px">操作</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
icon="View"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="handleView(scope.row)">
|
||||||
|
查看
|
||||||
|
</el-button>
|
||||||
|
<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>
|
||||||
|
<template #empty>
|
||||||
|
<el-empty description="暂无数据" :image-size="120" />
|
||||||
</template>
|
</template>
|
||||||
<template #default="scope">
|
</el-table>
|
||||||
<el-button
|
|
||||||
icon="View"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleView(scope.row)">
|
|
||||||
查看
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Edit"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleEdit(scope.row)">
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Delete"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row)">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<pagination
|
<div class="pagination-wrapper">
|
||||||
@size-change="sizeChangeHandle"
|
<pagination
|
||||||
@current-change="currentChangeHandle"
|
@size-change="sizeChangeHandle"
|
||||||
v-bind="state.pagination" />
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 新增/编辑表单弹窗 -->
|
<!-- 新增/编辑表单弹窗 -->
|
||||||
@@ -269,8 +292,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="ClassSummary">
|
<script setup lang="ts" name="ClassSummary">
|
||||||
import { reactive, ref, onMounted, computed } from 'vue'
|
import { reactive, ref, onMounted } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||||
import { fetchList, delObj } from "/@/api/stuwork/classsummary";
|
import { fetchList, delObj } from "/@/api/stuwork/classsummary";
|
||||||
import { getTeachDept } from "/@/api/basic/basicdept";
|
import { getTeachDept } from "/@/api/basic/basicdept";
|
||||||
@@ -281,11 +303,10 @@ import { useMessage, useMessageBox } from "/@/hooks/message";
|
|||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import FormDialog from './form.vue'
|
import FormDialog from './form.vue'
|
||||||
import DetailDialog from './detail.vue'
|
import DetailDialog from './detail.vue'
|
||||||
import { List, Calendar, Clock, Grid, UserFilled, Male, Female, House, Warning, CircleCheck, Setting, Menu } from '@element-plus/icons-vue'
|
import { List, Calendar, Clock, Grid, UserFilled, Male, Female, House, Warning, CircleCheck, Setting, Menu, Search, Document } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
|
||||||
const formDialogRef = ref()
|
const formDialogRef = ref()
|
||||||
const detailDialogRef = ref()
|
const detailDialogRef = ref()
|
||||||
const searchFormRef = ref()
|
const searchFormRef = ref()
|
||||||
@@ -316,94 +337,14 @@ const tableColumns = [
|
|||||||
{ prop: 'status', label: '状态', icon: CircleCheck }
|
{ prop: 'status', label: '状态', icon: CircleCheck }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制 Hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列变化处理
|
|
||||||
const handleColumnChange = (value: string[]) => {
|
|
||||||
visibleColumns.value = value
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = value.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列排序变化处理
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const visibleColumnsSorted = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: any[] = []
|
|
||||||
const unorderedColumns: any[] = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 配置 useTable
|
// 配置 useTable
|
||||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||||
@@ -559,7 +500,6 @@ const getStatusDict = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
loadSavedConfig()
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getSchoolYearList()
|
getSchoolYearList()
|
||||||
getSchoolTermDict()
|
getSchoolTermDict()
|
||||||
@@ -570,4 +510,5 @@ onMounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="state.queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</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-form-item label="学年" prop="schoolYear">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="state.queryForm.schoolYear"
|
v-model="state.queryForm.schoolYear"
|
||||||
@@ -82,23 +90,27 @@
|
|||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="Plus"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
class="ml10"
|
班级主题列表
|
||||||
@click="handleInit">
|
</span>
|
||||||
初始化
|
<div class="header-actions">
|
||||||
</el-button>
|
<el-button
|
||||||
<right-toolbar
|
icon="Plus"
|
||||||
v-model:showSearch="showSearch"
|
type="primary"
|
||||||
class="ml10"
|
@click="handleInit">
|
||||||
style="float: right;"
|
初始化
|
||||||
@queryTable="getDataList">
|
</el-button>
|
||||||
|
<right-toolbar
|
||||||
|
v-model:showSearch="showSearch"
|
||||||
|
class="ml10"
|
||||||
|
@queryTable="getDataList">
|
||||||
<TableColumnControl
|
<TableColumnControl
|
||||||
ref="columnControlRef"
|
ref="columnControlRef"
|
||||||
:columns="tableColumns"
|
:columns="tableColumns"
|
||||||
@@ -117,34 +129,39 @@
|
|||||||
</template>
|
</template>
|
||||||
</TableColumnControl>
|
</TableColumnControl>
|
||||||
</right-toolbar>
|
</right-toolbar>
|
||||||
</div>
|
</div>
|
||||||
</el-row>
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table
|
<el-table
|
||||||
:data="state.dataList"
|
:data="state.dataList"
|
||||||
v-loading="state.loading"
|
v-loading="state.loading"
|
||||||
border
|
stripe
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle">
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
class="modern-table">
|
||||||
<template #header>
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<template v-for="col in sortedTableColumns" :key="col.prop || col.label">
|
|
||||||
<el-table-column
|
|
||||||
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
|
||||||
:prop="col.prop"
|
|
||||||
:label="col.label"
|
|
||||||
show-overflow-tooltip
|
|
||||||
align="center"
|
|
||||||
:min-width="col.minWidth"
|
|
||||||
:width="col.width">
|
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
<el-icon><List /></el-icon>
|
||||||
<span style="margin-left: 4px">{{ col.label }}</span>
|
|
||||||
</template>
|
</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 !== '操作'"
|
||||||
|
: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 v-if="col.prop === 'schoolTerm'" #default="scope">
|
<template v-if="col.prop === 'schoolTerm'" #default="scope">
|
||||||
<el-tag size="small" type="primary" effect="plain">
|
<el-tag size="small" type="primary" effect="plain">
|
||||||
@@ -160,35 +177,41 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</template>
|
</template>
|
||||||
<el-table-column label="操作" width="200" align="center" fixed="right">
|
<el-table-column label="操作" width="200" align="center" fixed="right">
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><Setting /></el-icon>
|
<el-icon><Setting /></el-icon>
|
||||||
<span style="margin-left: 4px">操作</span>
|
<span style="margin-left: 4px">操作</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
icon="View"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="handleViewRecord(scope.row)">
|
||||||
|
查看上传记录
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="Delete"
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
@click="handleDelete(scope.row)">
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<template #empty>
|
||||||
|
<el-empty description="暂无数据" :image-size="120" />
|
||||||
</template>
|
</template>
|
||||||
<template #default="scope">
|
</el-table>
|
||||||
<el-button
|
|
||||||
icon="View"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleViewRecord(scope.row)">
|
|
||||||
查看上传记录
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Delete"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row)">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<pagination
|
<div class="pagination-wrapper">
|
||||||
@size-change="sizeChangeHandle"
|
<pagination
|
||||||
@current-change="currentChangeHandle"
|
@size-change="sizeChangeHandle"
|
||||||
v-bind="state.pagination" />
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 初始化弹窗 -->
|
<!-- 初始化弹窗 -->
|
||||||
@@ -200,8 +223,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="ClassTheme">
|
<script setup lang="ts" name="ClassTheme">
|
||||||
import { reactive, ref, onMounted, computed, nextTick } from 'vue'
|
import { reactive, ref, onMounted } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||||
import { fetchList, delObj } from "/@/api/stuwork/classtheme";
|
import { fetchList, delObj } from "/@/api/stuwork/classtheme";
|
||||||
import { getDeptList } from "/@/api/basic/basicclass";
|
import { getDeptList } from "/@/api/basic/basicclass";
|
||||||
@@ -212,11 +234,10 @@ import { useMessage, useMessageBox } from "/@/hooks/message";
|
|||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import InitDialog from './init.vue'
|
import InitDialog from './init.vue'
|
||||||
import RecordDialog from './record.vue'
|
import RecordDialog from './record.vue'
|
||||||
import { List, Calendar, Clock, OfficeBuilding, Grid, Upload, EditPen, Setting, Menu } from '@element-plus/icons-vue'
|
import { List, Calendar, Clock, OfficeBuilding, Grid, Upload, EditPen, Setting, Menu, Search, Document } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
|
||||||
const initDialogRef = ref()
|
const initDialogRef = ref()
|
||||||
const columnControlRef = ref<any>()
|
const columnControlRef = ref<any>()
|
||||||
const recordDialogRef = ref()
|
const recordDialogRef = ref()
|
||||||
@@ -230,122 +251,22 @@ const yesNoList = ref<any[]>([])
|
|||||||
|
|
||||||
// 表格列配置
|
// 表格列配置
|
||||||
const tableColumns = [
|
const tableColumns = [
|
||||||
{ prop: 'schoolYear', label: '学年' },
|
{ prop: 'schoolYear', label: '学年', icon: Calendar },
|
||||||
{ prop: 'schoolTerm', label: '学期' },
|
{ prop: 'schoolTerm', label: '学期', icon: Clock },
|
||||||
{ prop: 'deptName', label: '学院' },
|
{ prop: 'deptName', label: '学院', icon: OfficeBuilding },
|
||||||
{ prop: 'classNo', label: '班级' },
|
{ prop: 'classNo', label: '班级', icon: Grid },
|
||||||
{ prop: 'count', label: '上传次数' },
|
{ prop: 'count', label: '上传次数', icon: Upload },
|
||||||
{ prop: 'remarks', label: '备注', minWidth: 200 }
|
{ prop: 'remarks', label: '备注', icon: EditPen, minWidth: 200 }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 列配置映射(用于图标)
|
// 使用表格列控制 Hook
|
||||||
const columnConfigMap: Record<string, { icon: any }> = {
|
const {
|
||||||
schoolYear: { icon: Calendar },
|
visibleColumns,
|
||||||
schoolTerm: { icon: Clock },
|
visibleColumnsSorted,
|
||||||
deptName: { icon: OfficeBuilding },
|
checkColumnVisible,
|
||||||
classNo: { icon: Grid },
|
handleColumnChange,
|
||||||
count: { icon: Upload },
|
handleColumnOrderChange
|
||||||
remarks: { icon: EditPen }
|
} = useTableColumnControl(tableColumns)
|
||||||
}
|
|
||||||
|
|
||||||
// 当前显示的列
|
|
||||||
const visibleColumns = ref<string[]>([])
|
|
||||||
// 列排序顺序
|
|
||||||
const columnOrder = ref<string[]>([])
|
|
||||||
|
|
||||||
// 从本地统一存储加载配置
|
|
||||||
const loadSavedConfig = () => {
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadSavedConfig()
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const sortedTableColumns = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: typeof tableColumns = []
|
|
||||||
const unorderedColumns: typeof tableColumns = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 列显示控制函数
|
|
||||||
const checkColumnVisible = (prop: string): boolean => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return visibleColumns.value.includes(prop)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列变化
|
|
||||||
const handleColumnChange = (columns: string[]) => {
|
|
||||||
visibleColumns.value = columns
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = columns.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列排序变化
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 配置 useTable
|
// 配置 useTable
|
||||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||||
@@ -500,13 +421,9 @@ onMounted(() => {
|
|||||||
getDeptListData()
|
getDeptListData()
|
||||||
getClassListData()
|
getClassListData()
|
||||||
getYesNoDict()
|
getYesNoDict()
|
||||||
nextTick(() => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
loadSavedConfig()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,21 +1,24 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
|
<!-- 内容卡片 -->
|
||||||
<!-- 操作按钮 -->
|
<el-card class="content-card" shadow="never">
|
||||||
<el-row>
|
<template #header>
|
||||||
<div class="mb8" style="width: 100%">
|
<div class="card-header">
|
||||||
<el-button
|
<span class="card-title">
|
||||||
icon="FolderAdd"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
type="primary"
|
宿舍楼列表
|
||||||
class="ml10"
|
</span>
|
||||||
@click="formDialogRef.openDialog()">
|
<div class="header-actions">
|
||||||
新 增
|
<el-button
|
||||||
</el-button>
|
icon="FolderAdd"
|
||||||
<right-toolbar
|
type="primary"
|
||||||
class="ml10"
|
@click="formDialogRef.openDialog()">
|
||||||
style="float: right;"
|
新 增
|
||||||
@queryTable="getDataList">
|
</el-button>
|
||||||
|
<right-toolbar
|
||||||
|
class="ml10"
|
||||||
|
@queryTable="getDataList">
|
||||||
<TableColumnControl
|
<TableColumnControl
|
||||||
ref="columnControlRef"
|
ref="columnControlRef"
|
||||||
:columns="tableColumns"
|
:columns="tableColumns"
|
||||||
@@ -34,35 +37,42 @@
|
|||||||
</template>
|
</template>
|
||||||
</TableColumnControl>
|
</TableColumnControl>
|
||||||
</right-toolbar>
|
</right-toolbar>
|
||||||
</div>
|
</div>
|
||||||
</el-row>
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table
|
<el-table
|
||||||
:data="state.dataList"
|
:data="state.dataList"
|
||||||
v-loading="state.loading"
|
v-loading="state.loading"
|
||||||
border
|
stripe
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle"
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
@sort-change="sortChangeHandle"
|
class="modern-table"
|
||||||
show-summary
|
@sort-change="sortChangeHandle"
|
||||||
:summary-method="getSummaries">
|
show-summary
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
:summary-method="getSummaries">
|
||||||
<template #header>
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<template v-for="col in sortedTableColumns" :key="col.prop || col.label">
|
|
||||||
<el-table-column
|
|
||||||
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
|
||||||
:prop="col.prop"
|
|
||||||
:label="col.label"
|
|
||||||
show-overflow-tooltip
|
|
||||||
align="center">
|
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
<el-icon><List /></el-icon>
|
||||||
<span style="margin-left: 4px">{{ col.label }}</span>
|
|
||||||
</template>
|
</template>
|
||||||
|
<template #default="{ $index }">
|
||||||
|
{{ $index + 1 }}
|
||||||
|
</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 !== '操作'"
|
||||||
|
: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 v-if="col.prop === 'layers'" #default="scope">
|
<template v-if="col.prop === 'layers'" #default="scope">
|
||||||
<el-tag v-if="scope.row.layers" size="small" type="info" effect="plain">
|
<el-tag v-if="scope.row.layers" size="small" type="info" effect="plain">
|
||||||
@@ -159,35 +169,41 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</template>
|
</template>
|
||||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><Setting /></el-icon>
|
<el-icon><Setting /></el-icon>
|
||||||
<span style="margin-left: 4px">操作</span>
|
<span style="margin-left: 4px">操作</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>
|
||||||
|
<template #empty>
|
||||||
|
<el-empty description="暂无数据" :image-size="120" />
|
||||||
</template>
|
</template>
|
||||||
<template #default="scope">
|
</el-table>
|
||||||
<el-button
|
|
||||||
icon="Edit"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleEdit(scope.row)">
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Delete"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row)">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<pagination
|
<div class="pagination-wrapper">
|
||||||
@size-change="sizeChangeHandle"
|
<pagination
|
||||||
@current-change="currentChangeHandle"
|
@size-change="sizeChangeHandle"
|
||||||
v-bind="state.pagination" />
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 编辑、新增 -->
|
<!-- 编辑、新增 -->
|
||||||
@@ -199,8 +215,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="DormBuilding">
|
<script setup lang="ts" name="DormBuilding">
|
||||||
import { ref, reactive, defineAsyncComponent, computed, onMounted, nextTick } from 'vue'
|
import { ref, reactive, defineAsyncComponent, onMounted } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||||
import { fetchList, delObjs } from "/@/api/stuwork/dormbuilding";
|
import { fetchList, delObjs } from "/@/api/stuwork/dormbuilding";
|
||||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||||
@@ -209,145 +224,38 @@ import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
|||||||
// 引入组件
|
// 引入组件
|
||||||
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
||||||
const EmptyRoomDialog = defineAsyncComponent(() => import('./emptyRoomDialog.vue'));
|
const EmptyRoomDialog = defineAsyncComponent(() => import('./emptyRoomDialog.vue'));
|
||||||
import { List, OfficeBuilding, Grid, UserFilled, Setting, Menu, Calendar } from '@element-plus/icons-vue'
|
import { List, OfficeBuilding, Grid, UserFilled, Setting, Menu, Calendar, Document } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
|
||||||
const formDialogRef = ref()
|
const formDialogRef = ref()
|
||||||
const emptyRoomDialogRef = ref()
|
const emptyRoomDialogRef = ref()
|
||||||
const columnControlRef = ref<any>()
|
const columnControlRef = ref<any>()
|
||||||
|
|
||||||
// 表格列配置
|
// 表格列配置
|
||||||
const tableColumns = [
|
const tableColumns = [
|
||||||
{ prop: 'buildingNo', label: '楼号' },
|
{ prop: 'buildingNo', label: '楼号', icon: OfficeBuilding },
|
||||||
{ prop: 'layers', label: '层数' },
|
{ prop: 'layers', label: '层数', icon: Grid },
|
||||||
{ prop: 'allAlreadyNum', label: '已住人数' },
|
{ prop: 'allAlreadyNum', label: '已住人数', icon: UserFilled },
|
||||||
{ prop: 'nowNum', label: '现住人数' },
|
{ prop: 'nowNum', label: '现住人数', icon: UserFilled },
|
||||||
{ prop: 'surplusNum', label: '剩余可住人数' },
|
{ prop: 'surplusNum', label: '剩余可住人数', icon: UserFilled },
|
||||||
{ prop: 'roomEmptyNum', label: '空宿舍数' },
|
{ prop: 'roomEmptyNum', label: '空宿舍数', icon: UserFilled },
|
||||||
{ prop: 'roomEmptyNum5', label: '空5人宿舍数' },
|
{ prop: 'roomEmptyNum5', label: '空5人宿舍数', icon: UserFilled },
|
||||||
{ prop: 'roomEmptyNum4', label: '空4人宿舍数' },
|
{ prop: 'roomEmptyNum4', label: '空4人宿舍数', icon: UserFilled },
|
||||||
{ prop: 'roomEmptyNum3', label: '空3人宿舍数' },
|
{ prop: 'roomEmptyNum3', label: '空3人宿舍数', icon: UserFilled },
|
||||||
{ prop: 'roomEmptyNum2', label: '空2人宿舍数' },
|
{ prop: 'roomEmptyNum2', label: '空2人宿舍数', icon: UserFilled },
|
||||||
{ prop: 'roomEmptyNum1', label: '空1人宿舍数' },
|
{ prop: 'roomEmptyNum1', label: '空1人宿舍数', icon: UserFilled },
|
||||||
{ prop: 'phone', label: '电话' }
|
{ prop: 'phone', label: '电话', icon: Calendar }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 列配置映射(用于图标)
|
// 使用表格列控制 Hook
|
||||||
const columnConfigMap: Record<string, { icon: any }> = {
|
const {
|
||||||
buildingNo: { icon: OfficeBuilding },
|
visibleColumns,
|
||||||
layers: { icon: Grid },
|
visibleColumnsSorted,
|
||||||
allAlreadyNum: { icon: UserFilled },
|
checkColumnVisible,
|
||||||
nowNum: { icon: UserFilled },
|
handleColumnChange,
|
||||||
surplusNum: { icon: UserFilled },
|
handleColumnOrderChange
|
||||||
roomEmptyNum: { icon: UserFilled },
|
} = useTableColumnControl(tableColumns)
|
||||||
roomEmptyNum5: { icon: UserFilled },
|
|
||||||
roomEmptyNum4: { icon: UserFilled },
|
|
||||||
roomEmptyNum3: { icon: UserFilled },
|
|
||||||
roomEmptyNum2: { icon: UserFilled },
|
|
||||||
roomEmptyNum1: { icon: UserFilled },
|
|
||||||
phone: { icon: Calendar }
|
|
||||||
}
|
|
||||||
|
|
||||||
// 当前显示的列
|
|
||||||
const visibleColumns = ref<string[]>([])
|
|
||||||
// 列排序顺序
|
|
||||||
const columnOrder = ref<string[]>([])
|
|
||||||
|
|
||||||
// 从本地统一存储加载配置
|
|
||||||
const loadSavedConfig = () => {
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadSavedConfig()
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const sortedTableColumns = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: typeof tableColumns = []
|
|
||||||
const unorderedColumns: typeof tableColumns = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 列显示控制函数
|
|
||||||
const checkColumnVisible = (prop: string): boolean => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return visibleColumns.value.includes(prop)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列变化
|
|
||||||
const handleColumnChange = (columns: string[]) => {
|
|
||||||
visibleColumns.value = columns
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = columns.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列排序变化
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 搜索表单
|
// 搜索表单
|
||||||
const searchForm = reactive({
|
const searchForm = reactive({
|
||||||
@@ -442,10 +350,10 @@ const handleViewEmptyRoom = (row: any, roomType: string) => {
|
|||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
nextTick(() => {
|
// Hook 会自动加载配置
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
loadSavedConfig()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,82 +1,95 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<right-toolbar
|
<div class="card-header">
|
||||||
class="ml10"
|
<span class="card-title">
|
||||||
style="float: right;"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
@queryTable="getDataList">
|
宿舍楼管理员列表
|
||||||
<TableColumnControl
|
</span>
|
||||||
ref="columnControlRef"
|
<div class="header-actions">
|
||||||
:columns="tableColumns"
|
<right-toolbar
|
||||||
v-model="visibleColumns"
|
class="ml10"
|
||||||
trigger-type="default"
|
@queryTable="getDataList">
|
||||||
trigger-circle
|
<TableColumnControl
|
||||||
@change="handleColumnChange"
|
ref="columnControlRef"
|
||||||
@order-change="handleColumnOrderChange"
|
:columns="tableColumns"
|
||||||
>
|
v-model="visibleColumns"
|
||||||
<template #trigger>
|
trigger-type="default"
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
trigger-circle
|
||||||
<el-button circle style="margin-left: 0;">
|
@change="handleColumnChange"
|
||||||
<el-icon><Menu /></el-icon>
|
@order-change="handleColumnOrderChange"
|
||||||
</el-button>
|
>
|
||||||
</el-tooltip>
|
<template #trigger>
|
||||||
</template>
|
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
||||||
</TableColumnControl>
|
<el-button circle style="margin-left: 0;">
|
||||||
</right-toolbar>
|
<el-icon><Menu /></el-icon>
|
||||||
</div>
|
</el-button>
|
||||||
</el-row>
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
</TableColumnControl>
|
||||||
|
</right-toolbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table
|
<el-table
|
||||||
:data="state.dataList"
|
:data="state.dataList"
|
||||||
v-loading="state.loading"
|
v-loading="state.loading"
|
||||||
border
|
stripe
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle"
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
@sort-change="sortChangeHandle">
|
@sort-change="sortChangeHandle"
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
class="modern-table">
|
||||||
<template #header>
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<template v-for="col in sortedTableColumns" :key="col.prop || col.label">
|
|
||||||
<el-table-column
|
|
||||||
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
|
||||||
:prop="col.prop"
|
|
||||||
:label="col.label"
|
|
||||||
show-overflow-tooltip
|
|
||||||
align="center">
|
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
<el-icon><List /></el-icon>
|
||||||
<span style="margin-left: 4px">{{ col.label }}</span>
|
</template>
|
||||||
|
<template #default="{ $index }">
|
||||||
|
{{ $index + 1 + ((state.pagination?.current || 1) - 1) * (state.pagination?.size || 10) }}
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</template>
|
<template v-for="col in visibleColumnsSorted" :key="col.prop || col.label">
|
||||||
<el-table-column label="操作" width="100" align="center" fixed="right">
|
<el-table-column
|
||||||
<template #header>
|
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
||||||
<el-icon><Setting /></el-icon>
|
:prop="col.prop"
|
||||||
<span style="margin-left: 4px">操作</span>
|
:label="col.label"
|
||||||
|
show-overflow-tooltip
|
||||||
|
align="center">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
||||||
|
<span style="margin-left: 4px">{{ col.label }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
</template>
|
</template>
|
||||||
<template #default="scope">
|
<el-table-column label="操作" width="100" align="center" fixed="right">
|
||||||
<el-button
|
<template #header>
|
||||||
icon="Delete"
|
<el-icon><Setting /></el-icon>
|
||||||
text
|
<span style="margin-left: 4px">操作</span>
|
||||||
type="danger"
|
</template>
|
||||||
@click="handleDelete(scope.row)">
|
<template #default="scope">
|
||||||
删除
|
<el-button
|
||||||
</el-button>
|
icon="Delete"
|
||||||
</template>
|
link
|
||||||
</el-table-column>
|
type="danger"
|
||||||
</el-table>
|
@click="handleDelete(scope.row)">
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<pagination
|
<div class="pagination-wrapper">
|
||||||
@size-change="sizeChangeHandle"
|
<pagination
|
||||||
@current-change="currentChangeHandle"
|
@size-change="sizeChangeHandle"
|
||||||
v-bind="state.pagination" />
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -88,8 +101,9 @@ import { BasicTableProps, useTable } from "/@/hooks/table";
|
|||||||
import { fetchList, delObjs } from "/@/api/stuwork/dormbuildingmanger";
|
import { fetchList, delObjs } from "/@/api/stuwork/dormbuildingmanger";
|
||||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import { List, OfficeBuilding, CreditCard, UserFilled, Setting, Menu, Calendar } from '@element-plus/icons-vue'
|
import { List, OfficeBuilding, CreditCard, UserFilled, Setting, Menu, Calendar, Document } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumnControl'
|
||||||
|
import '/@/styles/modern-page.scss'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -109,104 +123,14 @@ const columnConfigMap: Record<string, { icon: any }> = {
|
|||||||
realName: { icon: UserFilled }
|
realName: { icon: UserFilled }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns, route.path)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadSavedConfig()
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const sortedTableColumns = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: typeof tableColumns = []
|
|
||||||
const unorderedColumns: typeof tableColumns = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 列显示控制函数
|
|
||||||
const checkColumnVisible = (prop: string): boolean => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return visibleColumns.value.includes(prop)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列变化
|
|
||||||
const handleColumnChange = (columns: string[]) => {
|
|
||||||
visibleColumns.value = columns
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = columns.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列排序变化
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 配置 useTable
|
// 配置 useTable
|
||||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch" class="search-form">
|
||||||
<el-form-item label="学年" prop="schoolYear">
|
<el-form-item label="学年" prop="schoolYear">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="searchForm.schoolYear"
|
v-model="searchForm.schoolYear"
|
||||||
@@ -70,23 +78,27 @@
|
|||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="FolderAdd"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
class="ml10"
|
宿舍卫生日记录列表
|
||||||
@click="formDialogRef.openDialog()">
|
</span>
|
||||||
新 增
|
<div class="header-actions">
|
||||||
</el-button>
|
<el-button
|
||||||
<right-toolbar
|
icon="FolderAdd"
|
||||||
v-model:showSearch="showSearch"
|
type="primary"
|
||||||
class="ml10"
|
@click="formDialogRef.openDialog()">
|
||||||
style="float: right;"
|
新 增
|
||||||
@queryTable="getDataList">
|
</el-button>
|
||||||
|
<right-toolbar
|
||||||
|
v-model:showSearch="showSearch"
|
||||||
|
class="ml10"
|
||||||
|
@queryTable="getDataList">
|
||||||
<TableColumnControl
|
<TableColumnControl
|
||||||
ref="columnControlRef"
|
ref="columnControlRef"
|
||||||
:columns="tableColumns"
|
:columns="tableColumns"
|
||||||
@@ -105,33 +117,40 @@
|
|||||||
</template>
|
</template>
|
||||||
</TableColumnControl>
|
</TableColumnControl>
|
||||||
</right-toolbar>
|
</right-toolbar>
|
||||||
</div>
|
</div>
|
||||||
</el-row>
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table
|
<el-table
|
||||||
:data="state.dataList"
|
:data="state.dataList"
|
||||||
v-loading="state.loading"
|
v-loading="state.loading"
|
||||||
border
|
stripe
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle"
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
@sort-change="sortChangeHandle">
|
class="modern-table"
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
@sort-change="sortChangeHandle">
|
||||||
<template #header>
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<template v-for="col in sortedTableColumns" :key="col.prop || col.label">
|
|
||||||
<el-table-column
|
|
||||||
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
|
||||||
:prop="col.prop"
|
|
||||||
:label="col.label"
|
|
||||||
show-overflow-tooltip
|
|
||||||
align="center">
|
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
<el-icon><List /></el-icon>
|
||||||
<span style="margin-left: 4px">{{ col.label }}</span>
|
|
||||||
</template>
|
</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 !== '操作'"
|
||||||
|
: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 v-if="col.prop === 'schoolTerm'" #default="scope">
|
<template v-if="col.prop === 'schoolTerm'" #default="scope">
|
||||||
<el-tag size="small" type="primary" effect="plain">
|
<el-tag size="small" type="primary" effect="plain">
|
||||||
@@ -144,35 +163,41 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</template>
|
</template>
|
||||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><Setting /></el-icon>
|
<el-icon><Setting /></el-icon>
|
||||||
<span style="margin-left: 4px">操作</span>
|
<span style="margin-left: 4px">操作</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>
|
||||||
|
<template #empty>
|
||||||
|
<el-empty description="暂无数据" :image-size="120" />
|
||||||
</template>
|
</template>
|
||||||
<template #default="scope">
|
</el-table>
|
||||||
<el-button
|
|
||||||
icon="Edit"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleEdit(scope.row)">
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Delete"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row)">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<pagination
|
<div class="pagination-wrapper">
|
||||||
@size-change="sizeChangeHandle"
|
<pagination
|
||||||
@current-change="currentChangeHandle"
|
@size-change="sizeChangeHandle"
|
||||||
v-bind="state.pagination" />
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 编辑、新增 -->
|
<!-- 编辑、新增 -->
|
||||||
@@ -181,8 +206,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="DormHygieneDaily">
|
<script setup lang="ts" name="DormHygieneDaily">
|
||||||
import { ref, reactive, defineAsyncComponent, onMounted, computed, nextTick } from 'vue'
|
import { ref, reactive, defineAsyncComponent, onMounted, computed } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||||
import { fetchList, delObjs } from "/@/api/stuwork/dormhygienedaily";
|
import { fetchList, delObjs } from "/@/api/stuwork/dormhygienedaily";
|
||||||
import { getBuildingList } from "/@/api/stuwork/dormbuilding";
|
import { getBuildingList } from "/@/api/stuwork/dormbuilding";
|
||||||
@@ -194,11 +218,10 @@ import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
|||||||
|
|
||||||
// 引入组件
|
// 引入组件
|
||||||
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
||||||
import { List, Calendar, Clock, OfficeBuilding, House, Document, Setting, Menu } from '@element-plus/icons-vue'
|
import { List, Calendar, Clock, OfficeBuilding, House, Document, Setting, Menu, Search } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
|
||||||
const formDialogRef = ref()
|
const formDialogRef = ref()
|
||||||
const columnControlRef = ref<any>()
|
const columnControlRef = ref<any>()
|
||||||
const searchFormRef = ref()
|
const searchFormRef = ref()
|
||||||
@@ -210,122 +233,22 @@ const roomList = ref<any[]>([])
|
|||||||
|
|
||||||
// 表格列配置
|
// 表格列配置
|
||||||
const tableColumns = [
|
const tableColumns = [
|
||||||
{ prop: 'schoolYear', label: '学年' },
|
{ prop: 'schoolYear', label: '学年', icon: Calendar },
|
||||||
{ prop: 'schoolTerm', label: '学期' },
|
{ prop: 'schoolTerm', label: '学期', icon: Clock },
|
||||||
{ prop: 'buildingNo', label: '楼号' },
|
{ prop: 'buildingNo', label: '楼号', icon: OfficeBuilding },
|
||||||
{ prop: 'roomNo', label: '宿舍号' },
|
{ prop: 'roomNo', label: '宿舍号', icon: House },
|
||||||
{ prop: 'recordDate', label: '记录日期' },
|
{ prop: 'recordDate', label: '记录日期', icon: Calendar },
|
||||||
{ prop: 'note', label: '情况记录' }
|
{ prop: 'note', label: '情况记录', icon: Document }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 列配置映射(用于图标)
|
// 使用表格列控制 Hook
|
||||||
const columnConfigMap: Record<string, { icon: any }> = {
|
const {
|
||||||
schoolYear: { icon: Calendar },
|
visibleColumns,
|
||||||
schoolTerm: { icon: Clock },
|
visibleColumnsSorted,
|
||||||
buildingNo: { icon: OfficeBuilding },
|
checkColumnVisible,
|
||||||
roomNo: { icon: House },
|
handleColumnChange,
|
||||||
recordDate: { icon: Calendar },
|
handleColumnOrderChange
|
||||||
note: { icon: Document }
|
} = useTableColumnControl(tableColumns)
|
||||||
}
|
|
||||||
|
|
||||||
// 当前显示的列
|
|
||||||
const visibleColumns = ref<string[]>([])
|
|
||||||
// 列排序顺序
|
|
||||||
const columnOrder = ref<string[]>([])
|
|
||||||
|
|
||||||
// 从本地统一存储加载配置
|
|
||||||
const loadSavedConfig = () => {
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadSavedConfig()
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const sortedTableColumns = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: typeof tableColumns = []
|
|
||||||
const unorderedColumns: typeof tableColumns = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 列显示控制函数
|
|
||||||
const checkColumnVisible = (prop: string): boolean => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return visibleColumns.value.includes(prop)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列变化
|
|
||||||
const handleColumnChange = (columns: string[]) => {
|
|
||||||
visibleColumns.value = columns
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = columns.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列排序变化
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 搜索表单
|
// 搜索表单
|
||||||
const searchForm = reactive({
|
const searchForm = reactive({
|
||||||
@@ -471,10 +394,9 @@ onMounted(() => {
|
|||||||
getSchoolYearList()
|
getSchoolYearList()
|
||||||
getSchoolTermDict()
|
getSchoolTermDict()
|
||||||
getBuildingListData()
|
getBuildingListData()
|
||||||
nextTick(() => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
loadSavedConfig()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch" class="search-form">
|
||||||
<el-form-item label="学年" prop="schoolYear">
|
<el-form-item label="学年" prop="schoolYear">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="searchForm.schoolYear"
|
v-model="searchForm.schoolYear"
|
||||||
@@ -88,30 +96,34 @@
|
|||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="FolderAdd"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
class="ml10"
|
宿舍留宿申请列表
|
||||||
@click="formDialogRef.openDialog()">
|
</span>
|
||||||
新 增
|
<div class="header-actions">
|
||||||
</el-button>
|
<el-button
|
||||||
<el-button
|
icon="FolderAdd"
|
||||||
icon="Download"
|
type="primary"
|
||||||
type="success"
|
@click="formDialogRef.openDialog()">
|
||||||
class="ml10"
|
新 增
|
||||||
@click="handleExport">
|
</el-button>
|
||||||
导 出
|
<el-button
|
||||||
</el-button>
|
icon="Download"
|
||||||
<right-toolbar
|
type="success"
|
||||||
v-model:showSearch="showSearch"
|
class="ml10"
|
||||||
class="ml10 mr20"
|
@click="handleExport">
|
||||||
style="float: right;"
|
导 出
|
||||||
@queryTable="getDataList">
|
</el-button>
|
||||||
|
<right-toolbar
|
||||||
|
v-model:showSearch="showSearch"
|
||||||
|
class="ml10"
|
||||||
|
@queryTable="getDataList">
|
||||||
<TableColumnControl
|
<TableColumnControl
|
||||||
ref="columnControlRef"
|
ref="columnControlRef"
|
||||||
:columns="tableColumns"
|
:columns="tableColumns"
|
||||||
@@ -130,34 +142,39 @@
|
|||||||
</template>
|
</template>
|
||||||
</TableColumnControl>
|
</TableColumnControl>
|
||||||
</right-toolbar>
|
</right-toolbar>
|
||||||
</div>
|
</div>
|
||||||
</el-row>
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table
|
<el-table
|
||||||
:data="state.dataList"
|
:data="state.dataList"
|
||||||
v-loading="state.loading"
|
v-loading="state.loading"
|
||||||
border
|
stripe
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle">
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
class="modern-table">
|
||||||
<template #header>
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
<el-icon><List /></el-icon>
|
<template #header>
|
||||||
</template>
|
<el-icon><List /></el-icon>
|
||||||
</el-table-column>
|
</template>
|
||||||
<el-table-column
|
<template #default="{ $index }">
|
||||||
v-for="col in visibleColumnsSorted"
|
{{ $index + 1 + ((state.pagination?.current || 1) - 1) * (state.pagination?.size || 10) }}
|
||||||
:key="col.prop"
|
</template>
|
||||||
:prop="col.prop"
|
</el-table-column>
|
||||||
:label="col.label"
|
<template v-for="col in visibleColumnsSorted" :key="col.prop || col.label">
|
||||||
:width="col.width"
|
<el-table-column
|
||||||
:min-width="col.minWidth"
|
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
||||||
:show-overflow-tooltip="col.showOverflowTooltip !== false"
|
:prop="col.prop"
|
||||||
:align="col.align || 'center'">
|
:label="col.label"
|
||||||
<template #header>
|
:width="col.width"
|
||||||
<el-icon v-if="col.icon"><component :is="col.icon" /></el-icon>
|
:min-width="col.minWidth"
|
||||||
<span :style="{ marginLeft: col.icon ? '4px' : '0' }">{{ col.label }}</span>
|
:show-overflow-tooltip="col.showOverflowTooltip !== false"
|
||||||
</template>
|
: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'">
|
<template #default="scope" v-if="col.prop === 'schoolTerm'">
|
||||||
<el-tag size="small" type="primary" effect="plain">
|
<el-tag size="small" type="primary" effect="plain">
|
||||||
{{ formatSchoolTerm(scope.row.schoolTerm) }}
|
{{ formatSchoolTerm(scope.row.schoolTerm) }}
|
||||||
@@ -170,31 +187,41 @@
|
|||||||
<span>{{ formatAuditStatus(scope.row.auditStatus) }}</span>
|
<span>{{ formatAuditStatus(scope.row.auditStatus) }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||||
<template #default="scope">
|
<template #header>
|
||||||
<el-button
|
<el-icon><Setting /></el-icon>
|
||||||
icon="Edit"
|
<span style="margin-left: 4px">操作</span>
|
||||||
text
|
</template>
|
||||||
type="primary"
|
<template #default="scope">
|
||||||
@click="handleEdit(scope.row)">
|
<el-button
|
||||||
编辑
|
icon="Edit"
|
||||||
</el-button>
|
link
|
||||||
<el-button
|
type="primary"
|
||||||
icon="Delete"
|
@click="handleEdit(scope.row)">
|
||||||
text
|
编辑
|
||||||
type="danger"
|
</el-button>
|
||||||
@click="handleDelete(scope.row)">
|
<el-button
|
||||||
删除
|
icon="Delete"
|
||||||
</el-button>
|
link
|
||||||
|
type="danger"
|
||||||
|
@click="handleDelete(scope.row)">
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<template #empty>
|
||||||
|
<el-empty description="暂无数据" :image-size="120" />
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table>
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<pagination
|
<div class="pagination-wrapper">
|
||||||
@size-change="sizeChangeHandle"
|
<pagination
|
||||||
@current-change="currentChangeHandle"
|
@size-change="sizeChangeHandle"
|
||||||
v-bind="state.pagination" />
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 新增/编辑表单弹窗 -->
|
<!-- 新增/编辑表单弹窗 -->
|
||||||
@@ -203,8 +230,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="DormLiveApply">
|
<script setup lang="ts" name="DormLiveApply">
|
||||||
import { reactive, ref, onMounted, computed } from 'vue'
|
import { reactive, ref, onMounted } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||||
import { fetchList, delObj, exportData } from "/@/api/stuwork/dormliveapply";
|
import { fetchList, delObj, exportData } from "/@/api/stuwork/dormliveapply";
|
||||||
import { queryAllSchoolYear } from "/@/api/basic/basicyear";
|
import { queryAllSchoolYear } from "/@/api/basic/basicyear";
|
||||||
@@ -214,11 +240,10 @@ import { getClassListByRole } from "/@/api/basic/basicclass";
|
|||||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import FormDialog from './form.vue'
|
import FormDialog from './form.vue'
|
||||||
import { List, Calendar, Clock, OfficeBuilding, Grid, Menu } from '@element-plus/icons-vue'
|
import { List, Calendar, Clock, OfficeBuilding, Grid, Menu, Search, Document, Setting, User } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
|
||||||
const searchFormRef = ref()
|
const searchFormRef = ref()
|
||||||
const columnControlRef = ref()
|
const columnControlRef = ref()
|
||||||
const showSearch = ref(true)
|
const showSearch = ref(true)
|
||||||
@@ -236,101 +261,21 @@ const tableColumns = [
|
|||||||
{ prop: 'schoolTerm', label: '学期', icon: Clock },
|
{ prop: 'schoolTerm', label: '学期', icon: Clock },
|
||||||
{ prop: 'deptName', label: '学院', icon: OfficeBuilding },
|
{ prop: 'deptName', label: '学院', icon: OfficeBuilding },
|
||||||
{ prop: 'classNo', label: '班级', icon: Grid },
|
{ prop: 'classNo', label: '班级', icon: Grid },
|
||||||
{ prop: 'realName', label: '姓名' },
|
{ prop: 'realName', label: '姓名', icon: User },
|
||||||
{ prop: 'roomNo', label: '宿舍号' },
|
{ prop: 'roomNo', label: '宿舍号', icon: OfficeBuilding },
|
||||||
{ prop: 'liveType', label: '留宿类型' },
|
{ prop: 'liveType', label: '留宿类型', icon: Calendar },
|
||||||
{ prop: 'liveDate', label: '留宿日期' },
|
{ prop: 'liveDate', label: '留宿日期', icon: Calendar },
|
||||||
{ prop: 'auditStatus', label: '审核状态' }
|
{ prop: 'auditStatus', label: '审核状态', icon: Setting }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制 Hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列变化处理
|
|
||||||
const handleColumnChange = (value: string[]) => {
|
|
||||||
visibleColumns.value = value
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = value.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列排序变化处理
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const visibleColumnsSorted = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: any[] = []
|
|
||||||
const unorderedColumns: any[] = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 搜索表单
|
// 搜索表单
|
||||||
const searchForm = reactive({
|
const searchForm = reactive({
|
||||||
@@ -522,7 +467,6 @@ const getAuditStatusDict = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
loadSavedConfig()
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getSchoolYearList()
|
getSchoolYearList()
|
||||||
getSchoolTermDict()
|
getSchoolTermDict()
|
||||||
@@ -533,3 +477,7 @@ onMounted(() => {
|
|||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch" class="search-form">
|
||||||
<el-form-item label="楼号" prop="buildingNo">
|
<el-form-item label="楼号" prop="buildingNo">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="searchForm.buildingNo"
|
v-model="searchForm.buildingNo"
|
||||||
@@ -50,75 +58,84 @@
|
|||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="FolderAdd"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
class="ml10"
|
宿舍整改记录列表
|
||||||
@click="formDialogRef.openDialog()">
|
</span>
|
||||||
新 增
|
<div class="header-actions">
|
||||||
</el-button>
|
<el-button
|
||||||
<el-button
|
icon="FolderAdd"
|
||||||
icon="Download"
|
type="primary"
|
||||||
type="warning"
|
@click="formDialogRef.openDialog()">
|
||||||
class="ml10"
|
新 增
|
||||||
@click="handleExport">
|
</el-button>
|
||||||
导 出
|
<el-button
|
||||||
</el-button>
|
icon="Download"
|
||||||
<right-toolbar
|
type="warning"
|
||||||
v-model:showSearch="showSearch"
|
class="ml10"
|
||||||
class="ml10"
|
@click="handleExport">
|
||||||
style="float: right;"
|
导 出
|
||||||
@queryTable="getDataList">
|
</el-button>
|
||||||
<TableColumnControl
|
<right-toolbar
|
||||||
ref="columnControlRef"
|
v-model:showSearch="showSearch"
|
||||||
:columns="tableColumns"
|
class="ml10"
|
||||||
v-model="visibleColumns"
|
@queryTable="getDataList">
|
||||||
trigger-type="default"
|
<TableColumnControl
|
||||||
trigger-circle
|
ref="columnControlRef"
|
||||||
@change="handleColumnChange"
|
:columns="tableColumns"
|
||||||
@order-change="handleColumnOrderChange"
|
v-model="visibleColumns"
|
||||||
>
|
trigger-type="default"
|
||||||
<template #trigger>
|
trigger-circle
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
@change="handleColumnChange"
|
||||||
<el-button circle style="margin-left: 0;">
|
@order-change="handleColumnOrderChange"
|
||||||
<el-icon><Menu /></el-icon>
|
>
|
||||||
</el-button>
|
<template #trigger>
|
||||||
</el-tooltip>
|
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
||||||
</template>
|
<el-button circle style="margin-left: 0;">
|
||||||
</TableColumnControl>
|
<el-icon><Menu /></el-icon>
|
||||||
</right-toolbar>
|
</el-button>
|
||||||
</div>
|
</el-tooltip>
|
||||||
</el-row>
|
</template>
|
||||||
|
</TableColumnControl>
|
||||||
|
</right-toolbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table
|
<el-table
|
||||||
:data="state.dataList"
|
:data="state.dataList"
|
||||||
v-loading="state.loading"
|
v-loading="state.loading"
|
||||||
border
|
stripe
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle"
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
@sort-change="sortChangeHandle">
|
@sort-change="sortChangeHandle"
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
class="modern-table">
|
||||||
<template #header>
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<template v-for="col in sortedTableColumns" :key="col.prop || col.label">
|
|
||||||
<el-table-column
|
|
||||||
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
|
||||||
:prop="col.prop"
|
|
||||||
:label="col.label"
|
|
||||||
show-overflow-tooltip
|
|
||||||
align="center">
|
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
<el-icon><List /></el-icon>
|
||||||
<span style="margin-left: 4px">{{ col.label }}</span>
|
|
||||||
</template>
|
</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 !== '操作'"
|
||||||
|
:prop="col.prop"
|
||||||
|
:label="col.label"
|
||||||
|
show-overflow-tooltip
|
||||||
|
align="center">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
||||||
|
<span style="margin-left: 4px">{{ col.label }}</span>
|
||||||
|
</template>
|
||||||
<!-- 整改时间列特殊模板 -->
|
<!-- 整改时间列特殊模板 -->
|
||||||
<template v-if="col.prop === 'reformDate'" #default="scope">
|
<template v-if="col.prop === 'reformDate'" #default="scope">
|
||||||
<span>{{ scope.row.reformDate ? scope.row.reformDate.split(' ')[0] : '-' }}</span>
|
<span>{{ scope.row.reformDate ? scope.row.reformDate.split(' ')[0] : '-' }}</span>
|
||||||
@@ -131,58 +148,61 @@
|
|||||||
:type-map="{ '合格': { type: 'success', effect: 'light' }, '不合格': { type: 'danger', effect: 'light' }, '未整改': { type: 'warning', effect: 'light' } }"
|
:type-map="{ '合格': { type: 'success', effect: 'light' }, '不合格': { type: 'danger', effect: 'light' }, '未整改': { type: 'warning', effect: 'light' } }"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column label="操作" width="350" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
link
|
||||||
|
type="success"
|
||||||
|
@click="handleSetStatus(scope.row, '合格')"
|
||||||
|
:disabled="isStatusDisabled(scope.row.reformStatus, '合格')">
|
||||||
|
合格
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
@click="handleSetStatus(scope.row, '不合格')"
|
||||||
|
:disabled="isStatusDisabled(scope.row.reformStatus, '不合格')">
|
||||||
|
不合格
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
link
|
||||||
|
type="warning"
|
||||||
|
@click="handleSetStatus(scope.row, '未整改')"
|
||||||
|
:disabled="isStatusDisabled(scope.row.reformStatus, '未整改')">
|
||||||
|
未整改
|
||||||
|
</el-button>
|
||||||
|
<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-column>
|
||||||
</template>
|
</el-table>
|
||||||
<el-table-column label="操作" width="350" align="center" fixed="right">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><Setting /></el-icon>
|
|
||||||
<span style="margin-left: 4px">操作</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
text
|
|
||||||
type="success"
|
|
||||||
@click="handleSetStatus(scope.row, '合格')"
|
|
||||||
:disabled="isStatusDisabled(scope.row.reformStatus, '合格')">
|
|
||||||
合格
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleSetStatus(scope.row, '不合格')"
|
|
||||||
:disabled="isStatusDisabled(scope.row.reformStatus, '不合格')">
|
|
||||||
不合格
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
text
|
|
||||||
type="warning"
|
|
||||||
@click="handleSetStatus(scope.row, '未整改')"
|
|
||||||
:disabled="isStatusDisabled(scope.row.reformStatus, '未整改')">
|
|
||||||
未整改
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Edit"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleEdit(scope.row)">
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Delete"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row)">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<pagination
|
<div class="pagination-wrapper">
|
||||||
@size-change="sizeChangeHandle"
|
<pagination
|
||||||
@current-change="currentChangeHandle"
|
@size-change="sizeChangeHandle"
|
||||||
v-bind="state.pagination" />
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 编辑、新增 -->
|
<!-- 编辑、新增 -->
|
||||||
@@ -204,8 +224,9 @@ import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
|||||||
|
|
||||||
// 引入组件
|
// 引入组件
|
||||||
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
||||||
import { List, OfficeBuilding, Grid, House, Calendar, Document, CircleCheck, EditPen, Setting, Menu } from '@element-plus/icons-vue'
|
import { List, OfficeBuilding, Grid, House, Calendar, Document, CircleCheck, EditPen, Setting, Menu, Search } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumnControl'
|
||||||
|
import '/@/styles/modern-page.scss'
|
||||||
import { defineAsyncComponent as defineStatusTag } from 'vue'
|
import { defineAsyncComponent as defineStatusTag } from 'vue'
|
||||||
const StatusTag = defineStatusTag(() => import('/@/components/StatusTag/index.vue'))
|
const StatusTag = defineStatusTag(() => import('/@/components/StatusTag/index.vue'))
|
||||||
|
|
||||||
@@ -241,104 +262,14 @@ const columnConfigMap: Record<string, { icon: any }> = {
|
|||||||
remarks: { icon: EditPen }
|
remarks: { icon: EditPen }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns, route.path)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadSavedConfig()
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const sortedTableColumns = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: typeof tableColumns = []
|
|
||||||
const unorderedColumns: typeof tableColumns = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 列显示控制函数
|
|
||||||
const checkColumnVisible = (prop: string): boolean => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return visibleColumns.value.includes(prop)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列变化
|
|
||||||
const handleColumnChange = (columns: string[]) => {
|
|
||||||
visibleColumns.value = columns
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = columns.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列排序变化
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 搜索表单
|
// 搜索表单
|
||||||
const searchForm = reactive({
|
const searchForm = reactive({
|
||||||
@@ -516,7 +447,6 @@ onMounted(() => {
|
|||||||
getReformStatusDict()
|
getReformStatusDict()
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (visibleColumns.value.length === 0) {
|
if (visibleColumns.value.length === 0) {
|
||||||
loadSavedConfig()
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch" class="search-form">
|
||||||
<el-form-item label="学院" prop="deptCode">
|
<el-form-item label="学院" prop="deptCode">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="searchForm.deptCode"
|
v-model="searchForm.deptCode"
|
||||||
@@ -67,84 +75,93 @@
|
|||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="FolderAdd"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
class="ml10"
|
宿舍房间列表
|
||||||
@click="formDialogRef.openDialog()">
|
</span>
|
||||||
新 增
|
<div class="header-actions">
|
||||||
</el-button>
|
<el-button
|
||||||
<el-button
|
icon="FolderAdd"
|
||||||
icon="OfficeBuilding"
|
type="primary"
|
||||||
type="success"
|
@click="formDialogRef.openDialog()">
|
||||||
class="ml10"
|
新 增
|
||||||
@click="handleDeptAssign">
|
</el-button>
|
||||||
学院安排
|
<el-button
|
||||||
</el-button>
|
icon="OfficeBuilding"
|
||||||
<el-button
|
type="success"
|
||||||
icon="Download"
|
class="ml10"
|
||||||
type="warning"
|
@click="handleDeptAssign">
|
||||||
class="ml10"
|
学院安排
|
||||||
@click="handleExport">
|
</el-button>
|
||||||
导 出
|
<el-button
|
||||||
</el-button>
|
icon="Download"
|
||||||
<right-toolbar
|
type="warning"
|
||||||
v-model:showSearch="showSearch"
|
class="ml10"
|
||||||
class="ml10"
|
@click="handleExport">
|
||||||
style="float: right;"
|
导 出
|
||||||
@queryTable="getDataList">
|
</el-button>
|
||||||
<TableColumnControl
|
<right-toolbar
|
||||||
ref="columnControlRef"
|
v-model:showSearch="showSearch"
|
||||||
:columns="tableColumns"
|
class="ml10"
|
||||||
v-model="visibleColumns"
|
@queryTable="getDataList">
|
||||||
trigger-type="default"
|
<TableColumnControl
|
||||||
trigger-circle
|
ref="columnControlRef"
|
||||||
@change="handleColumnChange"
|
:columns="tableColumns"
|
||||||
@order-change="handleColumnOrderChange"
|
v-model="visibleColumns"
|
||||||
>
|
trigger-type="default"
|
||||||
<template #trigger>
|
trigger-circle
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
@change="handleColumnChange"
|
||||||
<el-button circle style="margin-left: 0;">
|
@order-change="handleColumnOrderChange"
|
||||||
<el-icon><Menu /></el-icon>
|
>
|
||||||
</el-button>
|
<template #trigger>
|
||||||
</el-tooltip>
|
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
||||||
</template>
|
<el-button circle style="margin-left: 0;">
|
||||||
</TableColumnControl>
|
<el-icon><Menu /></el-icon>
|
||||||
</right-toolbar>
|
</el-button>
|
||||||
</div>
|
</el-tooltip>
|
||||||
</el-row>
|
</template>
|
||||||
|
</TableColumnControl>
|
||||||
|
</right-toolbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table
|
<el-table
|
||||||
:data="state.dataList"
|
:data="state.dataList"
|
||||||
v-loading="state.loading"
|
v-loading="state.loading"
|
||||||
border
|
stripe
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle"
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
@sort-change="sortChangeHandle"
|
@sort-change="sortChangeHandle"
|
||||||
@selection-change="handleSelectionChange">
|
@selection-change="handleSelectionChange"
|
||||||
<el-table-column type="selection" width="55" align="center" />
|
class="modern-table">
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
<el-table-column type="selection" width="55" align="center" />
|
||||||
<template #header>
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<template v-for="col in sortedTableColumns" :key="col.prop || col.label">
|
|
||||||
<el-table-column
|
|
||||||
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
|
||||||
:prop="col.prop"
|
|
||||||
:label="col.label"
|
|
||||||
show-overflow-tooltip
|
|
||||||
align="center">
|
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
<el-icon><List /></el-icon>
|
||||||
<span style="margin-left: 4px">{{ col.label }}</span>
|
|
||||||
</template>
|
</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 !== '操作'"
|
||||||
|
:prop="col.prop"
|
||||||
|
:label="col.label"
|
||||||
|
show-overflow-tooltip
|
||||||
|
align="center">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
||||||
|
<span style="margin-left: 4px">{{ col.label }}</span>
|
||||||
|
</template>
|
||||||
<!-- 几人间列特殊模板 -->
|
<!-- 几人间列特殊模板 -->
|
||||||
<template v-if="col.prop === 'bedNum'" #default="scope">
|
<template v-if="col.prop === 'bedNum'" #default="scope">
|
||||||
<el-tag v-if="scope.row.bedNum" size="small" type="info" effect="plain">
|
<el-tag v-if="scope.row.bedNum" size="small" type="info" effect="plain">
|
||||||
@@ -159,37 +176,40 @@
|
|||||||
</el-tag>
|
</el-tag>
|
||||||
<span v-else>-</span>
|
<span v-else>-</span>
|
||||||
</template>
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</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-column>
|
||||||
</template>
|
</el-table>
|
||||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><Setting /></el-icon>
|
|
||||||
<span style="margin-left: 4px">操作</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
icon="Edit"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleEdit(scope.row)">
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Delete"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row)">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<pagination
|
<div class="pagination-wrapper">
|
||||||
@size-change="sizeChangeHandle"
|
<pagination
|
||||||
@current-change="currentChangeHandle"
|
@size-change="sizeChangeHandle"
|
||||||
v-bind="state.pagination" />
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 编辑、新增 -->
|
<!-- 编辑、新增 -->
|
||||||
@@ -241,8 +261,9 @@ import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
|||||||
|
|
||||||
// 引入组件
|
// 引入组件
|
||||||
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
||||||
import { List, OfficeBuilding, House, UserFilled, EditPen, Setting, Menu, Calendar } from '@element-plus/icons-vue'
|
import { List, OfficeBuilding, House, UserFilled, EditPen, Setting, Menu, Calendar, Search, Document } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumnControl'
|
||||||
|
import '/@/styles/modern-page.scss'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -269,104 +290,14 @@ const columnConfigMap: Record<string, { icon: any }> = {
|
|||||||
remarks: { icon: EditPen }
|
remarks: { icon: EditPen }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns, route.path)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadSavedConfig()
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const sortedTableColumns = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: typeof tableColumns = []
|
|
||||||
const unorderedColumns: typeof tableColumns = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 列显示控制函数
|
|
||||||
const checkColumnVisible = (prop: string): boolean => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return visibleColumns.value.includes(prop)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列变化
|
|
||||||
const handleColumnChange = (columns: string[]) => {
|
|
||||||
visibleColumns.value = columns
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = columns.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列排序变化
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
const searchFormRef = ref()
|
const searchFormRef = ref()
|
||||||
const showSearch = ref(true)
|
const showSearch = ref(true)
|
||||||
const deptList = ref<any[]>([])
|
const deptList = ref<any[]>([])
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch" class="search-form">
|
||||||
<el-form-item label="学院" prop="deptCode">
|
<el-form-item label="学院" prop="deptCode">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="searchForm.deptCode"
|
v-model="searchForm.deptCode"
|
||||||
@@ -101,99 +109,108 @@
|
|||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="FolderAdd"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
class="ml10"
|
宿舍学生列表
|
||||||
@click="formDialogRef.openDialog()">
|
</span>
|
||||||
新增住宿生
|
<div class="header-actions">
|
||||||
</el-button>
|
<el-button
|
||||||
<el-button
|
icon="FolderAdd"
|
||||||
icon="Printer"
|
type="primary"
|
||||||
type="success"
|
@click="formDialogRef.openDialog()">
|
||||||
class="ml10"
|
新增住宿生
|
||||||
@click="handlePrintCard">
|
</el-button>
|
||||||
打印宿舍卡
|
<el-button
|
||||||
</el-button>
|
icon="Printer"
|
||||||
<el-button
|
type="success"
|
||||||
icon="Switch"
|
class="ml10"
|
||||||
type="warning"
|
@click="handlePrintCard">
|
||||||
class="ml10"
|
打印宿舍卡
|
||||||
@click="handleRoomSwap">
|
</el-button>
|
||||||
宿舍互换
|
<el-button
|
||||||
</el-button>
|
icon="Switch"
|
||||||
<el-button
|
type="warning"
|
||||||
icon="Download"
|
class="ml10"
|
||||||
type="info"
|
@click="handleRoomSwap">
|
||||||
class="ml10"
|
宿舍互换
|
||||||
@click="handleExport">
|
</el-button>
|
||||||
导 出
|
<el-button
|
||||||
</el-button>
|
icon="Download"
|
||||||
<el-button
|
type="info"
|
||||||
icon="Document"
|
class="ml10"
|
||||||
type="primary"
|
@click="handleExport">
|
||||||
plain
|
导 出
|
||||||
class="ml10"
|
</el-button>
|
||||||
@click="handleExportList">
|
<el-button
|
||||||
名单导出
|
icon="Document"
|
||||||
</el-button>
|
type="primary"
|
||||||
<right-toolbar
|
plain
|
||||||
v-model:showSearch="showSearch"
|
class="ml10"
|
||||||
class="ml10"
|
@click="handleExportList">
|
||||||
style="float: right;"
|
名单导出
|
||||||
@queryTable="getDataList">
|
</el-button>
|
||||||
<TableColumnControl
|
<right-toolbar
|
||||||
ref="columnControlRef"
|
v-model:showSearch="showSearch"
|
||||||
:columns="tableColumns"
|
class="ml10"
|
||||||
v-model="visibleColumns"
|
@queryTable="getDataList">
|
||||||
trigger-type="default"
|
<TableColumnControl
|
||||||
trigger-circle
|
ref="columnControlRef"
|
||||||
@change="handleColumnChange"
|
:columns="tableColumns"
|
||||||
@order-change="handleColumnOrderChange"
|
v-model="visibleColumns"
|
||||||
>
|
trigger-type="default"
|
||||||
<template #trigger>
|
trigger-circle
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
@change="handleColumnChange"
|
||||||
<el-button circle style="margin-left: 0;">
|
@order-change="handleColumnOrderChange"
|
||||||
<el-icon><Menu /></el-icon>
|
>
|
||||||
</el-button>
|
<template #trigger>
|
||||||
</el-tooltip>
|
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
||||||
</template>
|
<el-button circle style="margin-left: 0;">
|
||||||
</TableColumnControl>
|
<el-icon><Menu /></el-icon>
|
||||||
</right-toolbar>
|
</el-button>
|
||||||
</div>
|
</el-tooltip>
|
||||||
</el-row>
|
</template>
|
||||||
|
</TableColumnControl>
|
||||||
|
</right-toolbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table
|
<el-table
|
||||||
:data="state.dataList"
|
:data="state.dataList"
|
||||||
v-loading="state.loading"
|
v-loading="state.loading"
|
||||||
border
|
stripe
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle"
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
@sort-change="sortChangeHandle">
|
@sort-change="sortChangeHandle"
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
class="modern-table">
|
||||||
<template #header>
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<template v-for="col in sortedTableColumns" :key="col.prop || col.label">
|
|
||||||
<el-table-column
|
|
||||||
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
|
||||||
:prop="col.prop"
|
|
||||||
:label="col.label"
|
|
||||||
show-overflow-tooltip
|
|
||||||
align="center"
|
|
||||||
:min-width="col.minWidth"
|
|
||||||
:width="col.width">
|
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><component :is="columnConfigMap[col.prop]?.icon || OfficeBuilding" /></el-icon>
|
<el-icon><List /></el-icon>
|
||||||
<span style="margin-left: 4px">{{ col.label }}</span>
|
|
||||||
</template>
|
</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 !== '操作'"
|
||||||
|
:prop="col.prop"
|
||||||
|
:label="col.label"
|
||||||
|
show-overflow-tooltip
|
||||||
|
align="center"
|
||||||
|
:min-width="col.minWidth"
|
||||||
|
:width="col.width">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><component :is="columnConfigMap[col.prop]?.icon || OfficeBuilding" /></el-icon>
|
||||||
|
<span style="margin-left: 4px">{{ col.label }}</span>
|
||||||
|
</template>
|
||||||
<!-- 床位号列特殊模板 -->
|
<!-- 床位号列特殊模板 -->
|
||||||
<template v-if="col.prop === 'bedNo'" #default="scope">
|
<template v-if="col.prop === 'bedNo'" #default="scope">
|
||||||
<el-tag v-if="scope.row.bedNo" size="small" type="info" effect="plain">
|
<el-tag v-if="scope.row.bedNo" size="small" type="info" effect="plain">
|
||||||
@@ -209,37 +226,47 @@
|
|||||||
:type-map="{ '1': { type: 'success', effect: 'light' }, '0': { type: 'info', effect: 'light' } }"
|
:type-map="{ '1': { type: 'success', effect: 'light' }, '0': { type: 'info', effect: 'light' } }"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
icon="Edit"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="handleEdit(scope.row)">
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="Switch"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="handleTransfer(scope.row)">
|
||||||
|
转宿
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="Delete"
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
@click="handleCheckout(scope.row)">
|
||||||
|
退宿
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</template>
|
</el-table>
|
||||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><Setting /></el-icon>
|
|
||||||
<span style="margin-left: 4px">操作</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
icon="Switch"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleTransfer(scope.row)">
|
|
||||||
转宿
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Delete"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleCheckout(scope.row)">
|
|
||||||
退宿
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<pagination
|
<div class="pagination-wrapper">
|
||||||
@size-change="sizeChangeHandle"
|
<pagination
|
||||||
@current-change="currentChangeHandle"
|
@size-change="sizeChangeHandle"
|
||||||
v-bind="state.pagination" />
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 新增/转宿表单弹窗 -->
|
<!-- 新增/转宿表单弹窗 -->
|
||||||
@@ -263,8 +290,9 @@ import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
|||||||
import FormDialog from './form.vue';
|
import FormDialog from './form.vue';
|
||||||
import TransferDialog from './transfer.vue';
|
import TransferDialog from './transfer.vue';
|
||||||
import TreeSelect from '/@/components/TreeSelect/index.vue';
|
import TreeSelect from '/@/components/TreeSelect/index.vue';
|
||||||
import { List, OfficeBuilding, House, Grid, UserFilled, Phone, CreditCard, Avatar, User, Setting, Menu } from '@element-plus/icons-vue'
|
import { List, OfficeBuilding, House, Grid, UserFilled, Phone, CreditCard, Avatar, User, Setting, Menu, Search, Document } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumnControl'
|
||||||
|
import '/@/styles/modern-page.scss'
|
||||||
import { defineAsyncComponent } from 'vue'
|
import { defineAsyncComponent } from 'vue'
|
||||||
const StatusTag = defineAsyncComponent(() => import('/@/components/StatusTag/index.vue'))
|
const StatusTag = defineAsyncComponent(() => import('/@/components/StatusTag/index.vue'))
|
||||||
|
|
||||||
@@ -309,104 +337,14 @@ const columnConfigMap: Record<string, { icon: any }> = {
|
|||||||
tel: { icon: Phone }
|
tel: { icon: Phone }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns, route.path)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadSavedConfig()
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const sortedTableColumns = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: typeof tableColumns = []
|
|
||||||
const unorderedColumns: typeof tableColumns = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 列显示控制函数
|
|
||||||
const checkColumnVisible = (prop: string): boolean => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return visibleColumns.value.includes(prop)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列变化
|
|
||||||
const handleColumnChange = (columns: string[]) => {
|
|
||||||
visibleColumns.value = columns
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = columns.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列排序变化
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 树形选择器配置
|
// 树形选择器配置
|
||||||
const treeProps = {
|
const treeProps = {
|
||||||
@@ -581,7 +519,6 @@ onMounted(() => {
|
|||||||
getDormRoomTreeListData()
|
getDormRoomTreeListData()
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (visibleColumns.value.length === 0) {
|
if (visibleColumns.value.length === 0) {
|
||||||
loadSavedConfig()
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch" class="search-form">
|
||||||
<el-form-item label="学院" prop="deptCode">
|
<el-form-item label="学院" prop="deptCode">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="searchForm.deptCode"
|
v-model="searchForm.deptCode"
|
||||||
@@ -45,61 +53,71 @@
|
|||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<right-toolbar
|
<div class="card-header">
|
||||||
v-model:showSearch="showSearch"
|
<span class="card-title">
|
||||||
class="ml10"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
style="float: right;"
|
宿舍异动记录列表
|
||||||
@queryTable="getDataList">
|
</span>
|
||||||
<TableColumnControl
|
<div class="header-actions">
|
||||||
ref="columnControlRef"
|
<right-toolbar
|
||||||
:columns="tableColumns"
|
v-model:showSearch="showSearch"
|
||||||
v-model="visibleColumns"
|
class="ml10"
|
||||||
trigger-type="default"
|
@queryTable="getDataList">
|
||||||
trigger-circle
|
<TableColumnControl
|
||||||
@change="handleColumnChange"
|
ref="columnControlRef"
|
||||||
@order-change="handleColumnOrderChange"
|
:columns="tableColumns"
|
||||||
>
|
v-model="visibleColumns"
|
||||||
<template #trigger>
|
trigger-type="default"
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
trigger-circle
|
||||||
<el-button circle style="margin-left: 0;">
|
@change="handleColumnChange"
|
||||||
<el-icon><Menu /></el-icon>
|
@order-change="handleColumnOrderChange"
|
||||||
</el-button>
|
>
|
||||||
</el-tooltip>
|
<template #trigger>
|
||||||
</template>
|
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
||||||
</TableColumnControl>
|
<el-button circle style="margin-left: 0;">
|
||||||
</right-toolbar>
|
<el-icon><Menu /></el-icon>
|
||||||
</div>
|
</el-button>
|
||||||
</el-row>
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
</TableColumnControl>
|
||||||
|
</right-toolbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table
|
<el-table
|
||||||
:data="state.dataList"
|
:data="state.dataList"
|
||||||
v-loading="state.loading"
|
v-loading="state.loading"
|
||||||
border
|
stripe
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle"
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
@sort-change="sortChangeHandle">
|
@sort-change="sortChangeHandle"
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
class="modern-table">
|
||||||
<template #header>
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<template v-for="col in sortedTableColumns" :key="col.prop || col.label">
|
|
||||||
<el-table-column
|
|
||||||
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
|
||||||
:prop="col.prop"
|
|
||||||
:label="col.label"
|
|
||||||
show-overflow-tooltip
|
|
||||||
align="center">
|
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
<el-icon><List /></el-icon>
|
||||||
<span style="margin-left: 4px">{{ col.label }}</span>
|
|
||||||
</template>
|
</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 !== '操作'"
|
||||||
|
:prop="col.prop"
|
||||||
|
:label="col.label"
|
||||||
|
show-overflow-tooltip
|
||||||
|
align="center">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
||||||
|
<span style="margin-left: 4px">{{ col.label }}</span>
|
||||||
|
</template>
|
||||||
<!-- 变动时间列特殊模板 -->
|
<!-- 变动时间列特殊模板 -->
|
||||||
<template v-if="col.prop === 'createTime'" #default="scope">
|
<template v-if="col.prop === 'createTime'" #default="scope">
|
||||||
<span>{{ scope.row.createTime ? scope.row.createTime.split(' ')[0] + ' ' + scope.row.createTime.split(' ')[1] : '-' }}</span>
|
<span>{{ scope.row.createTime ? scope.row.createTime.split(' ')[0] + ' ' + scope.row.createTime.split(' ')[1] : '-' }}</span>
|
||||||
@@ -124,30 +142,33 @@
|
|||||||
</el-tag>
|
</el-tag>
|
||||||
<span v-else>-</span>
|
<span v-else>-</span>
|
||||||
</template>
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column label="操作" width="100" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
icon="View"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="handleView(scope.row)">
|
||||||
|
查看
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</template>
|
</el-table>
|
||||||
<el-table-column label="操作" width="100" align="center" fixed="right">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><Setting /></el-icon>
|
|
||||||
<span style="margin-left: 4px">操作</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
icon="View"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleView(scope.row)">
|
|
||||||
查看
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<pagination
|
<div class="pagination-wrapper">
|
||||||
@size-change="sizeChangeHandle"
|
<pagination
|
||||||
@current-change="currentChangeHandle"
|
@size-change="sizeChangeHandle"
|
||||||
v-bind="state.pagination" />
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -161,8 +182,9 @@ import { getDeptList } from "/@/api/basic/basicclass";
|
|||||||
import { useMessage } from "/@/hooks/message";
|
import { useMessage } from "/@/hooks/message";
|
||||||
import { getDicts } from "/@/api/admin/dict";
|
import { getDicts } from "/@/api/admin/dict";
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import { List, Calendar, OfficeBuilding, Grid, CreditCard, Avatar, Collection, House, Setting, Menu } from '@element-plus/icons-vue'
|
import { List, Calendar, OfficeBuilding, Grid, CreditCard, Avatar, Collection, House, Setting, Menu, Search, Document } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumnControl'
|
||||||
|
import '/@/styles/modern-page.scss'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -200,104 +222,14 @@ const columnConfigMap: Record<string, { icon: any }> = {
|
|||||||
newBedNo: { icon: House }
|
newBedNo: { icon: House }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns, route.path)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadSavedConfig()
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const sortedTableColumns = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: typeof tableColumns = []
|
|
||||||
const unorderedColumns: typeof tableColumns = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 列显示控制函数
|
|
||||||
const checkColumnVisible = (prop: string): boolean => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return visibleColumns.value.includes(prop)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列变化
|
|
||||||
const handleColumnChange = (columns: string[]) => {
|
|
||||||
visibleColumns.value = columns
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = columns.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列排序变化
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 搜索表单
|
// 搜索表单
|
||||||
const searchForm = reactive({
|
const searchForm = reactive({
|
||||||
@@ -387,7 +319,6 @@ onMounted(() => {
|
|||||||
getChangeTypeDict()
|
getChangeTypeDict()
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (visibleColumns.value.length === 0) {
|
if (visibleColumns.value.length === 0) {
|
||||||
loadSavedConfig()
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,118 +1,130 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="FolderAdd"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
class="ml10"
|
门禁规则列表
|
||||||
@click="formDialogRef.openDialog()">
|
</span>
|
||||||
新 增
|
<div class="header-actions">
|
||||||
</el-button>
|
<el-button
|
||||||
<right-toolbar
|
icon="FolderAdd"
|
||||||
v-model:showSearch="showSearch"
|
type="primary"
|
||||||
class="ml10"
|
@click="formDialogRef.openDialog()">
|
||||||
style="float: right;"
|
新 增
|
||||||
@queryTable="getDataList">
|
</el-button>
|
||||||
<TableColumnControl
|
<right-toolbar
|
||||||
ref="columnControlRef"
|
v-model:showSearch="showSearch"
|
||||||
:columns="tableColumns"
|
class="ml10"
|
||||||
v-model="visibleColumns"
|
@queryTable="getDataList">
|
||||||
trigger-type="default"
|
<TableColumnControl
|
||||||
trigger-circle
|
ref="columnControlRef"
|
||||||
@change="handleColumnChange"
|
:columns="tableColumns"
|
||||||
@order-change="handleColumnOrderChange"
|
v-model="visibleColumns"
|
||||||
>
|
trigger-type="default"
|
||||||
<template #trigger>
|
trigger-circle
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
@change="handleColumnChange"
|
||||||
<el-button circle style="margin-left: 0;">
|
@order-change="handleColumnOrderChange"
|
||||||
<el-icon><Menu /></el-icon>
|
>
|
||||||
</el-button>
|
<template #trigger>
|
||||||
</el-tooltip>
|
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
||||||
</template>
|
<el-button circle style="margin-left: 0;">
|
||||||
</TableColumnControl>
|
<el-icon><Menu /></el-icon>
|
||||||
</right-toolbar>
|
</el-button>
|
||||||
</div>
|
</el-tooltip>
|
||||||
</el-row>
|
</template>
|
||||||
|
</TableColumnControl>
|
||||||
|
</right-toolbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table
|
<el-table
|
||||||
:data="state.dataList"
|
:data="state.dataList"
|
||||||
v-loading="state.loading"
|
v-loading="state.loading"
|
||||||
border
|
stripe
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle"
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
@sort-change="sortChangeHandle">
|
@sort-change="sortChangeHandle"
|
||||||
<el-table-column type="index" label="序号" align="center">
|
class="modern-table">
|
||||||
<template #header>
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<template v-for="col in sortedTableColumns" :key="col.prop || col.label">
|
|
||||||
<el-table-column
|
|
||||||
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
|
||||||
:prop="col.prop"
|
|
||||||
:label="col.label"
|
|
||||||
show-overflow-tooltip>
|
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Document" /></el-icon>
|
<el-icon><List /></el-icon>
|
||||||
<span style="margin-left: 4px">{{ col.label }}</span>
|
|
||||||
</template>
|
</template>
|
||||||
<!-- 假期模式列特殊模板 -->
|
<template #default="{ $index }">
|
||||||
<template v-if="col.prop === 'isHoliday'" #default="scope">
|
{{ $index + 1 + ((state.pagination?.current || 1) - 1) * (state.pagination?.size || 10) }}
|
||||||
<StatusTag
|
|
||||||
:value="scope.row.isHoliday"
|
|
||||||
:options="[{ label: '开启', value: '1' }, { label: '关闭', value: '0' }]"
|
|
||||||
:type-map="{ '1': { type: 'success', effect: 'light' }, '0': { type: 'info', effect: 'light' } }"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</template>
|
<template v-for="col in visibleColumnsSorted" :key="col.prop || col.label">
|
||||||
<el-table-column label="操作" align="center" fixed="right" width="280">
|
<el-table-column
|
||||||
<template #header>
|
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
||||||
<el-icon><Setting /></el-icon>
|
:prop="col.prop"
|
||||||
<span style="margin-left: 4px">操作</span>
|
:label="col.label"
|
||||||
|
show-overflow-tooltip>
|
||||||
|
<template #header>
|
||||||
|
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Document" /></el-icon>
|
||||||
|
<span style="margin-left: 4px">{{ col.label }}</span>
|
||||||
|
</template>
|
||||||
|
<!-- 假期模式列特殊模板 -->
|
||||||
|
<template v-if="col.prop === 'isHoliday'" #default="scope">
|
||||||
|
<StatusTag
|
||||||
|
:value="scope.row.isHoliday"
|
||||||
|
:options="[{ label: '开启', value: '1' }, { label: '关闭', value: '0' }]"
|
||||||
|
:type-map="{ '1': { type: 'success', effect: 'light' }, '0': { type: 'info', effect: 'light' } }"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
</template>
|
</template>
|
||||||
<template #default="scope">
|
<el-table-column label="操作" align="center" fixed="right" width="280">
|
||||||
<el-button
|
<template #header>
|
||||||
icon="Switch"
|
<el-icon><Setting /></el-icon>
|
||||||
text
|
<span style="margin-left: 4px">操作</span>
|
||||||
:type="scope.row.isHoliday === '1' ? 'warning' : 'success'"
|
</template>
|
||||||
@click="handleToggleHoliday(scope.row)">
|
<template #default="scope">
|
||||||
{{ scope.row.isHoliday === '1' ? '关闭假期模式' : '开启假期模式' }}
|
<el-button
|
||||||
</el-button>
|
icon="Switch"
|
||||||
<el-button
|
link
|
||||||
icon="View"
|
:type="scope.row.isHoliday === '1' ? 'warning' : 'success'"
|
||||||
text
|
@click="handleToggleHoliday(scope.row)">
|
||||||
type="primary"
|
{{ scope.row.isHoliday === '1' ? '关闭假期模式' : '开启假期模式' }}
|
||||||
@click="handleViewDetail(scope.row)">
|
</el-button>
|
||||||
查看详情
|
<el-button
|
||||||
</el-button>
|
icon="View"
|
||||||
<el-button
|
link
|
||||||
icon="Edit"
|
type="primary"
|
||||||
text
|
@click="handleViewDetail(scope.row)">
|
||||||
type="primary"
|
查看详情
|
||||||
@click="formDialogRef.openDialog(scope.row.id)">
|
</el-button>
|
||||||
编辑
|
<el-button
|
||||||
</el-button>
|
icon="Edit"
|
||||||
<el-button
|
link
|
||||||
icon="Delete"
|
type="primary"
|
||||||
text
|
@click="formDialogRef.openDialog(scope.row.id)">
|
||||||
type="danger"
|
编辑
|
||||||
@click="handleDelete([scope.row.id])">
|
</el-button>
|
||||||
删除
|
<el-button
|
||||||
</el-button>
|
icon="Delete"
|
||||||
</template>
|
link
|
||||||
</el-table-column>
|
type="danger"
|
||||||
</el-table>
|
@click="handleDelete([scope.row.id])">
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<pagination
|
<div class="pagination-wrapper">
|
||||||
@size-change="sizeChangeHandle"
|
<pagination
|
||||||
@current-change="currentChangeHandle"
|
@size-change="sizeChangeHandle"
|
||||||
v-bind="state.pagination" />
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 编辑、新增 -->
|
<!-- 编辑、新增 -->
|
||||||
@@ -155,7 +167,8 @@ import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
|||||||
// 引入组件
|
// 引入组件
|
||||||
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
||||||
import { List, Document, Grid, CircleCheck, Setting, Menu } from '@element-plus/icons-vue'
|
import { List, Document, Grid, CircleCheck, Setting, Menu } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumnControl'
|
||||||
|
import '/@/styles/modern-page.scss'
|
||||||
import { defineAsyncComponent as defineStatusTag } from 'vue'
|
import { defineAsyncComponent as defineStatusTag } from 'vue'
|
||||||
const StatusTag = defineStatusTag(() => import('/@/components/StatusTag/index.vue'))
|
const StatusTag = defineStatusTag(() => import('/@/components/StatusTag/index.vue'))
|
||||||
|
|
||||||
@@ -182,104 +195,14 @@ const columnConfigMap: Record<string, { icon: any }> = {
|
|||||||
isHoliday: { icon: CircleCheck }
|
isHoliday: { icon: CircleCheck }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns, route.path)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadSavedConfig()
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const sortedTableColumns = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: typeof tableColumns = []
|
|
||||||
const unorderedColumns: typeof tableColumns = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 列显示控制函数
|
|
||||||
const checkColumnVisible = (prop: string): boolean => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return visibleColumns.value.includes(prop)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列变化
|
|
||||||
const handleColumnChange = (columns: string[]) => {
|
|
||||||
visibleColumns.value = columns
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = columns.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列排序变化
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 配置 useTable
|
// 配置 useTable
|
||||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch" class="search-form">
|
||||||
<el-form-item label="学院" prop="deptCode">
|
<el-form-item label="学院" prop="deptCode">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="searchForm.deptCode"
|
v-model="searchForm.deptCode"
|
||||||
@@ -40,62 +48,72 @@
|
|||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<right-toolbar
|
<div class="card-header">
|
||||||
v-model:showSearch="showSearch"
|
<span class="card-title">
|
||||||
class="ml10"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
style="float: right;"
|
待办事项列表
|
||||||
@queryTable="getDataList">
|
</span>
|
||||||
<TableColumnControl
|
<div class="header-actions">
|
||||||
ref="columnControlRef"
|
<right-toolbar
|
||||||
:columns="tableColumns"
|
v-model:showSearch="showSearch"
|
||||||
v-model="visibleColumns"
|
class="ml10"
|
||||||
trigger-type="default"
|
@queryTable="getDataList">
|
||||||
trigger-circle
|
<TableColumnControl
|
||||||
@change="handleColumnChange"
|
ref="columnControlRef"
|
||||||
@order-change="handleColumnOrderChange"
|
:columns="tableColumns"
|
||||||
>
|
v-model="visibleColumns"
|
||||||
<template #trigger>
|
trigger-type="default"
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
trigger-circle
|
||||||
<el-button circle style="margin-left: 0;">
|
@change="handleColumnChange"
|
||||||
<el-icon><Menu /></el-icon>
|
@order-change="handleColumnOrderChange"
|
||||||
</el-button>
|
>
|
||||||
</el-tooltip>
|
<template #trigger>
|
||||||
</template>
|
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
||||||
</TableColumnControl>
|
<el-button circle style="margin-left: 0;">
|
||||||
</right-toolbar>
|
<el-icon><Menu /></el-icon>
|
||||||
</div>
|
</el-button>
|
||||||
</el-row>
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
</TableColumnControl>
|
||||||
|
</right-toolbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table
|
<el-table
|
||||||
:data="state.dataList"
|
:data="state.dataList"
|
||||||
v-loading="state.loading"
|
v-loading="state.loading"
|
||||||
border
|
stripe
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle">
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
class="modern-table">
|
||||||
<template #header>
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<template v-for="col in sortedTableColumns" :key="col.prop || col.label">
|
|
||||||
<el-table-column
|
|
||||||
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
|
||||||
:prop="col.prop"
|
|
||||||
:label="col.label"
|
|
||||||
show-overflow-tooltip
|
|
||||||
align="center"
|
|
||||||
:min-width="col.minWidth"
|
|
||||||
:width="col.width">
|
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
<el-icon><List /></el-icon>
|
||||||
<span style="margin-left: 4px">{{ col.label }}</span>
|
|
||||||
</template>
|
</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 !== '操作'"
|
||||||
|
:prop="col.prop"
|
||||||
|
:label="col.label"
|
||||||
|
show-overflow-tooltip
|
||||||
|
align="center"
|
||||||
|
:min-width="col.minWidth"
|
||||||
|
:width="col.width">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
||||||
|
<span style="margin-left: 4px">{{ col.label }}</span>
|
||||||
|
</template>
|
||||||
<!-- 数量列特殊模板 -->
|
<!-- 数量列特殊模板 -->
|
||||||
<template v-if="col.prop === 'nums'" #default="scope">
|
<template v-if="col.prop === 'nums'" #default="scope">
|
||||||
<el-tag type="warning" v-if="scope.row.nums > 0">{{ scope.row.nums }}</el-tag>
|
<el-tag type="warning" v-if="scope.row.nums > 0">{{ scope.row.nums }}</el-tag>
|
||||||
@@ -109,30 +127,33 @@
|
|||||||
<template v-else-if="col.prop === 'updateTime'" #default="scope">
|
<template v-else-if="col.prop === 'updateTime'" #default="scope">
|
||||||
<span>{{ parseTime(scope.row.updateTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
<span>{{ parseTime(scope.row.updateTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
icon="View"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="handleView(scope.row)">
|
||||||
|
查看详情
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</template>
|
</el-table>
|
||||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><Setting /></el-icon>
|
|
||||||
<span style="margin-left: 4px">操作</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
icon="View"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleView(scope.row)">
|
|
||||||
查看详情
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<pagination
|
<div class="pagination-wrapper">
|
||||||
@size-change="sizeChangeHandle"
|
<pagination
|
||||||
@current-change="currentChangeHandle"
|
@size-change="sizeChangeHandle"
|
||||||
v-bind="state.pagination" />
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -147,8 +168,9 @@ import { getClassListByRole } from "/@/api/basic/basicclass";
|
|||||||
import { useMessage } from "/@/hooks/message";
|
import { useMessage } from "/@/hooks/message";
|
||||||
import { parseTime } from "/@/utils/formatTime";
|
import { parseTime } from "/@/utils/formatTime";
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import { List, OfficeBuilding, Grid, Document, DataAnalysis, Calendar, Clock, Setting, Menu } from '@element-plus/icons-vue'
|
import { List, OfficeBuilding, Grid, Document, DataAnalysis, Calendar, Clock, Setting, Menu, Search } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumnControl'
|
||||||
|
import '/@/styles/modern-page.scss'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -178,104 +200,14 @@ const columnConfigMap: Record<string, { icon: any }> = {
|
|||||||
updateTime: { icon: Clock }
|
updateTime: { icon: Clock }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns, route.path)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadSavedConfig()
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const sortedTableColumns = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: typeof tableColumns = []
|
|
||||||
const unorderedColumns: typeof tableColumns = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 列显示控制函数
|
|
||||||
const checkColumnVisible = (prop: string): boolean => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return visibleColumns.value.includes(prop)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列变化
|
|
||||||
const handleColumnChange = (columns: string[]) => {
|
|
||||||
visibleColumns.value = columns
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = columns.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列排序变化
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 搜索表单
|
// 搜索表单
|
||||||
const searchForm = reactive({
|
const searchForm = reactive({
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="state.queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</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-form-item label="学年" prop="schoolYear">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="state.queryForm.schoolYear"
|
v-model="state.queryForm.schoolYear"
|
||||||
@@ -69,29 +77,34 @@
|
|||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="Plus"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
@click="formDialogRef.openDialog()">
|
文明班级列表
|
||||||
新增
|
</span>
|
||||||
</el-button>
|
<div class="header-actions">
|
||||||
<el-button
|
<el-button
|
||||||
icon="Upload"
|
icon="Plus"
|
||||||
type="primary"
|
type="primary"
|
||||||
class="ml10"
|
@click="formDialogRef.openDialog()">
|
||||||
@click="handleImport">
|
新增
|
||||||
导入
|
</el-button>
|
||||||
</el-button>
|
<el-button
|
||||||
<right-toolbar
|
icon="Upload"
|
||||||
v-model:showSearch="showSearch"
|
type="success"
|
||||||
class="ml10 mr20"
|
class="ml10"
|
||||||
style="float: right;"
|
@click="handleImport">
|
||||||
@queryTable="getDataList">
|
导入
|
||||||
|
</el-button>
|
||||||
|
<right-toolbar
|
||||||
|
v-model:showSearch="showSearch"
|
||||||
|
class="ml10"
|
||||||
|
@queryTable="getDataList">
|
||||||
<TableColumnControl
|
<TableColumnControl
|
||||||
ref="columnControlRef"
|
ref="columnControlRef"
|
||||||
:columns="tableColumns"
|
:columns="tableColumns"
|
||||||
@@ -110,34 +123,39 @@
|
|||||||
</template>
|
</template>
|
||||||
</TableColumnControl>
|
</TableColumnControl>
|
||||||
</right-toolbar>
|
</right-toolbar>
|
||||||
</div>
|
</div>
|
||||||
</el-row>
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table
|
<el-table
|
||||||
:data="state.dataList"
|
:data="state.dataList"
|
||||||
v-loading="state.loading"
|
v-loading="state.loading"
|
||||||
border
|
stripe
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle">
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
class="modern-table">
|
||||||
<template #header>
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
<el-icon><List /></el-icon>
|
<template #header>
|
||||||
</template>
|
<el-icon><List /></el-icon>
|
||||||
</el-table-column>
|
</template>
|
||||||
<el-table-column
|
<template #default="{ $index }">
|
||||||
v-for="col in visibleColumnsSorted"
|
{{ $index + 1 + ((state.pagination?.current || 1) - 1) * (state.pagination?.size || 10) }}
|
||||||
:key="col.prop"
|
</template>
|
||||||
:prop="col.prop"
|
</el-table-column>
|
||||||
:label="col.label"
|
<template v-for="col in visibleColumnsSorted" :key="col.prop || col.label">
|
||||||
:width="col.width"
|
<el-table-column
|
||||||
:min-width="col.minWidth"
|
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
||||||
:show-overflow-tooltip="col.showOverflowTooltip !== false"
|
:prop="col.prop"
|
||||||
:align="col.align || 'center'">
|
:label="col.label"
|
||||||
<template #header>
|
:width="col.width"
|
||||||
<el-icon v-if="col.icon"><component :is="col.icon" /></el-icon>
|
:min-width="col.minWidth"
|
||||||
<span :style="{ marginLeft: col.icon ? '4px' : '0' }">{{ col.label }}</span>
|
:show-overflow-tooltip="col.showOverflowTooltip !== false"
|
||||||
</template>
|
: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'">
|
<template #default="scope" v-if="col.prop === 'schoolTerm'">
|
||||||
<el-tag size="small" type="primary" effect="plain">
|
<el-tag size="small" type="primary" effect="plain">
|
||||||
{{ formatSchoolTerm(scope.row.schoolTerm) }}
|
{{ formatSchoolTerm(scope.row.schoolTerm) }}
|
||||||
@@ -150,35 +168,41 @@
|
|||||||
<span v-else>-</span>
|
<span v-else>-</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><Setting /></el-icon>
|
<el-icon><Setting /></el-icon>
|
||||||
<span style="margin-left: 4px">操作</span>
|
<span style="margin-left: 4px">操作</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>
|
||||||
|
<template #empty>
|
||||||
|
<el-empty description="暂无数据" :image-size="120" />
|
||||||
</template>
|
</template>
|
||||||
<template #default="scope">
|
</el-table>
|
||||||
<el-button
|
|
||||||
icon="Edit"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleEdit(scope.row)">
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Delete"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row)">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<pagination
|
<div class="pagination-wrapper">
|
||||||
@size-change="sizeChangeHandle"
|
<pagination
|
||||||
@current-change="currentChangeHandle"
|
@size-change="sizeChangeHandle"
|
||||||
v-bind="state.pagination" />
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 新增/编辑表单弹窗 -->
|
<!-- 新增/编辑表单弹窗 -->
|
||||||
@@ -227,8 +251,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="RewardClass">
|
<script setup lang="ts" name="RewardClass">
|
||||||
import { reactive, ref, onMounted, computed } from 'vue'
|
import { reactive, ref, onMounted } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||||
import { fetchList, delObj, importExcel } from "/@/api/stuwork/rewardclass";
|
import { fetchList, delObj, importExcel } from "/@/api/stuwork/rewardclass";
|
||||||
import { getDeptList } from "/@/api/basic/basicclass";
|
import { getDeptList } from "/@/api/basic/basicclass";
|
||||||
@@ -237,12 +260,11 @@ import { queryAllSchoolYear } from "/@/api/basic/basicyear";
|
|||||||
import { getDicts } from "/@/api/admin/dict";
|
import { getDicts } from "/@/api/admin/dict";
|
||||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import { UploadFilled, List, Calendar, Clock, OfficeBuilding, Grid, UserFilled, Trophy, EditPen, Setting, Menu } from '@element-plus/icons-vue'
|
import { UploadFilled, List, Calendar, Clock, OfficeBuilding, Grid, UserFilled, Trophy, EditPen, Setting, Menu, Search, Document } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
import FormDialog from './form.vue'
|
import FormDialog from './form.vue'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
|
||||||
const formDialogRef = ref()
|
const formDialogRef = ref()
|
||||||
const uploadRef = ref()
|
const uploadRef = ref()
|
||||||
const searchFormRef = ref()
|
const searchFormRef = ref()
|
||||||
@@ -267,94 +289,14 @@ const tableColumns = [
|
|||||||
{ prop: 'remarks', label: '备注', icon: EditPen, minWidth: 200 }
|
{ prop: 'remarks', label: '备注', icon: EditPen, minWidth: 200 }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制 Hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列变化处理
|
|
||||||
const handleColumnChange = (value: string[]) => {
|
|
||||||
visibleColumns.value = value
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = value.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列排序变化处理
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const visibleColumnsSorted = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: any[] = []
|
|
||||||
const unorderedColumns: any[] = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 表格样式
|
// 表格样式
|
||||||
const tableStyle = {
|
const tableStyle = {
|
||||||
@@ -549,7 +491,6 @@ const getClassListData = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
loadSavedConfig()
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getSchoolYearList()
|
getSchoolYearList()
|
||||||
getSchoolTermDict()
|
getSchoolTermDict()
|
||||||
@@ -559,5 +500,6 @@ onMounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="state.queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</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-form-item label="学年" prop="schoolYear">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="state.queryForm.schoolYear"
|
v-model="state.queryForm.schoolYear"
|
||||||
@@ -52,29 +60,34 @@
|
|||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="Plus"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
@click="formDialogRef.openDialog()">
|
文明宿舍列表
|
||||||
新增
|
</span>
|
||||||
</el-button>
|
<div class="header-actions">
|
||||||
<el-button
|
<el-button
|
||||||
icon="Upload"
|
icon="Plus"
|
||||||
type="primary"
|
type="primary"
|
||||||
class="ml10"
|
@click="formDialogRef.openDialog()">
|
||||||
@click="handleImport">
|
新增
|
||||||
导入
|
</el-button>
|
||||||
</el-button>
|
<el-button
|
||||||
<right-toolbar
|
icon="Upload"
|
||||||
v-model:showSearch="showSearch"
|
type="success"
|
||||||
class="ml10 mr20"
|
class="ml10"
|
||||||
style="float: right;"
|
@click="handleImport">
|
||||||
@queryTable="getDataList">
|
导入
|
||||||
|
</el-button>
|
||||||
|
<right-toolbar
|
||||||
|
v-model:showSearch="showSearch"
|
||||||
|
class="ml10"
|
||||||
|
@queryTable="getDataList">
|
||||||
<TableColumnControl
|
<TableColumnControl
|
||||||
ref="columnControlRef"
|
ref="columnControlRef"
|
||||||
:columns="tableColumns"
|
:columns="tableColumns"
|
||||||
@@ -93,75 +106,84 @@
|
|||||||
</template>
|
</template>
|
||||||
</TableColumnControl>
|
</TableColumnControl>
|
||||||
</right-toolbar>
|
</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="序号" 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 !== '操作'"
|
||||||
|
: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="操作" width="150" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</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>
|
</div>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 表格 -->
|
|
||||||
<el-table
|
|
||||||
:data="state.dataList"
|
|
||||||
v-loading="state.loading"
|
|
||||||
border
|
|
||||||
:cell-style="tableStyle.cellStyle"
|
|
||||||
:header-cell-style="tableStyle.headerCellStyle">
|
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
v-for="col in visibleColumnsSorted"
|
|
||||||
:key="col.prop"
|
|
||||||
: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>
|
|
||||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><Setting /></el-icon>
|
|
||||||
<span style="margin-left: 4px">操作</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
icon="Edit"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleEdit(scope.row)">
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Delete"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row)">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
|
||||||
<pagination
|
|
||||||
@size-change="sizeChangeHandle"
|
|
||||||
@current-change="currentChangeHandle"
|
|
||||||
v-bind="state.pagination" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 新增/编辑表单弹窗 -->
|
<!-- 新增/编辑表单弹窗 -->
|
||||||
@@ -218,9 +240,10 @@ import { queryAllSchoolYear } from "/@/api/basic/basicyear";
|
|||||||
import { getDicts } from "/@/api/admin/dict";
|
import { getDicts } from "/@/api/admin/dict";
|
||||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import { UploadFilled, List, Calendar, Clock, House, Trophy, EditPen, Setting, Menu } from '@element-plus/icons-vue'
|
import { UploadFilled, List, Calendar, Clock, House, Trophy, EditPen, Setting, Menu, Search, Document } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumnControl'
|
||||||
import FormDialog from './form.vue'
|
import FormDialog from './form.vue'
|
||||||
|
import '/@/styles/modern-page.scss'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -244,94 +267,14 @@ const tableColumns = [
|
|||||||
{ prop: 'remarks', label: '备注', icon: EditPen, minWidth: 200 }
|
{ prop: 'remarks', label: '备注', icon: EditPen, minWidth: 200 }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns, route.path)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列变化处理
|
|
||||||
const handleColumnChange = (value: string[]) => {
|
|
||||||
visibleColumns.value = value
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = value.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列排序变化处理
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const visibleColumnsSorted = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: any[] = []
|
|
||||||
const unorderedColumns: any[] = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 表格样式
|
// 表格样式
|
||||||
const tableStyle = {
|
const tableStyle = {
|
||||||
@@ -493,13 +436,9 @@ const getSchoolTermDict = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
loadSavedConfig()
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getSchoolYearList()
|
getSchoolYearList()
|
||||||
getSchoolTermDict()
|
getSchoolTermDict()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
</style>
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="state.queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form :model="state.queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList" class="search-form">
|
||||||
<el-form-item label="奖项类型" prop="ruleType">
|
<el-form-item label="奖项类型" prop="ruleType">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="state.queryForm.ruleType"
|
v-model="state.queryForm.ruleType"
|
||||||
@@ -23,109 +31,123 @@
|
|||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="Plus"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
@click="formDialogRef.openDialog()">
|
评优评先奖项列表
|
||||||
新增
|
</span>
|
||||||
</el-button>
|
<div class="header-actions">
|
||||||
<right-toolbar
|
<el-button
|
||||||
v-model:showSearch="showSearch"
|
icon="Plus"
|
||||||
class="ml10 mr20"
|
type="primary"
|
||||||
style="float: right;"
|
@click="formDialogRef.openDialog()">
|
||||||
@queryTable="getDataList">
|
新增
|
||||||
<TableColumnControl
|
</el-button>
|
||||||
ref="columnControlRef"
|
<right-toolbar
|
||||||
:columns="tableColumns"
|
v-model:showSearch="showSearch"
|
||||||
v-model="visibleColumns"
|
class="ml10"
|
||||||
trigger-type="default"
|
@queryTable="getDataList">
|
||||||
trigger-circle
|
<TableColumnControl
|
||||||
@change="handleColumnChange"
|
ref="columnControlRef"
|
||||||
@order-change="handleColumnOrderChange"
|
:columns="tableColumns"
|
||||||
>
|
v-model="visibleColumns"
|
||||||
<template #trigger>
|
trigger-type="default"
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
trigger-circle
|
||||||
<el-button circle style="margin-left: 0;">
|
@change="handleColumnChange"
|
||||||
<el-icon><Menu /></el-icon>
|
@order-change="handleColumnOrderChange"
|
||||||
</el-button>
|
>
|
||||||
</el-tooltip>
|
<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="序号" 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 !== '操作'"
|
||||||
|
: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>
|
||||||
</TableColumnControl>
|
<template #default="scope" v-if="col.prop === 'ruleName'">
|
||||||
</right-toolbar>
|
<el-tag v-if="scope.row.ruleName" size="small" type="warning" effect="light">
|
||||||
|
{{ scope.row.ruleName }}
|
||||||
|
</el-tag>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope" v-else-if="col.prop === 'ruleType'">
|
||||||
|
<el-tag size="small" type="info" effect="plain">
|
||||||
|
{{ formatRuleType(scope.row.ruleType) }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</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>
|
</div>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 表格 -->
|
|
||||||
<el-table
|
|
||||||
:data="state.dataList"
|
|
||||||
v-loading="state.loading"
|
|
||||||
border
|
|
||||||
:cell-style="tableStyle.cellStyle"
|
|
||||||
:header-cell-style="tableStyle.headerCellStyle">
|
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
v-for="col in visibleColumnsSorted"
|
|
||||||
:key="col.prop"
|
|
||||||
: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 === 'ruleName'">
|
|
||||||
<el-tag v-if="scope.row.ruleName" size="small" type="warning" effect="light">
|
|
||||||
{{ scope.row.ruleName }}
|
|
||||||
</el-tag>
|
|
||||||
<span v-else>-</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope" v-else-if="col.prop === 'ruleType'">
|
|
||||||
<el-tag size="small" type="info" effect="plain">
|
|
||||||
{{ formatRuleType(scope.row.ruleType) }}
|
|
||||||
</el-tag>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><Setting /></el-icon>
|
|
||||||
<span style="margin-left: 4px">操作</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
icon="Edit"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleEdit(scope.row)">
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Delete"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row)">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
|
||||||
<pagination
|
|
||||||
@size-change="sizeChangeHandle"
|
|
||||||
@current-change="currentChangeHandle"
|
|
||||||
v-bind="state.pagination" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 新增/编辑表单弹窗 -->
|
<!-- 新增/编辑表单弹窗 -->
|
||||||
@@ -142,8 +164,9 @@ import { getDicts } from "/@/api/admin/dict";
|
|||||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import FormDialog from './form.vue'
|
import FormDialog from './form.vue'
|
||||||
import { List, Trophy, Collection, EditPen, Setting, Menu } from '@element-plus/icons-vue'
|
import { List, Trophy, Collection, EditPen, Setting, Menu, Search, Document } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumnControl'
|
||||||
|
import '/@/styles/modern-page.scss'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -160,94 +183,14 @@ const tableColumns = [
|
|||||||
{ prop: 'remarks', label: '备注', icon: EditPen, minWidth: 200 }
|
{ prop: 'remarks', label: '备注', icon: EditPen, minWidth: 200 }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns, route.path)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列变化处理
|
|
||||||
const handleColumnChange = (value: string[]) => {
|
|
||||||
visibleColumns.value = value
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = value.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列排序变化处理
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const visibleColumnsSorted = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: any[] = []
|
|
||||||
const unorderedColumns: any[] = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 表格样式
|
// 表格样式
|
||||||
const tableStyle = {
|
const tableStyle = {
|
||||||
@@ -330,12 +273,8 @@ const getRuleTypeDict = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
loadSavedConfig()
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getRuleTypeDict()
|
getRuleTypeDict()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
</style>
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form :model="queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList" class="search-form">
|
||||||
<el-form-item label="学院" prop="deptCode">
|
<el-form-item label="学院" prop="deptCode">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="queryForm.deptCode"
|
v-model="queryForm.deptCode"
|
||||||
@@ -68,69 +76,79 @@
|
|||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="Download"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
class="ml10"
|
学生评优评先列表
|
||||||
@click="handleExport">
|
</span>
|
||||||
导出
|
<div class="header-actions">
|
||||||
</el-button>
|
<el-button
|
||||||
<right-toolbar
|
icon="Download"
|
||||||
v-model:showSearch="showSearch"
|
type="success"
|
||||||
class="ml10"
|
class="ml10"
|
||||||
style="float: right;"
|
@click="handleExport">
|
||||||
@queryTable="getDataList">
|
导出
|
||||||
<TableColumnControl
|
</el-button>
|
||||||
ref="columnControlRef"
|
<right-toolbar
|
||||||
:columns="tableColumns"
|
v-model:showSearch="showSearch"
|
||||||
v-model="visibleColumns"
|
class="ml10"
|
||||||
trigger-type="default"
|
@queryTable="getDataList">
|
||||||
trigger-circle
|
<TableColumnControl
|
||||||
@change="handleColumnChange"
|
ref="columnControlRef"
|
||||||
@order-change="handleColumnOrderChange"
|
:columns="tableColumns"
|
||||||
>
|
v-model="visibleColumns"
|
||||||
<template #trigger>
|
trigger-type="default"
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
trigger-circle
|
||||||
<el-button circle style="margin-left: 0;">
|
@change="handleColumnChange"
|
||||||
<el-icon><Menu /></el-icon>
|
@order-change="handleColumnOrderChange"
|
||||||
</el-button>
|
>
|
||||||
</el-tooltip>
|
<template #trigger>
|
||||||
</template>
|
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
||||||
</TableColumnControl>
|
<el-button circle style="margin-left: 0;">
|
||||||
</right-toolbar>
|
<el-icon><Menu /></el-icon>
|
||||||
</div>
|
</el-button>
|
||||||
</el-row>
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
</TableColumnControl>
|
||||||
|
</right-toolbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table
|
<el-table
|
||||||
:data="dataList"
|
:data="dataList"
|
||||||
v-loading="loading"
|
v-loading="loading"
|
||||||
border
|
stripe
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle">
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
class="modern-table">
|
||||||
<template #header>
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<template v-for="col in sortedTableColumns" :key="col.prop || col.label">
|
|
||||||
<el-table-column
|
|
||||||
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
|
||||||
:prop="col.prop"
|
|
||||||
:label="col.label"
|
|
||||||
show-overflow-tooltip
|
|
||||||
align="center"
|
|
||||||
:min-width="col.minWidth"
|
|
||||||
:width="col.width">
|
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><component :is="columnConfigMap[col.prop]?.icon || OfficeBuilding" /></el-icon>
|
<el-icon><List /></el-icon>
|
||||||
<span style="margin-left: 4px">{{ col.label }}</span>
|
|
||||||
</template>
|
</template>
|
||||||
|
<template #default="{ $index }">
|
||||||
|
{{ $index + 1 }}
|
||||||
|
</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 !== '操作'"
|
||||||
|
:prop="col.prop"
|
||||||
|
:label="col.label"
|
||||||
|
show-overflow-tooltip
|
||||||
|
align="center"
|
||||||
|
:min-width="col.minWidth"
|
||||||
|
:width="col.width">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><component :is="columnConfigMap[col.prop]?.icon || OfficeBuilding" /></el-icon>
|
||||||
|
<span style="margin-left: 4px">{{ col.label }}</span>
|
||||||
|
</template>
|
||||||
<!-- 操行平均分列特殊模板 -->
|
<!-- 操行平均分列特殊模板 -->
|
||||||
<template v-if="col.prop === 'averageConduct'" #default="scope">
|
<template v-if="col.prop === 'averageConduct'" #default="scope">
|
||||||
<el-tag v-if="scope.row.averageConduct !== null && scope.row.averageConduct !== undefined" size="small" type="success" effect="plain">
|
<el-tag v-if="scope.row.averageConduct !== null && scope.row.averageConduct !== undefined" size="small" type="success" effect="plain">
|
||||||
@@ -156,9 +174,10 @@
|
|||||||
<template v-else-if="col.prop === 'upDateTime'" #default="scope">
|
<template v-else-if="col.prop === 'upDateTime'" #default="scope">
|
||||||
<span>{{ parseTime(scope.row.upDateTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
<span>{{ parseTime(scope.row.upDateTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</template>
|
</template>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -174,8 +193,9 @@ import { getDicts } from "/@/api/admin/dict";
|
|||||||
import { useMessage } from "/@/hooks/message";
|
import { useMessage } from "/@/hooks/message";
|
||||||
import { parseTime } from "/@/utils/formatTime";
|
import { parseTime } from "/@/utils/formatTime";
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import { List, OfficeBuilding, Grid, CreditCard, Avatar, DataAnalysis, Warning, Trophy, Clock, Menu } from '@element-plus/icons-vue'
|
import { List, OfficeBuilding, Grid, CreditCard, Avatar, DataAnalysis, Warning, Trophy, Clock, Menu, Search, Document } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumnControl'
|
||||||
|
import '/@/styles/modern-page.scss'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -215,104 +235,14 @@ const columnConfigMap: Record<string, { icon: any }> = {
|
|||||||
upDateTime: { icon: Clock }
|
upDateTime: { icon: Clock }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns, route.path)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadSavedConfig()
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const sortedTableColumns = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: typeof tableColumns = []
|
|
||||||
const unorderedColumns: typeof tableColumns = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 列显示控制函数
|
|
||||||
const checkColumnVisible = (prop: string): boolean => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return visibleColumns.value.includes(prop)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列变化
|
|
||||||
const handleColumnChange = (columns: string[]) => {
|
|
||||||
visibleColumns.value = columns
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = columns.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列排序变化
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 查询表单
|
// 查询表单
|
||||||
const queryForm = reactive({
|
const queryForm = reactive({
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="state.queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</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-form-item label="学年" prop="schoolYear">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="state.queryForm.schoolYear"
|
v-model="state.queryForm.schoolYear"
|
||||||
@@ -38,55 +46,65 @@
|
|||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="Plus"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
@click="handleAdd">
|
助学金批次列表
|
||||||
新增
|
</span>
|
||||||
</el-button>
|
<div class="header-actions">
|
||||||
<right-toolbar
|
<el-button
|
||||||
v-model:showSearch="showSearch"
|
icon="Plus"
|
||||||
class="ml10 mr20"
|
type="primary"
|
||||||
style="float: right;"
|
@click="handleAdd">
|
||||||
@queryTable="getDataList">
|
新增
|
||||||
<TableColumnControl
|
</el-button>
|
||||||
ref="columnControlRef"
|
<right-toolbar
|
||||||
:columns="tableColumns"
|
v-model:showSearch="showSearch"
|
||||||
v-model="visibleColumns"
|
class="ml10"
|
||||||
trigger-type="default"
|
@queryTable="getDataList">
|
||||||
trigger-circle
|
<TableColumnControl
|
||||||
@change="handleColumnChange"
|
ref="columnControlRef"
|
||||||
@order-change="handleColumnOrderChange"
|
:columns="tableColumns"
|
||||||
>
|
v-model="visibleColumns"
|
||||||
<template #trigger>
|
trigger-type="default"
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
trigger-circle
|
||||||
<el-button circle style="margin-left: 0;">
|
@change="handleColumnChange"
|
||||||
<el-icon><Menu /></el-icon>
|
@order-change="handleColumnOrderChange"
|
||||||
</el-button>
|
>
|
||||||
</el-tooltip>
|
<template #trigger>
|
||||||
</template>
|
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
||||||
</TableColumnControl>
|
<el-button circle style="margin-left: 0;">
|
||||||
</right-toolbar>
|
<el-icon><Menu /></el-icon>
|
||||||
</div>
|
</el-button>
|
||||||
</el-row>
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
</TableColumnControl>
|
||||||
|
</right-toolbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table
|
<el-table
|
||||||
:data="state.dataList"
|
:data="state.dataList"
|
||||||
v-loading="state.loading"
|
v-loading="state.loading"
|
||||||
border
|
stripe
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle">
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
class="modern-table">
|
||||||
<template #header>
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
<el-icon><List /></el-icon>
|
<template #header>
|
||||||
</template>
|
<el-icon><List /></el-icon>
|
||||||
</el-table-column>
|
</template>
|
||||||
|
<template #default="{ $index }">
|
||||||
|
{{ $index + 1 + ((state.pagination?.current || 1) - 1) * (state.pagination?.size || 10) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
v-for="col in visibleColumnsSorted"
|
v-for="col in visibleColumnsSorted"
|
||||||
:key="col.prop"
|
:key="col.prop"
|
||||||
@@ -128,35 +146,38 @@
|
|||||||
<span>{{ parseTime(scope.row.endTime, '{y}-{m}-{d}') }}</span>
|
<span>{{ parseTime(scope.row.endTime, '{y}-{m}-{d}') }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><Setting /></el-icon>
|
<el-icon><Setting /></el-icon>
|
||||||
<span style="margin-left: 4px">操作</span>
|
<span style="margin-left: 4px">操作</span>
|
||||||
</template>
|
</template>
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button
|
<el-button
|
||||||
icon="Edit"
|
icon="Edit"
|
||||||
text
|
link
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="handleEdit(scope.row)">
|
@click="handleEdit(scope.row)">
|
||||||
编辑
|
编辑
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
icon="Delete"
|
icon="Delete"
|
||||||
text
|
link
|
||||||
type="danger"
|
type="danger"
|
||||||
@click="handleDelete(scope.row)">
|
@click="handleDelete(scope.row)">
|
||||||
删除
|
删除
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<pagination
|
<div class="pagination-wrapper">
|
||||||
@size-change="sizeChangeHandle"
|
<pagination
|
||||||
@current-change="currentChangeHandle"
|
@size-change="sizeChangeHandle"
|
||||||
v-bind="state.pagination" />
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 新增/编辑表单弹窗 -->
|
<!-- 新增/编辑表单弹窗 -->
|
||||||
@@ -177,9 +198,10 @@ import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
|||||||
import FormDialog from './form.vue'
|
import FormDialog from './form.vue'
|
||||||
import {
|
import {
|
||||||
List, Document, Calendar, Clock, Timer, Collection,
|
List, Document, Calendar, Clock, Timer, Collection,
|
||||||
Money, EditPen, Setting, Menu
|
Money, EditPen, Setting, Menu, Search
|
||||||
} from '@element-plus/icons-vue'
|
} from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
import '/@/styles/modern-page.scss'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -205,94 +227,14 @@ const tableColumns = [
|
|||||||
{ prop: 'remarks', label: '备注', icon: EditPen, minWidth: 150 }
|
{ prop: 'remarks', label: '备注', icon: EditPen, minWidth: 150 }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns, route.path)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列变化处理
|
|
||||||
const handleColumnChange = (value: string[]) => {
|
|
||||||
visibleColumns.value = value
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = value.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列排序变化处理
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const visibleColumnsSorted = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: any[] = []
|
|
||||||
const unorderedColumns: any[] = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 表格样式
|
// 表格样式
|
||||||
const tableStyle = {
|
const tableStyle = {
|
||||||
@@ -448,7 +390,6 @@ const getClassifyDict = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
loadSavedConfig()
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getSchoolYearList()
|
getSchoolYearList()
|
||||||
getSchoolTermDict()
|
getSchoolTermDict()
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="state.queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form :model="state.queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList" class="search-form">
|
||||||
<el-form-item label="学院" prop="deptCode">
|
<el-form-item label="学院" prop="deptCode">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="state.queryForm.deptCode"
|
v-model="state.queryForm.deptCode"
|
||||||
@@ -45,68 +53,78 @@
|
|||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="Plus"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
@click="formDialogRef.openDialog()">
|
学生社团列表
|
||||||
新增
|
</span>
|
||||||
</el-button>
|
<div class="header-actions">
|
||||||
<right-toolbar
|
<el-button
|
||||||
v-model:showSearch="showSearch"
|
icon="Plus"
|
||||||
class="ml10"
|
type="primary"
|
||||||
style="float: right;"
|
@click="formDialogRef.openDialog()">
|
||||||
@queryTable="getDataList">
|
新增
|
||||||
<TableColumnControl
|
</el-button>
|
||||||
ref="columnControlRef"
|
<right-toolbar
|
||||||
:columns="tableColumns"
|
v-model:showSearch="showSearch"
|
||||||
v-model="visibleColumns"
|
class="ml10"
|
||||||
trigger-type="default"
|
@queryTable="getDataList">
|
||||||
trigger-circle
|
<TableColumnControl
|
||||||
@change="handleColumnChange"
|
ref="columnControlRef"
|
||||||
@order-change="handleColumnOrderChange"
|
:columns="tableColumns"
|
||||||
>
|
v-model="visibleColumns"
|
||||||
<template #trigger>
|
trigger-type="default"
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
trigger-circle
|
||||||
<el-button circle style="margin-left: 0;">
|
@change="handleColumnChange"
|
||||||
<el-icon><Menu /></el-icon>
|
@order-change="handleColumnOrderChange"
|
||||||
</el-button>
|
>
|
||||||
</el-tooltip>
|
<template #trigger>
|
||||||
</template>
|
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
||||||
</TableColumnControl>
|
<el-button circle style="margin-left: 0;">
|
||||||
</right-toolbar>
|
<el-icon><Menu /></el-icon>
|
||||||
</div>
|
</el-button>
|
||||||
</el-row>
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
</TableColumnControl>
|
||||||
|
</right-toolbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table
|
<el-table
|
||||||
:data="state.dataList"
|
:data="state.dataList"
|
||||||
v-loading="state.loading"
|
v-loading="state.loading"
|
||||||
border
|
stripe
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle">
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
class="modern-table">
|
||||||
<template #header>
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<template v-for="col in sortedTableColumns" :key="col.prop || col.label">
|
|
||||||
<el-table-column
|
|
||||||
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
|
||||||
:prop="col.prop"
|
|
||||||
:label="col.label"
|
|
||||||
show-overflow-tooltip
|
|
||||||
align="center"
|
|
||||||
:min-width="col.minWidth"
|
|
||||||
:width="col.width">
|
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
<el-icon><List /></el-icon>
|
||||||
<span style="margin-left: 4px">{{ col.label }}</span>
|
|
||||||
</template>
|
</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 !== '操作'"
|
||||||
|
:prop="col.prop"
|
||||||
|
:label="col.label"
|
||||||
|
show-overflow-tooltip
|
||||||
|
align="center"
|
||||||
|
:min-width="col.minWidth"
|
||||||
|
:width="col.width">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
||||||
|
<span style="margin-left: 4px">{{ col.label }}</span>
|
||||||
|
</template>
|
||||||
<!-- 人数列特殊模板 -->
|
<!-- 人数列特殊模板 -->
|
||||||
<template v-if="col.prop === 'num'" #default="scope">
|
<template v-if="col.prop === 'num'" #default="scope">
|
||||||
<el-tag v-if="scope.row.num !== undefined && scope.row.num !== null" size="small" type="success" effect="plain">
|
<el-tag v-if="scope.row.num !== undefined && scope.row.num !== null" size="small" type="success" effect="plain">
|
||||||
@@ -124,44 +142,47 @@
|
|||||||
{{ formatType(scope.row.type) }}
|
{{ formatType(scope.row.type) }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</template>
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column label="操作" width="200" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
icon="User"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="handleViewMember(scope.row)">
|
||||||
|
查看成员
|
||||||
|
</el-button>
|
||||||
|
<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-column>
|
||||||
</template>
|
</el-table>
|
||||||
<el-table-column label="操作" width="200" align="center" fixed="right">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><Setting /></el-icon>
|
|
||||||
<span style="margin-left: 4px">操作</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
icon="User"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleViewMember(scope.row)">
|
|
||||||
查看成员
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Edit"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleEdit(scope.row)">
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Delete"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row)">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<pagination
|
<div class="pagination-wrapper">
|
||||||
@size-change="sizeChangeHandle"
|
<pagination
|
||||||
@current-change="currentChangeHandle"
|
@size-change="sizeChangeHandle"
|
||||||
v-bind="state.pagination" />
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 新增/编辑表单弹窗 -->
|
<!-- 新增/编辑表单弹窗 -->
|
||||||
@@ -186,9 +207,10 @@ import FormDialog from './form.vue'
|
|||||||
import MemberDialog from './member.vue'
|
import MemberDialog from './member.vue'
|
||||||
import {
|
import {
|
||||||
List, Trophy, OfficeBuilding, User, UserFilled, Calendar,
|
List, Trophy, OfficeBuilding, User, UserFilled, Calendar,
|
||||||
Phone, Collection, Document, Files, Setting, Menu
|
Phone, Collection, Document, Files, Setting, Menu, Search
|
||||||
} from '@element-plus/icons-vue'
|
} from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
import '/@/styles/modern-page.scss'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -228,104 +250,14 @@ const columnConfigMap: Record<string, { icon: any }> = {
|
|||||||
ruleNote: { icon: Files }
|
ruleNote: { icon: Files }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns, route.path)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadSavedConfig()
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const sortedTableColumns = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: typeof tableColumns = []
|
|
||||||
const unorderedColumns: typeof tableColumns = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 列显示控制函数
|
|
||||||
const checkColumnVisible = (prop: string): boolean => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return visibleColumns.value.includes(prop)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列变化
|
|
||||||
const handleColumnChange = (columns: string[]) => {
|
|
||||||
visibleColumns.value = columns
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = columns.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列排序变化
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 表格样式
|
// 表格样式
|
||||||
const tableStyle = {
|
const tableStyle = {
|
||||||
@@ -436,7 +368,6 @@ onMounted(() => {
|
|||||||
getTypeDict()
|
getTypeDict()
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (visibleColumns.value.length === 0) {
|
if (visibleColumns.value.length === 0) {
|
||||||
loadSavedConfig()
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="state.queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</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-form-item label="学年" prop="schoolYear">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="state.queryForm.schoolYear"
|
v-model="state.queryForm.schoolYear"
|
||||||
@@ -82,69 +90,78 @@
|
|||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="Plus"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
class="ml10"
|
学生关爱记录列表
|
||||||
@click="formDialogRef.openDialog()">
|
</span>
|
||||||
新增
|
<div class="header-actions">
|
||||||
</el-button>
|
<el-button
|
||||||
<right-toolbar
|
icon="Plus"
|
||||||
v-model:showSearch="showSearch"
|
type="primary"
|
||||||
class="ml10"
|
@click="formDialogRef.openDialog()">
|
||||||
style="float: right;"
|
新增
|
||||||
@queryTable="getDataList">
|
</el-button>
|
||||||
<TableColumnControl
|
<right-toolbar
|
||||||
ref="columnControlRef"
|
v-model:showSearch="showSearch"
|
||||||
:columns="tableColumns"
|
class="ml10"
|
||||||
v-model="visibleColumns"
|
@queryTable="getDataList">
|
||||||
trigger-type="default"
|
<TableColumnControl
|
||||||
trigger-circle
|
ref="columnControlRef"
|
||||||
@change="handleColumnChange"
|
:columns="tableColumns"
|
||||||
@order-change="handleColumnOrderChange"
|
v-model="visibleColumns"
|
||||||
>
|
trigger-type="default"
|
||||||
<template #trigger>
|
trigger-circle
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
@change="handleColumnChange"
|
||||||
<el-button circle style="margin-left: 0;">
|
@order-change="handleColumnOrderChange"
|
||||||
<el-icon><Menu /></el-icon>
|
>
|
||||||
</el-button>
|
<template #trigger>
|
||||||
</el-tooltip>
|
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
||||||
</template>
|
<el-button circle style="margin-left: 0;">
|
||||||
</TableColumnControl>
|
<el-icon><Menu /></el-icon>
|
||||||
</right-toolbar>
|
</el-button>
|
||||||
</div>
|
</el-tooltip>
|
||||||
</el-row>
|
</template>
|
||||||
|
</TableColumnControl>
|
||||||
|
</right-toolbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table
|
<el-table
|
||||||
:data="state.dataList"
|
:data="state.dataList"
|
||||||
v-loading="state.loading"
|
v-loading="state.loading"
|
||||||
border
|
stripe
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle">
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
class="modern-table">
|
||||||
<template #header>
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<template v-for="col in sortedTableColumns" :key="col.prop || col.label">
|
|
||||||
<el-table-column
|
|
||||||
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
|
||||||
:prop="col.prop"
|
|
||||||
:label="col.label"
|
|
||||||
show-overflow-tooltip
|
|
||||||
align="center"
|
|
||||||
:min-width="col.minWidth"
|
|
||||||
:width="col.width">
|
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
<el-icon><List /></el-icon>
|
||||||
<span style="margin-left: 4px">{{ col.label }}</span>
|
|
||||||
</template>
|
</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 !== '操作'"
|
||||||
|
:prop="col.prop"
|
||||||
|
:label="col.label"
|
||||||
|
show-overflow-tooltip
|
||||||
|
align="center"
|
||||||
|
:min-width="col.minWidth"
|
||||||
|
:width="col.width">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
||||||
|
<span style="margin-left: 4px">{{ col.label }}</span>
|
||||||
|
</template>
|
||||||
<!-- 学期列特殊模板 -->
|
<!-- 学期列特殊模板 -->
|
||||||
<template v-if="col.prop === 'schoolTerm'" #default="scope">
|
<template v-if="col.prop === 'schoolTerm'" #default="scope">
|
||||||
<el-tag size="small" type="primary" effect="plain">
|
<el-tag size="small" type="primary" effect="plain">
|
||||||
@@ -165,44 +182,47 @@
|
|||||||
<template v-else-if="col.prop === 'result'" #default="scope">
|
<template v-else-if="col.prop === 'result'" #default="scope">
|
||||||
<span>{{ scope.row.result || '-' }}</span>
|
<span>{{ scope.row.result || '-' }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column label="操作" width="250" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
icon="Edit"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="handleEdit(scope.row)">
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="Document"
|
||||||
|
link
|
||||||
|
type="success"
|
||||||
|
@click="handleResult(scope.row)">
|
||||||
|
干预结果
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="Delete"
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
@click="handleDelete(scope.row)">
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</template>
|
</el-table>
|
||||||
<el-table-column label="操作" width="250" align="center" fixed="right">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><Setting /></el-icon>
|
|
||||||
<span style="margin-left: 4px">操作</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
icon="Edit"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleEdit(scope.row)">
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Document"
|
|
||||||
text
|
|
||||||
type="success"
|
|
||||||
@click="handleResult(scope.row)">
|
|
||||||
干预结果
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Delete"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row)">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<pagination
|
<div class="pagination-wrapper">
|
||||||
@size-change="sizeChangeHandle"
|
<pagination
|
||||||
@current-change="currentChangeHandle"
|
@size-change="sizeChangeHandle"
|
||||||
v-bind="state.pagination" />
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 新增/编辑表单弹窗 -->
|
<!-- 新增/编辑表单弹窗 -->
|
||||||
@@ -226,8 +246,9 @@ import { useMessage, useMessageBox } from "/@/hooks/message";
|
|||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import FormDialog from './form.vue'
|
import FormDialog from './form.vue'
|
||||||
import ResultDialog from './result.vue'
|
import ResultDialog from './result.vue'
|
||||||
import { List, Calendar, Clock, OfficeBuilding, Grid, Avatar, UserFilled, Phone, Heart, Warning, CircleCheck, Setting, Menu } from '@element-plus/icons-vue'
|
import { List, Calendar, Clock, OfficeBuilding, Grid, Avatar, UserFilled, Phone, Heart, Warning, CircleCheck, Setting, Menu, Search, Document } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumnControl'
|
||||||
|
import '/@/styles/modern-page.scss'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -272,104 +293,14 @@ const columnConfigMap: Record<string, { icon: any }> = {
|
|||||||
result: { icon: CircleCheck }
|
result: { icon: CircleCheck }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns, route.path)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadSavedConfig()
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const sortedTableColumns = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: typeof tableColumns = []
|
|
||||||
const unorderedColumns: typeof tableColumns = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 列显示控制函数
|
|
||||||
const checkColumnVisible = (prop: string): boolean => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return visibleColumns.value.includes(prop)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列变化
|
|
||||||
const handleColumnChange = (columns: string[]) => {
|
|
||||||
visibleColumns.value = columns
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = columns.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列排序变化
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 配置 useTable
|
// 配置 useTable
|
||||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||||
@@ -535,7 +466,6 @@ onMounted(() => {
|
|||||||
getCareTypeDict()
|
getCareTypeDict()
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (visibleColumns.value.length === 0) {
|
if (visibleColumns.value.length === 0) {
|
||||||
loadSavedConfig()
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="state.queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</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-form-item label="学年" prop="schoolYear">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="state.queryForm.schoolYear"
|
v-model="state.queryForm.schoolYear"
|
||||||
@@ -90,76 +98,85 @@
|
|||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="Plus"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
class="ml10"
|
学生操行记录列表
|
||||||
@click="formDialogRef.openDialog()">
|
</span>
|
||||||
新增
|
<div class="header-actions">
|
||||||
</el-button>
|
<el-button
|
||||||
<el-button
|
icon="Plus"
|
||||||
icon="Upload"
|
type="primary"
|
||||||
type="primary"
|
@click="formDialogRef.openDialog()">
|
||||||
class="ml10"
|
新增
|
||||||
@click="handleImport">
|
</el-button>
|
||||||
导入
|
<el-button
|
||||||
</el-button>
|
icon="Upload"
|
||||||
<right-toolbar
|
type="success"
|
||||||
v-model:showSearch="showSearch"
|
class="ml10"
|
||||||
class="ml10"
|
@click="handleImport">
|
||||||
style="float: right;"
|
导入
|
||||||
@queryTable="getDataList">
|
</el-button>
|
||||||
<TableColumnControl
|
<right-toolbar
|
||||||
ref="columnControlRef"
|
v-model:showSearch="showSearch"
|
||||||
:columns="tableColumns"
|
class="ml10"
|
||||||
v-model="visibleColumns"
|
@queryTable="getDataList">
|
||||||
trigger-type="default"
|
<TableColumnControl
|
||||||
trigger-circle
|
ref="columnControlRef"
|
||||||
@change="handleColumnChange"
|
:columns="tableColumns"
|
||||||
@order-change="handleColumnOrderChange"
|
v-model="visibleColumns"
|
||||||
>
|
trigger-type="default"
|
||||||
<template #trigger>
|
trigger-circle
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
@change="handleColumnChange"
|
||||||
<el-button circle style="margin-left: 0;">
|
@order-change="handleColumnOrderChange"
|
||||||
<el-icon><Menu /></el-icon>
|
>
|
||||||
</el-button>
|
<template #trigger>
|
||||||
</el-tooltip>
|
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
||||||
</template>
|
<el-button circle style="margin-left: 0;">
|
||||||
</TableColumnControl>
|
<el-icon><Menu /></el-icon>
|
||||||
</right-toolbar>
|
</el-button>
|
||||||
</div>
|
</el-tooltip>
|
||||||
</el-row>
|
</template>
|
||||||
|
</TableColumnControl>
|
||||||
|
</right-toolbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table
|
<el-table
|
||||||
:data="state.dataList"
|
:data="state.dataList"
|
||||||
v-loading="state.loading"
|
v-loading="state.loading"
|
||||||
border
|
stripe
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle">
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
class="modern-table">
|
||||||
<template #header>
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<template v-for="col in sortedTableColumns" :key="col.prop || col.label">
|
|
||||||
<el-table-column
|
|
||||||
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
|
||||||
:prop="col.prop"
|
|
||||||
:label="col.label"
|
|
||||||
show-overflow-tooltip
|
|
||||||
align="center"
|
|
||||||
:min-width="col.minWidth"
|
|
||||||
:width="col.width">
|
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
<el-icon><List /></el-icon>
|
||||||
<span style="margin-left: 4px">{{ col.label }}</span>
|
|
||||||
</template>
|
</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 !== '操作'"
|
||||||
|
:prop="col.prop"
|
||||||
|
:label="col.label"
|
||||||
|
show-overflow-tooltip
|
||||||
|
align="center"
|
||||||
|
:min-width="col.minWidth"
|
||||||
|
:width="col.width">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
||||||
|
<span style="margin-left: 4px">{{ col.label }}</span>
|
||||||
|
</template>
|
||||||
<!-- 学期列特殊模板 -->
|
<!-- 学期列特殊模板 -->
|
||||||
<template v-if="col.prop === 'schoolTerm'" #default="scope">
|
<template v-if="col.prop === 'schoolTerm'" #default="scope">
|
||||||
<el-tag size="small" type="primary" effect="plain">
|
<el-tag size="small" type="primary" effect="plain">
|
||||||
@@ -189,37 +206,40 @@
|
|||||||
</el-button>
|
</el-button>
|
||||||
<span v-else>-</span>
|
<span v-else>-</span>
|
||||||
</template>
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</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-column>
|
||||||
</template>
|
</el-table>
|
||||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><Setting /></el-icon>
|
|
||||||
<span style="margin-left: 4px">操作</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
icon="Edit"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleEdit(scope.row)">
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Delete"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row)">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<pagination
|
<div class="pagination-wrapper">
|
||||||
@size-change="sizeChangeHandle"
|
<pagination
|
||||||
@current-change="currentChangeHandle"
|
@size-change="sizeChangeHandle"
|
||||||
v-bind="state.pagination" />
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 新增/编辑表单弹窗 -->
|
<!-- 新增/编辑表单弹窗 -->
|
||||||
@@ -270,8 +290,9 @@ import { getClassListByRole } from "/@/api/basic/basicclass";
|
|||||||
import { getDicts } from "/@/api/admin/dict";
|
import { getDicts } from "/@/api/admin/dict";
|
||||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import { UploadFilled, List, CreditCard, Calendar, Clock, OfficeBuilding, Grid, Avatar, Collection, Document, Setting, Menu } from '@element-plus/icons-vue'
|
import { UploadFilled, List, CreditCard, Calendar, Clock, OfficeBuilding, Grid, Avatar, Collection, Document, Setting, Menu, Search } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumnControl'
|
||||||
|
import '/@/styles/modern-page.scss'
|
||||||
import FormDialog from './form.vue'
|
import FormDialog from './form.vue'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
@@ -318,104 +339,14 @@ const columnConfigMap: Record<string, { icon: any }> = {
|
|||||||
attachment: { icon: Document }
|
attachment: { icon: Document }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns, route.path)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadSavedConfig()
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const sortedTableColumns = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: typeof tableColumns = []
|
|
||||||
const unorderedColumns: typeof tableColumns = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 列显示控制函数
|
|
||||||
const checkColumnVisible = (prop: string): boolean => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return visibleColumns.value.includes(prop)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列变化
|
|
||||||
const handleColumnChange = (columns: string[]) => {
|
|
||||||
visibleColumns.value = columns
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = columns.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列排序变化
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 配置 useTable
|
// 配置 useTable
|
||||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||||
@@ -627,7 +558,6 @@ onMounted(() => {
|
|||||||
getTypeDict()
|
getTypeDict()
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (visibleColumns.value.length === 0) {
|
if (visibleColumns.value.length === 0) {
|
||||||
loadSavedConfig()
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch" class="search-form">
|
||||||
<el-form-item label="发起部门" prop="deptCode">
|
<el-form-item label="发起部门" prop="deptCode">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="searchForm.deptCode"
|
v-model="searchForm.deptCode"
|
||||||
@@ -24,105 +32,118 @@
|
|||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="FolderAdd"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
class="ml10"
|
学生校内请假分组列表
|
||||||
@click="formDialogRef.openDialog()">
|
</span>
|
||||||
新 增
|
<div class="header-actions">
|
||||||
</el-button>
|
<el-button
|
||||||
<right-toolbar
|
icon="FolderAdd"
|
||||||
v-model:showSearch="showSearch"
|
type="primary"
|
||||||
class="ml10 mr20"
|
@click="formDialogRef.openDialog()">
|
||||||
style="float: right;"
|
新 增
|
||||||
@queryTable="getDataList">
|
</el-button>
|
||||||
<TableColumnControl
|
<right-toolbar
|
||||||
ref="columnControlRef"
|
v-model:showSearch="showSearch"
|
||||||
:columns="tableColumns"
|
class="ml10"
|
||||||
v-model="visibleColumns"
|
@queryTable="getDataList">
|
||||||
trigger-type="default"
|
<TableColumnControl
|
||||||
trigger-circle
|
ref="columnControlRef"
|
||||||
@change="handleColumnChange"
|
:columns="tableColumns"
|
||||||
@order-change="handleColumnOrderChange"
|
v-model="visibleColumns"
|
||||||
>
|
trigger-type="default"
|
||||||
<template #trigger>
|
trigger-circle
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
@change="handleColumnChange"
|
||||||
<el-button circle style="margin-left: 0;">
|
@order-change="handleColumnOrderChange"
|
||||||
<el-icon><Menu /></el-icon>
|
>
|
||||||
</el-button>
|
<template #trigger>
|
||||||
</el-tooltip>
|
<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="序号" 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 !== '操作'"
|
||||||
|
: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>
|
||||||
</TableColumnControl>
|
|
||||||
</right-toolbar>
|
|
||||||
</div>
|
|
||||||
</el-row>
|
|
||||||
|
|
||||||
<!-- 表格 -->
|
|
||||||
<el-table
|
|
||||||
:data="state.dataList"
|
|
||||||
v-loading="state.loading"
|
|
||||||
border
|
|
||||||
:cell-style="tableStyle.cellStyle"
|
|
||||||
:header-cell-style="tableStyle.headerCellStyle">
|
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
v-for="col in visibleColumnsSorted"
|
|
||||||
:key="col.prop"
|
|
||||||
: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 === 'startTime'">
|
<template #default="scope" v-if="col.prop === 'startTime'">
|
||||||
<span>{{ scope.row.startTime ? formatDateTime(scope.row.startTime) : '-' }}</span>
|
<span>{{ scope.row.startTime ? formatDateTime(scope.row.startTime) : '-' }}</span>
|
||||||
</template>
|
</template>
|
||||||
<template #default="scope" v-else-if="col.prop === 'endTime'">
|
<template #default="scope" v-else-if="col.prop === 'endTime'">
|
||||||
<span>{{ scope.row.endTime ? formatDateTime(scope.row.endTime) : '-' }}</span>
|
<span>{{ scope.row.endTime ? formatDateTime(scope.row.endTime) : '-' }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
<template #header>
|
||||||
<template #header>
|
<el-icon><Setting /></el-icon>
|
||||||
<el-icon><Setting /></el-icon>
|
<span style="margin-left: 4px">操作</span>
|
||||||
<span style="margin-left: 4px">操作</span>
|
</template>
|
||||||
</template>
|
<template #default="scope">
|
||||||
<template #default="scope">
|
<el-button
|
||||||
<el-button
|
icon="Edit"
|
||||||
icon="Edit"
|
link
|
||||||
text
|
type="primary"
|
||||||
type="primary"
|
@click="handleEdit(scope.row)">
|
||||||
@click="handleEdit(scope.row)">
|
编辑
|
||||||
编辑
|
</el-button>
|
||||||
</el-button>
|
<el-button
|
||||||
<el-button
|
icon="Delete"
|
||||||
icon="Delete"
|
link
|
||||||
text
|
type="danger"
|
||||||
type="danger"
|
@click="handleDelete(scope.row)">
|
||||||
@click="handleDelete(scope.row)">
|
删除
|
||||||
删除
|
</el-button>
|
||||||
</el-button>
|
</template>
|
||||||
</template>
|
</el-table-column>
|
||||||
</el-table-column>
|
</el-table>
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<pagination
|
<div class="pagination-wrapper">
|
||||||
@size-change="sizeChangeHandle"
|
<pagination
|
||||||
@current-change="currentChangeHandle"
|
@size-change="sizeChangeHandle"
|
||||||
v-bind="state.pagination" />
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 新增/编辑表单弹窗 -->
|
<!-- 新增/编辑表单弹窗 -->
|
||||||
@@ -139,8 +160,9 @@ import { getDeptListByLevelTwo } from "/@/api/basic/basicdept";
|
|||||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import FormDialog from './form.vue'
|
import FormDialog from './form.vue'
|
||||||
import { List, Calendar, Document, OfficeBuilding, Setting, Menu } from '@element-plus/icons-vue'
|
import { List, Calendar, Document, OfficeBuilding, Setting, Menu, Search } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumnControl'
|
||||||
|
import '/@/styles/modern-page.scss'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -158,94 +180,14 @@ const tableColumns = [
|
|||||||
{ prop: 'deptName', label: '发起部门', icon: OfficeBuilding }
|
{ prop: 'deptName', label: '发起部门', icon: OfficeBuilding }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns, route.path)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列变化处理
|
|
||||||
const handleColumnChange = (value: string[]) => {
|
|
||||||
visibleColumns.value = value
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = value.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列排序变化处理
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const visibleColumnsSorted = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: any[] = []
|
|
||||||
const unorderedColumns: any[] = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 搜索表单
|
// 搜索表单
|
||||||
const searchForm = reactive({
|
const searchForm = reactive({
|
||||||
@@ -325,7 +267,6 @@ const getDeptListData = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
loadSavedConfig()
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getDeptListData()
|
getDeptListData()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch" class="search-form">
|
||||||
<el-form-item label="学年" prop="schoolYear">
|
<el-form-item label="学年" prop="schoolYear">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="searchForm.schoolYear"
|
v-model="searchForm.schoolYear"
|
||||||
@@ -155,68 +163,77 @@
|
|||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="FolderAdd"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
class="ml10"
|
学生请假申请列表
|
||||||
@click="formDialogRef.openDialog()">
|
</span>
|
||||||
新 增
|
<div class="header-actions">
|
||||||
</el-button>
|
<el-button
|
||||||
<el-button
|
icon="FolderAdd"
|
||||||
icon="Download"
|
type="primary"
|
||||||
type="success"
|
@click="formDialogRef.openDialog()">
|
||||||
class="ml10"
|
新 增
|
||||||
@click="handleExport">
|
</el-button>
|
||||||
导 出
|
<el-button
|
||||||
</el-button>
|
icon="Download"
|
||||||
<right-toolbar
|
type="success"
|
||||||
v-model:showSearch="showSearch"
|
class="ml10"
|
||||||
class="ml10"
|
@click="handleExport">
|
||||||
style="float: right;"
|
导 出
|
||||||
@queryTable="getDataList">
|
</el-button>
|
||||||
<TableColumnControl
|
<right-toolbar
|
||||||
ref="columnControlRef"
|
v-model:showSearch="showSearch"
|
||||||
:columns="tableColumns"
|
class="ml10"
|
||||||
v-model="visibleColumns"
|
@queryTable="getDataList">
|
||||||
trigger-type="default"
|
<TableColumnControl
|
||||||
trigger-circle
|
ref="columnControlRef"
|
||||||
@change="handleColumnChange"
|
:columns="tableColumns"
|
||||||
@order-change="handleColumnOrderChange"
|
v-model="visibleColumns"
|
||||||
>
|
trigger-type="default"
|
||||||
<template #trigger>
|
trigger-circle
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
@change="handleColumnChange"
|
||||||
<el-button circle style="margin-left: 0;">
|
@order-change="handleColumnOrderChange"
|
||||||
<el-icon><Menu /></el-icon>
|
>
|
||||||
</el-button>
|
<template #trigger>
|
||||||
</el-tooltip>
|
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
||||||
</template>
|
<el-button circle style="margin-left: 0;">
|
||||||
</TableColumnControl>
|
<el-icon><Menu /></el-icon>
|
||||||
</right-toolbar>
|
</el-button>
|
||||||
</div>
|
</el-tooltip>
|
||||||
</el-row>
|
</template>
|
||||||
|
</TableColumnControl>
|
||||||
|
</right-toolbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table
|
<el-table
|
||||||
:data="state.dataList"
|
:data="state.dataList"
|
||||||
v-loading="state.loading"
|
v-loading="state.loading"
|
||||||
border
|
stripe
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle">
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
class="modern-table">
|
||||||
<template #header>
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
<el-icon><List /></el-icon>
|
<template #header>
|
||||||
</template>
|
<el-icon><List /></el-icon>
|
||||||
</el-table-column>
|
</template>
|
||||||
<template v-for="col in sortedTableColumns" :key="col.prop || col.label">
|
<template #default="{ $index }">
|
||||||
<el-table-column
|
{{ $index + 1 + ((state.pagination?.current || 1) - 1) * (state.pagination?.size || 10) }}
|
||||||
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
</template>
|
||||||
:prop="col.prop"
|
</el-table-column>
|
||||||
:label="col.label"
|
<template v-for="col in visibleColumnsSorted" :key="col.prop || col.label">
|
||||||
|
<el-table-column
|
||||||
|
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
||||||
|
:prop="col.prop"
|
||||||
|
:label="col.label"
|
||||||
show-overflow-tooltip
|
show-overflow-tooltip
|
||||||
align="center"
|
align="center"
|
||||||
:min-width="col.minWidth"
|
:min-width="col.minWidth"
|
||||||
@@ -287,28 +304,33 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</template>
|
</template>
|
||||||
<el-table-column label="操作" width="100" align="center" fixed="right">
|
</el-table-column>
|
||||||
<template #header>
|
|
||||||
<el-icon><Setting /></el-icon>
|
|
||||||
<span style="margin-left: 4px">操作</span>
|
|
||||||
</template>
|
</template>
|
||||||
<template #default="scope">
|
<el-table-column label="操作" width="100" align="center" fixed="right">
|
||||||
<el-button
|
<template #header>
|
||||||
icon="CircleClose"
|
<el-icon><Setting /></el-icon>
|
||||||
text
|
<span style="margin-left: 4px">操作</span>
|
||||||
type="warning"
|
</template>
|
||||||
@click="handleCancel(scope.row)">
|
<template #default="scope">
|
||||||
撤销
|
<el-button
|
||||||
</el-button>
|
icon="CircleClose"
|
||||||
</template>
|
link
|
||||||
</el-table-column>
|
type="warning"
|
||||||
</el-table>
|
@click="handleCancel(scope.row)">
|
||||||
|
撤销
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<pagination
|
<div class="pagination-wrapper">
|
||||||
@size-change="sizeChangeHandle"
|
<pagination
|
||||||
@current-change="currentChangeHandle"
|
@size-change="sizeChangeHandle"
|
||||||
v-bind="state.pagination" />
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 新增表单弹窗 -->
|
<!-- 新增表单弹窗 -->
|
||||||
@@ -328,8 +350,9 @@ import { getClassListByRole } from "/@/api/basic/basicclass";
|
|||||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import FormDialog from './form.vue'
|
import FormDialog from './form.vue'
|
||||||
import { List, Calendar, Clock, OfficeBuilding, Avatar, Collection, Document, House, Warning, CircleCheck, EditPen, Setting, Menu } from '@element-plus/icons-vue'
|
import { List, Calendar, Clock, OfficeBuilding, Avatar, Collection, Document, House, Warning, CircleCheck, EditPen, Setting, Menu, Search } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumnControl'
|
||||||
|
import '/@/styles/modern-page.scss'
|
||||||
import { defineAsyncComponent } from 'vue'
|
import { defineAsyncComponent } from 'vue'
|
||||||
const StatusTag = defineAsyncComponent(() => import('/@/components/StatusTag/index.vue'))
|
const StatusTag = defineAsyncComponent(() => import('/@/components/StatusTag/index.vue'))
|
||||||
|
|
||||||
@@ -385,104 +408,14 @@ const columnConfigMap: Record<string, { icon: any }> = {
|
|||||||
rejectReason: { icon: Warning }
|
rejectReason: { icon: Warning }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns, route.path)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadSavedConfig()
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const sortedTableColumns = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: typeof tableColumns = []
|
|
||||||
const unorderedColumns: typeof tableColumns = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 列显示控制函数
|
|
||||||
const checkColumnVisible = (prop: string): boolean => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return visibleColumns.value.includes(prop)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列变化
|
|
||||||
const handleColumnChange = (columns: string[]) => {
|
|
||||||
visibleColumns.value = columns
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = columns.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列排序变化
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 搜索表单
|
// 搜索表单
|
||||||
const searchForm = reactive({
|
const searchForm = reactive({
|
||||||
@@ -721,7 +654,6 @@ onMounted(() => {
|
|||||||
getAuditTypeDict()
|
getAuditTypeDict()
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (visibleColumns.value.length === 0) {
|
if (visibleColumns.value.length === 0) {
|
||||||
loadSavedConfig()
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch" class="search-form">
|
||||||
<el-form-item label="学年" prop="schoolYear">
|
<el-form-item label="学年" prop="schoolYear">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="searchForm.schoolYear"
|
v-model="searchForm.schoolYear"
|
||||||
@@ -106,76 +114,85 @@
|
|||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="FolderAdd"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
class="ml10"
|
学生处分记录列表
|
||||||
@click="formDialogRef.openDialog()">
|
</span>
|
||||||
新 增
|
<div class="header-actions">
|
||||||
</el-button>
|
<el-button
|
||||||
<el-button
|
icon="FolderAdd"
|
||||||
icon="Download"
|
type="primary"
|
||||||
type="success"
|
@click="formDialogRef.openDialog()">
|
||||||
class="ml10"
|
新 增
|
||||||
@click="handleExport">
|
</el-button>
|
||||||
导 出
|
<el-button
|
||||||
</el-button>
|
icon="Download"
|
||||||
<right-toolbar
|
type="success"
|
||||||
v-model:showSearch="showSearch"
|
class="ml10"
|
||||||
class="ml10"
|
@click="handleExport">
|
||||||
style="float: right;"
|
导 出
|
||||||
@queryTable="getDataList">
|
</el-button>
|
||||||
<TableColumnControl
|
<right-toolbar
|
||||||
ref="columnControlRef"
|
v-model:showSearch="showSearch"
|
||||||
:columns="tableColumns"
|
class="ml10"
|
||||||
v-model="visibleColumns"
|
@queryTable="getDataList">
|
||||||
trigger-type="default"
|
<TableColumnControl
|
||||||
trigger-circle
|
ref="columnControlRef"
|
||||||
@change="handleColumnChange"
|
:columns="tableColumns"
|
||||||
@order-change="handleColumnOrderChange"
|
v-model="visibleColumns"
|
||||||
>
|
trigger-type="default"
|
||||||
<template #trigger>
|
trigger-circle
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
@change="handleColumnChange"
|
||||||
<el-button circle style="margin-left: 0;">
|
@order-change="handleColumnOrderChange"
|
||||||
<el-icon><Menu /></el-icon>
|
>
|
||||||
</el-button>
|
<template #trigger>
|
||||||
</el-tooltip>
|
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
||||||
</template>
|
<el-button circle style="margin-left: 0;">
|
||||||
</TableColumnControl>
|
<el-icon><Menu /></el-icon>
|
||||||
</right-toolbar>
|
</el-button>
|
||||||
</div>
|
</el-tooltip>
|
||||||
</el-row>
|
</template>
|
||||||
|
</TableColumnControl>
|
||||||
|
</right-toolbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table
|
<el-table
|
||||||
:data="state.dataList"
|
:data="state.dataList"
|
||||||
v-loading="state.loading"
|
v-loading="state.loading"
|
||||||
border
|
stripe
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle">
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
class="modern-table">
|
||||||
<template #header>
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<template v-for="col in sortedTableColumns" :key="col.prop || col.label">
|
|
||||||
<el-table-column
|
|
||||||
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
|
||||||
:prop="col.prop"
|
|
||||||
:label="col.label"
|
|
||||||
show-overflow-tooltip
|
|
||||||
align="center"
|
|
||||||
:min-width="col.minWidth"
|
|
||||||
:width="col.width">
|
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
<el-icon><List /></el-icon>
|
||||||
<span style="margin-left: 4px">{{ col.label }}</span>
|
|
||||||
</template>
|
</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 !== '操作'"
|
||||||
|
:prop="col.prop"
|
||||||
|
:label="col.label"
|
||||||
|
show-overflow-tooltip
|
||||||
|
align="center"
|
||||||
|
:min-width="col.minWidth"
|
||||||
|
:width="col.width">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
||||||
|
<span style="margin-left: 4px">{{ col.label }}</span>
|
||||||
|
</template>
|
||||||
<!-- 学期列特殊模板 -->
|
<!-- 学期列特殊模板 -->
|
||||||
<template v-if="col.prop === 'schoolTerm'" #default="scope">
|
<template v-if="col.prop === 'schoolTerm'" #default="scope">
|
||||||
<el-tag size="small" type="primary" effect="plain">
|
<el-tag size="small" type="primary" effect="plain">
|
||||||
@@ -204,51 +221,54 @@
|
|||||||
:type-map="{ '1': { type: 'danger', effect: 'light' }, '2': { type: 'success', effect: 'light' }, '3': { type: 'info', effect: 'light' } }"
|
:type-map="{ '1': { type: 'danger', effect: 'light' }, '2': { type: 'success', effect: 'light' }, '3': { type: 'info', effect: 'light' } }"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column label="操作" width="250" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
icon="Document"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="handleReport(scope.row)">
|
||||||
|
思想汇报
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="CircleClose"
|
||||||
|
link
|
||||||
|
type="warning"
|
||||||
|
@click="handleCancel(scope.row)">
|
||||||
|
撤销处分
|
||||||
|
</el-button>
|
||||||
|
<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-column>
|
||||||
</template>
|
</el-table>
|
||||||
<el-table-column label="操作" width="250" align="center" fixed="right">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><Setting /></el-icon>
|
|
||||||
<span style="margin-left: 4px">操作</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
icon="Document"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleReport(scope.row)">
|
|
||||||
思想汇报
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="CircleClose"
|
|
||||||
text
|
|
||||||
type="warning"
|
|
||||||
@click="handleCancel(scope.row)">
|
|
||||||
撤销处分
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Edit"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleEdit(scope.row)">
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Delete"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row)">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<pagination
|
<div class="pagination-wrapper">
|
||||||
@size-change="sizeChangeHandle"
|
<pagination
|
||||||
@current-change="currentChangeHandle"
|
@size-change="sizeChangeHandle"
|
||||||
v-bind="state.pagination" />
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 新增/编辑表单弹窗 -->
|
<!-- 新增/编辑表单弹窗 -->
|
||||||
@@ -268,8 +288,9 @@ import { getClassListByRole } from "/@/api/basic/basicclass";
|
|||||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import FormDialog from './form.vue'
|
import FormDialog from './form.vue'
|
||||||
import { List, Calendar, Clock, OfficeBuilding, Grid, Avatar, CreditCard, Warning, Document, CircleCheck, Setting, Menu } from '@element-plus/icons-vue'
|
import { List, Calendar, Clock, OfficeBuilding, Grid, Avatar, CreditCard, Warning, Document, CircleCheck, Setting, Menu, Search } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumnControl'
|
||||||
|
import '/@/styles/modern-page.scss'
|
||||||
import { defineAsyncComponent } from 'vue'
|
import { defineAsyncComponent } from 'vue'
|
||||||
const StatusTag = defineAsyncComponent(() => import('/@/components/StatusTag/index.vue'))
|
const StatusTag = defineAsyncComponent(() => import('/@/components/StatusTag/index.vue'))
|
||||||
|
|
||||||
@@ -316,104 +337,14 @@ const columnConfigMap: Record<string, { icon: any }> = {
|
|||||||
publishStatus: { icon: CircleCheck }
|
publishStatus: { icon: CircleCheck }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns, route.path)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadSavedConfig()
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const sortedTableColumns = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: typeof tableColumns = []
|
|
||||||
const unorderedColumns: typeof tableColumns = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 列显示控制函数
|
|
||||||
const checkColumnVisible = (prop: string): boolean => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return visibleColumns.value.includes(prop)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列变化
|
|
||||||
const handleColumnChange = (columns: string[]) => {
|
|
||||||
visibleColumns.value = columns
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = columns.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列排序变化
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 搜索表单
|
// 搜索表单
|
||||||
const searchForm = reactive({
|
const searchForm = reactive({
|
||||||
@@ -682,7 +613,6 @@ onMounted(() => {
|
|||||||
getPublishStatusDict()
|
getPublishStatusDict()
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (visibleColumns.value.length === 0) {
|
if (visibleColumns.value.length === 0) {
|
||||||
loadSavedConfig()
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch" class="search-form">
|
||||||
<el-form-item label="系部代码" prop="deptCode">
|
<el-form-item label="系部代码" prop="deptCode">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="searchForm.deptCode"
|
v-model="searchForm.deptCode"
|
||||||
@@ -46,69 +54,78 @@
|
|||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="Plus"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
class="ml10"
|
学生临时请假列表
|
||||||
@click="formDialogRef.openDialog()">
|
</span>
|
||||||
新增
|
<div class="header-actions">
|
||||||
</el-button>
|
<el-button
|
||||||
<right-toolbar
|
icon="Plus"
|
||||||
v-model:showSearch="showSearch"
|
type="primary"
|
||||||
class="ml10 mr20"
|
@click="formDialogRef.openDialog()">
|
||||||
style="float: right;"
|
新增
|
||||||
@queryTable="getDataList">
|
</el-button>
|
||||||
<TableColumnControl
|
<right-toolbar
|
||||||
ref="columnControlRef"
|
v-model:showSearch="showSearch"
|
||||||
:columns="tableColumns"
|
class="ml10"
|
||||||
v-model="visibleColumns"
|
@queryTable="getDataList">
|
||||||
trigger-type="default"
|
<TableColumnControl
|
||||||
trigger-circle
|
ref="columnControlRef"
|
||||||
@change="handleColumnChange"
|
:columns="tableColumns"
|
||||||
@order-change="handleColumnOrderChange"
|
v-model="visibleColumns"
|
||||||
>
|
trigger-type="default"
|
||||||
<template #trigger>
|
trigger-circle
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
@change="handleColumnChange"
|
||||||
<el-button circle style="margin-left: 0;">
|
@order-change="handleColumnOrderChange"
|
||||||
<el-icon><Menu /></el-icon>
|
>
|
||||||
</el-button>
|
<template #trigger>
|
||||||
</el-tooltip>
|
<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="序号" 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 !== '操作'"
|
||||||
|
: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>
|
||||||
</TableColumnControl>
|
|
||||||
</right-toolbar>
|
|
||||||
</div>
|
|
||||||
</el-row>
|
|
||||||
|
|
||||||
<!-- 表格 -->
|
|
||||||
<el-table
|
|
||||||
:data="state.dataList"
|
|
||||||
v-loading="state.loading"
|
|
||||||
border
|
|
||||||
:cell-style="tableStyle.cellStyle"
|
|
||||||
:header-cell-style="tableStyle.headerCellStyle">
|
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
v-for="col in visibleColumnsSorted"
|
|
||||||
:key="col.prop"
|
|
||||||
: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'">
|
<template #default="scope" v-if="col.prop === 'schoolTerm'">
|
||||||
<el-tag size="small" type="primary" effect="plain">
|
<el-tag size="small" type="primary" effect="plain">
|
||||||
{{ formatSchoolTerm(scope.row.schoolTerm) }}
|
{{ formatSchoolTerm(scope.row.schoolTerm) }}
|
||||||
@@ -119,30 +136,34 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #default="scope" v-else-if="col.prop === 'endTime'">
|
<template #default="scope" v-else-if="col.prop === 'endTime'">
|
||||||
<span>{{ scope.row.endTime ? formatDateTime(scope.row.endTime) : '-' }}</span>
|
<span>{{ scope.row.endTime ? formatDateTime(scope.row.endTime) : '-' }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
<el-table-column label="操作" width="100" align="center" fixed="right">
|
||||||
<el-table-column label="操作" width="100" align="center" fixed="right">
|
<template #header>
|
||||||
<template #header>
|
<el-icon><Setting /></el-icon>
|
||||||
<el-icon><Setting /></el-icon>
|
<span style="margin-left: 4px">操作</span>
|
||||||
<span style="margin-left: 4px">操作</span>
|
</template>
|
||||||
</template>
|
<template #default="scope">
|
||||||
<template #default="scope">
|
<el-button
|
||||||
<el-button
|
icon="Delete"
|
||||||
icon="Delete"
|
link
|
||||||
text
|
type="danger"
|
||||||
type="danger"
|
@click="handleDelete(scope.row)">
|
||||||
@click="handleDelete(scope.row)">
|
删除
|
||||||
删除
|
</el-button>
|
||||||
</el-button>
|
</template>
|
||||||
</template>
|
</el-table-column>
|
||||||
</el-table-column>
|
</el-table>
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<pagination
|
<div class="pagination-wrapper">
|
||||||
@size-change="sizeChangeHandle"
|
<pagination
|
||||||
@current-change="currentChangeHandle"
|
@size-change="sizeChangeHandle"
|
||||||
v-bind="state.pagination" />
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 新增表单弹窗 -->
|
<!-- 新增表单弹窗 -->
|
||||||
@@ -161,8 +182,9 @@ import { getClassListByRole } from "/@/api/basic/basicclass";
|
|||||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import FormDialog from './form.vue'
|
import FormDialog from './form.vue'
|
||||||
import { List, Calendar, Clock, OfficeBuilding, Grid, CreditCard, Avatar, Document, UserFilled, Phone, EditPen, Setting, Menu } from '@element-plus/icons-vue'
|
import { List, Calendar, Clock, OfficeBuilding, Grid, CreditCard, Avatar, Document, UserFilled, Phone, EditPen, Setting, Menu, Search } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumnControl'
|
||||||
|
import '/@/styles/modern-page.scss'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -190,94 +212,14 @@ const tableColumns = [
|
|||||||
{ prop: 'remarks', label: '备注', icon: EditPen, minWidth: 150 }
|
{ prop: 'remarks', label: '备注', icon: EditPen, minWidth: 150 }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns, route.path)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列变化处理
|
|
||||||
const handleColumnChange = (value: string[]) => {
|
|
||||||
visibleColumns.value = value
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = value.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列排序变化处理
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const visibleColumnsSorted = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: any[] = []
|
|
||||||
const unorderedColumns: any[] = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 搜索表单
|
// 搜索表单
|
||||||
const searchForm = reactive({
|
const searchForm = reactive({
|
||||||
@@ -390,7 +332,6 @@ const getSchoolTermDict = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
loadSavedConfig()
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getDeptListData()
|
getDeptListData()
|
||||||
getClassListData()
|
getClassListData()
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch" class="search-form">
|
||||||
<el-form-item label="学年" prop="schoolYear">
|
<el-form-item label="学年" prop="schoolYear">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="searchForm.schoolYear"
|
v-model="searchForm.schoolYear"
|
||||||
@@ -96,76 +104,85 @@
|
|||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="FolderAdd"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
class="ml10"
|
学生异动记录列表
|
||||||
@click="formDialogRef.openDialog()">
|
</span>
|
||||||
批量新增异动
|
<div class="header-actions">
|
||||||
</el-button>
|
<el-button
|
||||||
<el-button
|
icon="FolderAdd"
|
||||||
icon="Download"
|
type="primary"
|
||||||
type="success"
|
@click="formDialogRef.openDialog()">
|
||||||
class="ml10"
|
批量新增异动
|
||||||
@click="handleExport">
|
</el-button>
|
||||||
导 出
|
<el-button
|
||||||
</el-button>
|
icon="Download"
|
||||||
<right-toolbar
|
type="success"
|
||||||
v-model:showSearch="showSearch"
|
class="ml10"
|
||||||
class="ml10"
|
@click="handleExport">
|
||||||
style="float: right;"
|
导 出
|
||||||
@queryTable="getDataList">
|
</el-button>
|
||||||
<TableColumnControl
|
<right-toolbar
|
||||||
ref="columnControlRef"
|
v-model:showSearch="showSearch"
|
||||||
:columns="tableColumns"
|
class="ml10"
|
||||||
v-model="visibleColumns"
|
@queryTable="getDataList">
|
||||||
trigger-type="default"
|
<TableColumnControl
|
||||||
trigger-circle
|
ref="columnControlRef"
|
||||||
@change="handleColumnChange"
|
:columns="tableColumns"
|
||||||
@order-change="handleColumnOrderChange"
|
v-model="visibleColumns"
|
||||||
>
|
trigger-type="default"
|
||||||
<template #trigger>
|
trigger-circle
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
@change="handleColumnChange"
|
||||||
<el-button circle style="margin-left: 0;">
|
@order-change="handleColumnOrderChange"
|
||||||
<el-icon><Menu /></el-icon>
|
>
|
||||||
</el-button>
|
<template #trigger>
|
||||||
</el-tooltip>
|
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
||||||
</template>
|
<el-button circle style="margin-left: 0;">
|
||||||
</TableColumnControl>
|
<el-icon><Menu /></el-icon>
|
||||||
</right-toolbar>
|
</el-button>
|
||||||
</div>
|
</el-tooltip>
|
||||||
</el-row>
|
</template>
|
||||||
|
</TableColumnControl>
|
||||||
|
</right-toolbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table
|
<el-table
|
||||||
:data="state.dataList"
|
:data="state.dataList"
|
||||||
v-loading="state.loading"
|
v-loading="state.loading"
|
||||||
border
|
stripe
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle">
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
class="modern-table">
|
||||||
<template #header>
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<template v-for="col in sortedTableColumns" :key="col.prop || col.label">
|
|
||||||
<el-table-column
|
|
||||||
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
|
||||||
:prop="col.prop"
|
|
||||||
:label="col.label"
|
|
||||||
show-overflow-tooltip
|
|
||||||
align="center"
|
|
||||||
:min-width="col.minWidth"
|
|
||||||
:width="col.width">
|
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
<el-icon><List /></el-icon>
|
||||||
<span style="margin-left: 4px">{{ col.label }}</span>
|
|
||||||
</template>
|
</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 !== '操作'"
|
||||||
|
:prop="col.prop"
|
||||||
|
:label="col.label"
|
||||||
|
show-overflow-tooltip
|
||||||
|
align="center"
|
||||||
|
:min-width="col.minWidth"
|
||||||
|
:width="col.width">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
||||||
|
<span style="margin-left: 4px">{{ col.label }}</span>
|
||||||
|
</template>
|
||||||
<!-- 学期列特殊模板 -->
|
<!-- 学期列特殊模板 -->
|
||||||
<template v-if="col.prop === 'schoolTerm'" #default="scope">
|
<template v-if="col.prop === 'schoolTerm'" #default="scope">
|
||||||
<el-tag size="small" type="primary" effect="plain">
|
<el-tag size="small" type="primary" effect="plain">
|
||||||
@@ -188,37 +205,40 @@
|
|||||||
{{ formatTurnYear(scope.row.turnYear) }}
|
{{ formatTurnYear(scope.row.turnYear) }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</template>
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</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-column>
|
||||||
</template>
|
</el-table>
|
||||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><Setting /></el-icon>
|
|
||||||
<span style="margin-left: 4px">操作</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
icon="Edit"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleEdit(scope.row)">
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Delete"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row)">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<pagination
|
<div class="pagination-wrapper">
|
||||||
@size-change="sizeChangeHandle"
|
<pagination
|
||||||
@current-change="currentChangeHandle"
|
@size-change="sizeChangeHandle"
|
||||||
v-bind="state.pagination" />
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 批量新增/编辑表单弹窗 -->
|
<!-- 批量新增/编辑表单弹窗 -->
|
||||||
@@ -238,8 +258,9 @@ import { list as getClassList } from "/@/api/basic/basicclass";
|
|||||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import FormDialog from './form.vue'
|
import FormDialog from './form.vue'
|
||||||
import { List, Calendar, Clock, OfficeBuilding, Grid, CreditCard, Avatar, Collection, Document, Setting, Menu } from '@element-plus/icons-vue'
|
import { List, Calendar, Clock, OfficeBuilding, Grid, CreditCard, Avatar, Collection, Document, Setting, Menu, Search } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumnControl'
|
||||||
|
import '/@/styles/modern-page.scss'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -284,104 +305,14 @@ const columnConfigMap: Record<string, { icon: any }> = {
|
|||||||
remarks: { icon: Document }
|
remarks: { icon: Document }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns, route.path)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadSavedConfig()
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const sortedTableColumns = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: typeof tableColumns = []
|
|
||||||
const unorderedColumns: typeof tableColumns = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 列显示控制函数
|
|
||||||
const checkColumnVisible = (prop: string): boolean => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return visibleColumns.value.includes(prop)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列变化
|
|
||||||
const handleColumnChange = (columns: string[]) => {
|
|
||||||
visibleColumns.value = columns
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = columns.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列排序变化
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 搜索表单
|
// 搜索表单
|
||||||
const searchForm = reactive({
|
const searchForm = reactive({
|
||||||
@@ -594,7 +525,6 @@ onMounted(() => {
|
|||||||
getTurnYearDict()
|
getTurnYearDict()
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (visibleColumns.value.length === 0) {
|
if (visibleColumns.value.length === 0) {
|
||||||
loadSavedConfig()
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,97 +1,114 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<right-toolbar
|
<div class="card-header">
|
||||||
v-model:showSearch="showSearch"
|
<span class="card-title">
|
||||||
class="ml10"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
style="float: right;"
|
学生会列表
|
||||||
@queryTable="getDataList">
|
</span>
|
||||||
<TableColumnControl
|
<div class="header-actions">
|
||||||
ref="columnControlRef"
|
<right-toolbar
|
||||||
:columns="tableColumns"
|
v-model:showSearch="showSearch"
|
||||||
v-model="visibleColumns"
|
class="ml10"
|
||||||
trigger-type="default"
|
@queryTable="getDataList">
|
||||||
trigger-circle
|
<TableColumnControl
|
||||||
@change="handleColumnChange"
|
ref="columnControlRef"
|
||||||
@order-change="handleColumnOrderChange"
|
:columns="tableColumns"
|
||||||
>
|
v-model="visibleColumns"
|
||||||
<template #trigger>
|
trigger-type="default"
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
trigger-circle
|
||||||
<el-button circle style="margin-left: 0;">
|
@change="handleColumnChange"
|
||||||
<el-icon><Menu /></el-icon>
|
@order-change="handleColumnOrderChange"
|
||||||
</el-button>
|
>
|
||||||
</el-tooltip>
|
<template #trigger>
|
||||||
</template>
|
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
||||||
</TableColumnControl>
|
<el-button circle style="margin-left: 0;">
|
||||||
</right-toolbar>
|
<el-icon><Menu /></el-icon>
|
||||||
</div>
|
</el-button>
|
||||||
</el-row>
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
</TableColumnControl>
|
||||||
|
</right-toolbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table
|
<el-table
|
||||||
:data="state.dataList"
|
:data="state.dataList"
|
||||||
v-loading="state.loading"
|
v-loading="state.loading"
|
||||||
border
|
stripe
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle">
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
class="modern-table">
|
||||||
<template #header>
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<template v-for="col in sortedTableColumns" :key="col.prop || col.label">
|
|
||||||
<el-table-column
|
|
||||||
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
|
||||||
:prop="col.prop"
|
|
||||||
:label="col.label"
|
|
||||||
show-overflow-tooltip
|
|
||||||
align="center"
|
|
||||||
:min-width="col.minWidth"
|
|
||||||
:width="col.width">
|
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
<el-icon><List /></el-icon>
|
||||||
<span style="margin-left: 4px">{{ col.label }}</span>
|
|
||||||
</template>
|
</template>
|
||||||
<!-- 类型列特殊模板 -->
|
<template #default="{ $index }">
|
||||||
<template v-if="col.prop === 'unionType'" #default="scope">
|
{{ $index + 1 + ((state.pagination?.current || 1) - 1) * (state.pagination?.size || 10) }}
|
||||||
<el-tag size="small" type="info" effect="plain">
|
|
||||||
{{ formatUnionType(scope.row.unionType) }}
|
|
||||||
</el-tag>
|
|
||||||
</template>
|
|
||||||
<!-- 排序列特殊模板 -->
|
|
||||||
<template v-else-if="col.prop === 'sort'" #default="scope">
|
|
||||||
<el-tag v-if="scope.row.sort !== undefined && scope.row.sort !== null" size="small" type="primary" effect="plain">
|
|
||||||
{{ scope.row.sort }}
|
|
||||||
</el-tag>
|
|
||||||
<span v-else>-</span>
|
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</template>
|
<template v-for="col in visibleColumnsSorted" :key="col.prop || col.label">
|
||||||
<el-table-column label="操作" width="100" align="center" fixed="right">
|
<el-table-column
|
||||||
<template #header>
|
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
||||||
<el-icon><Setting /></el-icon>
|
:prop="col.prop"
|
||||||
<span style="margin-left: 4px">操作</span>
|
:label="col.label"
|
||||||
|
show-overflow-tooltip
|
||||||
|
align="center"
|
||||||
|
:min-width="col.minWidth"
|
||||||
|
:width="col.width">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
||||||
|
<span style="margin-left: 4px">{{ col.label }}</span>
|
||||||
|
</template>
|
||||||
|
<!-- 类型列特殊模板 -->
|
||||||
|
<template v-if="col.prop === 'unionType'" #default="scope">
|
||||||
|
<el-tag size="small" type="info" effect="plain" round>
|
||||||
|
{{ formatUnionType(scope.row.unionType) }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
<!-- 排序列特殊模板 -->
|
||||||
|
<template v-else-if="col.prop === 'sort'" #default="scope">
|
||||||
|
<el-tag v-if="scope.row.sort !== undefined && scope.row.sort !== null" size="small" type="primary" effect="plain" round>
|
||||||
|
{{ scope.row.sort }}
|
||||||
|
</el-tag>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
</template>
|
</template>
|
||||||
<template #default="scope">
|
<el-table-column label="操作" width="100" align="center" fixed="right">
|
||||||
<el-button
|
<template #header>
|
||||||
icon="Edit"
|
<el-icon><Setting /></el-icon>
|
||||||
text
|
<span style="margin-left: 4px">操作</span>
|
||||||
type="primary"
|
</template>
|
||||||
@click="handleEdit(scope.row)">
|
<template #default="scope">
|
||||||
编辑
|
<el-button
|
||||||
</el-button>
|
icon="EditPen"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="handleEdit(scope.row)">
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<template #empty>
|
||||||
|
<el-empty description="暂无数据" :image-size="120">
|
||||||
|
</el-empty>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table>
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<pagination
|
<div class="pagination-wrapper">
|
||||||
@size-change="sizeChangeHandle"
|
<pagination
|
||||||
@current-change="currentChangeHandle"
|
@size-change="sizeChangeHandle"
|
||||||
v-bind="state.pagination" />
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 编辑表单弹窗 -->
|
<!-- 编辑表单弹窗 -->
|
||||||
@@ -100,8 +117,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="StuUnion">
|
<script setup lang="ts" name="StuUnion">
|
||||||
import { reactive, ref, onMounted, computed, nextTick } from 'vue'
|
import { reactive, ref, onMounted } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||||
import { fetchList } from "/@/api/stuwork/stuunion";
|
import { fetchList } from "/@/api/stuwork/stuunion";
|
||||||
import { useMessage } from "/@/hooks/message";
|
import { useMessage } from "/@/hooks/message";
|
||||||
@@ -109,12 +125,11 @@ import { getDicts } from "/@/api/admin/dict";
|
|||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import FormDialog from './form.vue'
|
import FormDialog from './form.vue'
|
||||||
import {
|
import {
|
||||||
List, OfficeBuilding, Collection, Location, UserFilled, Sort, Setting, Menu, Calendar
|
List, OfficeBuilding, Collection, Location, UserFilled, Sort, Setting, Menu, Calendar, Document
|
||||||
} from '@element-plus/icons-vue'
|
} from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
|
||||||
const formDialogRef = ref()
|
const formDialogRef = ref()
|
||||||
const columnControlRef = ref<any>()
|
const columnControlRef = ref<any>()
|
||||||
const showSearch = ref(false)
|
const showSearch = ref(false)
|
||||||
@@ -122,11 +137,11 @@ const unionTypeList = ref<any[]>([])
|
|||||||
|
|
||||||
// 表格列配置
|
// 表格列配置
|
||||||
const tableColumns = [
|
const tableColumns = [
|
||||||
{ prop: 'unionName', label: '学生会名称', minWidth: 200 },
|
{ prop: 'unionName', label: '学生会名称', icon: OfficeBuilding, minWidth: 200 },
|
||||||
{ prop: 'unionType', label: '类型', width: 120 },
|
{ prop: 'unionType', label: '类型', icon: Collection, width: 120 },
|
||||||
{ prop: 'address', label: '联系地址', minWidth: 200 },
|
{ prop: 'address', label: '联系地址', icon: Location, minWidth: 200 },
|
||||||
{ prop: 'maintainer', label: '负责人', width: 120 },
|
{ prop: 'maintainer', label: '负责人', icon: UserFilled, width: 120 },
|
||||||
{ prop: 'sort', label: '排序', width: 80 }
|
{ prop: 'sort', label: '排序', icon: Sort, width: 80 }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 列配置映射(用于图标)
|
// 列配置映射(用于图标)
|
||||||
@@ -138,104 +153,14 @@ const columnConfigMap: Record<string, { icon: any }> = {
|
|||||||
sort: { icon: Sort }
|
sort: { icon: Sort }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制 Hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadSavedConfig()
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const sortedTableColumns = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: typeof tableColumns = []
|
|
||||||
const unorderedColumns: typeof tableColumns = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 列显示控制函数
|
|
||||||
const checkColumnVisible = (prop: string): boolean => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return visibleColumns.value.includes(prop)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列变化
|
|
||||||
const handleColumnChange = (columns: string[]) => {
|
|
||||||
visibleColumns.value = columns
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = columns.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列排序变化
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 表格样式
|
// 表格样式
|
||||||
const tableStyle = {
|
const tableStyle = {
|
||||||
@@ -251,7 +176,7 @@ const state: BasicTableProps = reactive<BasicTableProps>({
|
|||||||
item: 'records',
|
item: 'records',
|
||||||
totalCount: 'total'
|
totalCount: 'total'
|
||||||
},
|
},
|
||||||
createdIsNeed: true // 页面加载时自动获取数据
|
createdIsNeed: true
|
||||||
})
|
})
|
||||||
|
|
||||||
// table hook
|
// table hook
|
||||||
@@ -296,14 +221,9 @@ const getUnionTypeDict = async () => {
|
|||||||
// 初始化
|
// 初始化
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getUnionTypeDict()
|
getUnionTypeDict()
|
||||||
nextTick(() => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
loadSavedConfig()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="state.queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form :model="state.queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList" class="search-form">
|
||||||
<el-form-item label="班级" prop="classCode">
|
<el-form-item label="班级" prop="classCode">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="state.queryForm.classCode"
|
v-model="state.queryForm.classCode"
|
||||||
@@ -59,82 +67,92 @@
|
|||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="Plus"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
@click="formDialogRef.openDialog()">
|
学生团员列表
|
||||||
新增
|
</span>
|
||||||
</el-button>
|
<div class="header-actions">
|
||||||
<el-button
|
<el-button
|
||||||
icon="Upload"
|
icon="Plus"
|
||||||
type="primary"
|
type="primary"
|
||||||
class="ml10"
|
@click="formDialogRef.openDialog()">
|
||||||
@click="handleImport">
|
新增
|
||||||
导入
|
</el-button>
|
||||||
</el-button>
|
<el-button
|
||||||
<el-button
|
icon="Upload"
|
||||||
icon="Download"
|
type="success"
|
||||||
type="primary"
|
class="ml10"
|
||||||
class="ml10"
|
@click="handleImport">
|
||||||
@click="handleExport">
|
导入
|
||||||
导出
|
</el-button>
|
||||||
</el-button>
|
<el-button
|
||||||
<right-toolbar
|
icon="Download"
|
||||||
v-model:showSearch="showSearch"
|
type="warning"
|
||||||
class="ml10"
|
class="ml10"
|
||||||
style="float: right;"
|
@click="handleExport">
|
||||||
@queryTable="getDataList">
|
导出
|
||||||
<TableColumnControl
|
</el-button>
|
||||||
ref="columnControlRef"
|
<right-toolbar
|
||||||
:columns="tableColumns"
|
v-model:showSearch="showSearch"
|
||||||
v-model="visibleColumns"
|
class="ml10"
|
||||||
trigger-type="default"
|
@queryTable="getDataList">
|
||||||
trigger-circle
|
<TableColumnControl
|
||||||
@change="handleColumnChange"
|
ref="columnControlRef"
|
||||||
@order-change="handleColumnOrderChange"
|
:columns="tableColumns"
|
||||||
>
|
v-model="visibleColumns"
|
||||||
<template #trigger>
|
trigger-type="default"
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
trigger-circle
|
||||||
<el-button circle style="margin-left: 0;">
|
@change="handleColumnChange"
|
||||||
<el-icon><Menu /></el-icon>
|
@order-change="handleColumnOrderChange"
|
||||||
</el-button>
|
>
|
||||||
</el-tooltip>
|
<template #trigger>
|
||||||
</template>
|
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
||||||
</TableColumnControl>
|
<el-button circle style="margin-left: 0;">
|
||||||
</right-toolbar>
|
<el-icon><Menu /></el-icon>
|
||||||
</div>
|
</el-button>
|
||||||
</el-row>
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
</TableColumnControl>
|
||||||
|
</right-toolbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table
|
<el-table
|
||||||
:data="state.dataList"
|
:data="state.dataList"
|
||||||
v-loading="state.loading"
|
v-loading="state.loading"
|
||||||
border
|
stripe
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle">
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
class="modern-table">
|
||||||
<template #header>
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<template v-for="col in sortedTableColumns" :key="col.prop || col.label">
|
|
||||||
<el-table-column
|
|
||||||
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
|
||||||
:prop="col.prop"
|
|
||||||
:label="col.label"
|
|
||||||
show-overflow-tooltip
|
|
||||||
align="center"
|
|
||||||
:min-width="col.minWidth"
|
|
||||||
:width="col.width">
|
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
<el-icon><List /></el-icon>
|
||||||
<span style="margin-left: 4px">{{ col.label }}</span>
|
|
||||||
</template>
|
</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 !== '操作'"
|
||||||
|
:prop="col.prop"
|
||||||
|
:label="col.label"
|
||||||
|
show-overflow-tooltip
|
||||||
|
align="center"
|
||||||
|
:min-width="col.minWidth"
|
||||||
|
:width="col.width">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
||||||
|
<span style="margin-left: 4px">{{ col.label }}</span>
|
||||||
|
</template>
|
||||||
<!-- 入团时间列特殊模板 -->
|
<!-- 入团时间列特殊模板 -->
|
||||||
<template v-if="col.prop === 'enterTime'" #default="scope">
|
<template v-if="col.prop === 'enterTime'" #default="scope">
|
||||||
<span>{{ parseTime(scope.row.enterTime, '{y}-{m}-{d}') }}</span>
|
<span>{{ parseTime(scope.row.enterTime, '{y}-{m}-{d}') }}</span>
|
||||||
@@ -146,37 +164,40 @@
|
|||||||
</el-tag>
|
</el-tag>
|
||||||
<span v-else>-</span>
|
<span v-else>-</span>
|
||||||
</template>
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</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-column>
|
||||||
</template>
|
</el-table>
|
||||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><Setting /></el-icon>
|
|
||||||
<span style="margin-left: 4px">操作</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
icon="Edit"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleEdit(scope.row)">
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Delete"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row)">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<pagination
|
<div class="pagination-wrapper">
|
||||||
@size-change="sizeChangeHandle"
|
<pagination
|
||||||
@current-change="currentChangeHandle"
|
@size-change="sizeChangeHandle"
|
||||||
v-bind="state.pagination" />
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 新增/编辑表单弹窗 -->
|
<!-- 新增/编辑表单弹窗 -->
|
||||||
@@ -227,8 +248,9 @@ import { getGradeList } from "/@/api/basic/basicclass";
|
|||||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||||
import { parseTime } from "/@/utils/formatTime";
|
import { parseTime } from "/@/utils/formatTime";
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import { UploadFilled, List, OfficeBuilding, Grid, CreditCard, Avatar, Phone, Calendar, Postcard, Briefcase, Setting, Menu } from '@element-plus/icons-vue'
|
import { UploadFilled, List, OfficeBuilding, Grid, CreditCard, Avatar, Phone, Calendar, Postcard, Briefcase, Setting, Menu, Search, Document } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable';
|
import { useTableColumnControl } from '/@/hooks/tableColumnControl'
|
||||||
|
import '/@/styles/modern-page.scss'
|
||||||
import type { UploadFile, UploadFiles } from 'element-plus';
|
import type { UploadFile, UploadFiles } from 'element-plus';
|
||||||
import FormDialog from './form.vue'
|
import FormDialog from './form.vue'
|
||||||
|
|
||||||
@@ -269,104 +291,14 @@ const columnConfigMap: Record<string, { icon: any }> = {
|
|||||||
position: { icon: Briefcase }
|
position: { icon: Briefcase }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns, route.path)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadSavedConfig()
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const sortedTableColumns = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: typeof tableColumns = []
|
|
||||||
const unorderedColumns: typeof tableColumns = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 列显示控制函数
|
|
||||||
const checkColumnVisible = (prop: string): boolean => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return visibleColumns.value.includes(prop)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列变化
|
|
||||||
const handleColumnChange = (columns: string[]) => {
|
|
||||||
visibleColumns.value = columns
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = columns.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列排序变化
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 表格样式
|
// 表格样式
|
||||||
const tableStyle = {
|
const tableStyle = {
|
||||||
@@ -526,7 +458,6 @@ onMounted(() => {
|
|||||||
getGradeListData()
|
getGradeListData()
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (visibleColumns.value.length === 0) {
|
if (visibleColumns.value.length === 0) {
|
||||||
loadSavedConfig()
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch" class="search-form">
|
||||||
<el-form-item label="学年" prop="schoolYear">
|
<el-form-item label="学年" prop="schoolYear">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="searchForm.schoolYear"
|
v-model="searchForm.schoolYear"
|
||||||
@@ -86,76 +94,85 @@
|
|||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="FolderAdd"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
class="ml10"
|
学生工学交替列表
|
||||||
@click="formDialogRef.openDialog()">
|
</span>
|
||||||
新 增
|
<div class="header-actions">
|
||||||
</el-button>
|
<el-button
|
||||||
<el-button
|
icon="FolderAdd"
|
||||||
icon="User"
|
type="primary"
|
||||||
type="success"
|
@click="formDialogRef.openDialog()">
|
||||||
class="ml10"
|
新 增
|
||||||
@click="teacherDialogRef.openDialog()">
|
</el-button>
|
||||||
指定带班教师
|
<el-button
|
||||||
</el-button>
|
icon="User"
|
||||||
<right-toolbar
|
type="success"
|
||||||
v-model:showSearch="showSearch"
|
class="ml10"
|
||||||
class="ml10"
|
@click="teacherDialogRef.openDialog()">
|
||||||
style="float: right;"
|
指定带班教师
|
||||||
@queryTable="getDataList">
|
</el-button>
|
||||||
<TableColumnControl
|
<right-toolbar
|
||||||
ref="columnControlRef"
|
v-model:showSearch="showSearch"
|
||||||
:columns="tableColumns"
|
class="ml10"
|
||||||
v-model="visibleColumns"
|
@queryTable="getDataList">
|
||||||
trigger-type="default"
|
<TableColumnControl
|
||||||
trigger-circle
|
ref="columnControlRef"
|
||||||
@change="handleColumnChange"
|
:columns="tableColumns"
|
||||||
@order-change="handleColumnOrderChange"
|
v-model="visibleColumns"
|
||||||
>
|
trigger-type="default"
|
||||||
<template #trigger>
|
trigger-circle
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
@change="handleColumnChange"
|
||||||
<el-button circle style="margin-left: 0;">
|
@order-change="handleColumnOrderChange"
|
||||||
<el-icon><Menu /></el-icon>
|
>
|
||||||
</el-button>
|
<template #trigger>
|
||||||
</el-tooltip>
|
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
||||||
</template>
|
<el-button circle style="margin-left: 0;">
|
||||||
</TableColumnControl>
|
<el-icon><Menu /></el-icon>
|
||||||
</right-toolbar>
|
</el-button>
|
||||||
</div>
|
</el-tooltip>
|
||||||
</el-row>
|
</template>
|
||||||
|
</TableColumnControl>
|
||||||
|
</right-toolbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table
|
<el-table
|
||||||
:data="state.dataList"
|
:data="state.dataList"
|
||||||
v-loading="state.loading"
|
v-loading="state.loading"
|
||||||
border
|
stripe
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle">
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
class="modern-table">
|
||||||
<template #header>
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<template v-for="col in sortedTableColumns" :key="col.prop || col.label">
|
|
||||||
<el-table-column
|
|
||||||
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
|
||||||
:prop="col.prop"
|
|
||||||
:label="col.label"
|
|
||||||
show-overflow-tooltip
|
|
||||||
align="center"
|
|
||||||
:min-width="col.minWidth"
|
|
||||||
:width="col.width">
|
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
<el-icon><List /></el-icon>
|
||||||
<span style="margin-left: 4px">{{ col.label }}</span>
|
|
||||||
</template>
|
</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 !== '操作'"
|
||||||
|
:prop="col.prop"
|
||||||
|
:label="col.label"
|
||||||
|
show-overflow-tooltip
|
||||||
|
align="center"
|
||||||
|
:min-width="col.minWidth"
|
||||||
|
:width="col.width">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
||||||
|
<span style="margin-left: 4px">{{ col.label }}</span>
|
||||||
|
</template>
|
||||||
<!-- 学期列特殊模板 -->
|
<!-- 学期列特殊模板 -->
|
||||||
<template v-if="col.prop === 'schoolTerm'" #default="scope">
|
<template v-if="col.prop === 'schoolTerm'" #default="scope">
|
||||||
<el-tag size="small" type="primary" effect="plain">
|
<el-tag size="small" type="primary" effect="plain">
|
||||||
@@ -174,30 +191,33 @@
|
|||||||
<template v-else-if="col.prop === 'attendanceTeacherName'" #default="scope">
|
<template v-else-if="col.prop === 'attendanceTeacherName'" #default="scope">
|
||||||
<span>{{ scope.row.attendanceTeacherName || '-' }}</span>
|
<span>{{ scope.row.attendanceTeacherName || '-' }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column label="操作" width="100" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
icon="Delete"
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
@click="handleDelete(scope.row)">
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</template>
|
</el-table>
|
||||||
<el-table-column label="操作" width="100" align="center" fixed="right">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><Setting /></el-icon>
|
|
||||||
<span style="margin-left: 4px">操作</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
icon="Delete"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row)">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<pagination
|
<div class="pagination-wrapper">
|
||||||
@size-change="sizeChangeHandle"
|
<pagination
|
||||||
@current-change="currentChangeHandle"
|
@size-change="sizeChangeHandle"
|
||||||
v-bind="state.pagination" />
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 新增表单弹窗 -->
|
<!-- 新增表单弹窗 -->
|
||||||
@@ -220,8 +240,9 @@ import { useMessage, useMessageBox } from "/@/hooks/message";
|
|||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import FormDialog from './form.vue'
|
import FormDialog from './form.vue'
|
||||||
import TeacherDialog from './teacher.vue'
|
import TeacherDialog from './teacher.vue'
|
||||||
import { List, Calendar, Clock, OfficeBuilding, CreditCard, Avatar, Grid, UserFilled, Setting, Menu } from '@element-plus/icons-vue'
|
import { List, Calendar, Clock, OfficeBuilding, CreditCard, Avatar, Grid, UserFilled, Setting, Menu, Search, Document } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumnControl'
|
||||||
|
import '/@/styles/modern-page.scss'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -262,104 +283,14 @@ const columnConfigMap: Record<string, { icon: any }> = {
|
|||||||
attendanceTeacherName: { icon: UserFilled }
|
attendanceTeacherName: { icon: UserFilled }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns, route.path)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadSavedConfig()
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const sortedTableColumns = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: typeof tableColumns = []
|
|
||||||
const unorderedColumns: typeof tableColumns = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 列显示控制函数
|
|
||||||
const checkColumnVisible = (prop: string): boolean => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return visibleColumns.value.includes(prop)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列变化
|
|
||||||
const handleColumnChange = (columns: string[]) => {
|
|
||||||
visibleColumns.value = columns
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = columns.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列排序变化
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 搜索表单
|
// 搜索表单
|
||||||
const searchForm = reactive({
|
const searchForm = reactive({
|
||||||
@@ -480,7 +411,6 @@ onMounted(() => {
|
|||||||
getSchoolTermList()
|
getSchoolTermList()
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (visibleColumns.value.length === 0) {
|
if (visibleColumns.value.length === 0) {
|
||||||
loadSavedConfig()
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,97 +1,109 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="Plus"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
class="ml10"
|
教学楼管理列表
|
||||||
@click="formDialogRef.openDialog()">
|
</span>
|
||||||
新增
|
<div class="header-actions">
|
||||||
</el-button>
|
<el-button
|
||||||
<right-toolbar
|
icon="Plus"
|
||||||
class="ml10"
|
type="primary"
|
||||||
style="float: right;"
|
@click="formDialogRef.openDialog()">
|
||||||
@queryTable="getDataList">
|
新增
|
||||||
<TableColumnControl
|
</el-button>
|
||||||
ref="columnControlRef"
|
<right-toolbar
|
||||||
:columns="tableColumns"
|
class="ml10"
|
||||||
v-model="visibleColumns"
|
@queryTable="getDataList">
|
||||||
trigger-type="default"
|
<TableColumnControl
|
||||||
trigger-circle
|
ref="columnControlRef"
|
||||||
@change="handleColumnChange"
|
:columns="tableColumns"
|
||||||
@order-change="handleColumnOrderChange"
|
v-model="visibleColumns"
|
||||||
>
|
trigger-type="default"
|
||||||
<template #trigger>
|
trigger-circle
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
@change="handleColumnChange"
|
||||||
<el-button circle style="margin-left: 0;">
|
@order-change="handleColumnOrderChange"
|
||||||
<el-icon><Menu /></el-icon>
|
>
|
||||||
</el-button>
|
<template #trigger>
|
||||||
</el-tooltip>
|
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
||||||
</template>
|
<el-button circle style="margin-left: 0;">
|
||||||
</TableColumnControl>
|
<el-icon><Menu /></el-icon>
|
||||||
</right-toolbar>
|
</el-button>
|
||||||
</div>
|
</el-tooltip>
|
||||||
</el-row>
|
</template>
|
||||||
|
</TableColumnControl>
|
||||||
|
</right-toolbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table
|
<el-table
|
||||||
:data="state.dataList"
|
:data="state.dataList"
|
||||||
v-loading="state.loading"
|
v-loading="state.loading"
|
||||||
border
|
stripe
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle">
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
class="modern-table">
|
||||||
<template #header>
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<template v-for="col in sortedTableColumns" :key="col.prop || col.label">
|
|
||||||
<el-table-column
|
|
||||||
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
|
||||||
:prop="col.prop"
|
|
||||||
:label="col.label"
|
|
||||||
show-overflow-tooltip
|
|
||||||
align="center"
|
|
||||||
:min-width="col.minWidth"
|
|
||||||
:width="col.width">
|
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
<el-icon><List /></el-icon>
|
||||||
<span style="margin-left: 4px">{{ col.label }}</span>
|
</template>
|
||||||
|
<template #default="{ $index }">
|
||||||
|
{{ $index + 1 + ((state.pagination?.current || 1) - 1) * (state.pagination?.size || 10) }}
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</template>
|
<template v-for="col in visibleColumnsSorted" :key="col.prop || col.label">
|
||||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
<el-table-column
|
||||||
<template #header>
|
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
||||||
<el-icon><Setting /></el-icon>
|
:prop="col.prop"
|
||||||
<span style="margin-left: 4px">操作</span>
|
:label="col.label"
|
||||||
|
show-overflow-tooltip
|
||||||
|
align="center"
|
||||||
|
:min-width="col.minWidth"
|
||||||
|
:width="col.width">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
||||||
|
<span style="margin-left: 4px">{{ col.label }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
</template>
|
</template>
|
||||||
<template #default="scope">
|
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||||
<el-button
|
<template #header>
|
||||||
icon="Edit"
|
<el-icon><Setting /></el-icon>
|
||||||
text
|
<span style="margin-left: 4px">操作</span>
|
||||||
type="primary"
|
</template>
|
||||||
@click="handleEdit(scope.row)">
|
<template #default="scope">
|
||||||
编辑
|
<el-button
|
||||||
</el-button>
|
icon="Edit"
|
||||||
<el-button
|
link
|
||||||
icon="Delete"
|
type="primary"
|
||||||
text
|
@click="handleEdit(scope.row)">
|
||||||
type="danger"
|
编辑
|
||||||
@click="handleDelete(scope.row)">
|
</el-button>
|
||||||
删除
|
<el-button
|
||||||
</el-button>
|
icon="Delete"
|
||||||
</template>
|
link
|
||||||
</el-table-column>
|
type="danger"
|
||||||
</el-table>
|
@click="handleDelete(scope.row)">
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<pagination
|
<div class="pagination-wrapper">
|
||||||
@size-change="sizeChangeHandle"
|
<pagination
|
||||||
@current-change="currentChangeHandle"
|
@size-change="sizeChangeHandle"
|
||||||
v-bind="state.pagination" />
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 新增/编辑表单弹窗 -->
|
<!-- 新增/编辑表单弹窗 -->
|
||||||
@@ -107,8 +119,9 @@ import { fetchList, delObj } from "/@/api/stuwork/teachbuilding";
|
|||||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import FormDialog from './form.vue'
|
import FormDialog from './form.vue'
|
||||||
import { List, OfficeBuilding, EditPen, Setting, Menu, Calendar } from '@element-plus/icons-vue'
|
import { List, OfficeBuilding, EditPen, Setting, Menu, Calendar, Document } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumnControl'
|
||||||
|
import '/@/styles/modern-page.scss'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -127,104 +140,14 @@ const columnConfigMap: Record<string, { icon: any }> = {
|
|||||||
remarks: { icon: EditPen }
|
remarks: { icon: EditPen }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns, route.path)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadSavedConfig()
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const sortedTableColumns = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: typeof tableColumns = []
|
|
||||||
const unorderedColumns: typeof tableColumns = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 列显示控制函数
|
|
||||||
const checkColumnVisible = (prop: string): boolean => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return visibleColumns.value.includes(prop)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列变化
|
|
||||||
const handleColumnChange = (columns: string[]) => {
|
|
||||||
visibleColumns.value = columns
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = columns.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列排序变化
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 配置 useTable
|
// 配置 useTable
|
||||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||||
@@ -269,7 +192,6 @@ const handleDelete = async (row: any) => {
|
|||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (visibleColumns.value.length === 0) {
|
if (visibleColumns.value.length === 0) {
|
||||||
loadSavedConfig()
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch" class="search-form">
|
||||||
<el-form-item label="学院" prop="deptCode">
|
<el-form-item label="学院" prop="deptCode">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="searchForm.deptCode"
|
v-model="searchForm.deptCode"
|
||||||
@@ -39,96 +47,123 @@
|
|||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="Plus"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
class="ml10"
|
教室管理列表
|
||||||
@click="formDialogRef.openDialog()">
|
</span>
|
||||||
新增
|
<div class="header-actions">
|
||||||
</el-button>
|
<el-button
|
||||||
<el-button
|
icon="Plus"
|
||||||
icon="Setting"
|
type="primary"
|
||||||
type="success"
|
@click="formDialogRef.openDialog()">
|
||||||
class="ml10"
|
新增
|
||||||
:disabled="selectedRows.length === 0"
|
</el-button>
|
||||||
@click="handleBatchSet">
|
<el-button
|
||||||
批量设置
|
icon="Setting"
|
||||||
</el-button>
|
type="success"
|
||||||
<right-toolbar
|
class="ml10"
|
||||||
v-model:showSearch="showSearch"
|
:disabled="selectedRows.length === 0"
|
||||||
class="ml10"
|
@click="handleBatchSet">
|
||||||
style="float: right;"
|
批量设置
|
||||||
@queryTable="getDataList">
|
</el-button>
|
||||||
<TableColumnControl
|
<right-toolbar
|
||||||
ref="columnControlRef"
|
v-model:showSearch="showSearch"
|
||||||
:columns="tableColumns"
|
class="ml10"
|
||||||
v-model="visibleColumns"
|
@queryTable="getDataList">
|
||||||
trigger-type="default"
|
<TableColumnControl
|
||||||
trigger-circle
|
ref="columnControlRef"
|
||||||
@change="handleColumnChange"
|
:columns="tableColumns"
|
||||||
@order-change="handleColumnOrderChange"
|
v-model="visibleColumns"
|
||||||
>
|
trigger-type="default"
|
||||||
<template #trigger>
|
trigger-circle
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
@change="handleColumnChange"
|
||||||
<el-button circle style="margin-left: 0;">
|
@order-change="handleColumnOrderChange"
|
||||||
<el-icon><Menu /></el-icon>
|
>
|
||||||
</el-button>
|
<template #trigger>
|
||||||
</el-tooltip>
|
<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
|
||||||
|
row-key="id"
|
||||||
|
:cell-style="tableStyle.cellStyle"
|
||||||
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
|
class="modern-table"
|
||||||
|
@selection-change="handleSelectionChange">
|
||||||
|
<el-table-column type="selection" width="55" align="center" />
|
||||||
|
<el-table-column type="index" label="序号" 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 !== '操作'"
|
||||||
|
:prop="col.prop"
|
||||||
|
:label="col.label"
|
||||||
|
show-overflow-tooltip
|
||||||
|
align="center"
|
||||||
|
:min-width="col.minWidth"
|
||||||
|
:width="col.width">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
||||||
|
<span style="margin-left: 4px">{{ col.label }}</span>
|
||||||
</template>
|
</template>
|
||||||
</TableColumnControl>
|
</el-table-column>
|
||||||
</right-toolbar>
|
</template>
|
||||||
|
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</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>
|
</div>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 表格 -->
|
|
||||||
<el-table
|
|
||||||
:data="state.dataList"
|
|
||||||
v-loading="state.loading"
|
|
||||||
border
|
|
||||||
row-key="id"
|
|
||||||
:cell-style="tableStyle.cellStyle"
|
|
||||||
:header-cell-style="tableStyle.headerCellStyle"
|
|
||||||
@selection-change="handleSelectionChange">
|
|
||||||
<el-table-column type="selection" width="55" align="center" />
|
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><Setting /></el-icon>
|
|
||||||
<span style="margin-left: 4px">操作</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
icon="Edit"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleEdit(scope.row)">
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Delete"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row)">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
|
||||||
<pagination
|
|
||||||
@size-change="sizeChangeHandle"
|
|
||||||
@current-change="currentChangeHandle"
|
|
||||||
v-bind="state.pagination" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 新增/编辑表单弹窗 -->
|
<!-- 新增/编辑表单弹窗 -->
|
||||||
@@ -150,8 +185,9 @@ import { useMessage, useMessageBox } from "/@/hooks/message";
|
|||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import FormDialog from './form.vue'
|
import FormDialog from './form.vue'
|
||||||
import BatchDialog from './batch.vue'
|
import BatchDialog from './batch.vue'
|
||||||
import { List, OfficeBuilding, Location, Setting, Menu, Calendar } from '@element-plus/icons-vue'
|
import { List, OfficeBuilding, Location, Setting, Menu, Calendar, Search, Document } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumnControl'
|
||||||
|
import '/@/styles/modern-page.scss'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -178,104 +214,14 @@ const columnConfigMap: Record<string, { icon: any }> = {
|
|||||||
position: { icon: Location }
|
position: { icon: Location }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns, route.path)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadSavedConfig()
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const sortedTableColumns = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: typeof tableColumns = []
|
|
||||||
const unorderedColumns: typeof tableColumns = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 列显示控制函数
|
|
||||||
const checkColumnVisible = (prop: string): boolean => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return visibleColumns.value.includes(prop)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列变化
|
|
||||||
const handleColumnChange = (columns: string[]) => {
|
|
||||||
visibleColumns.value = columns
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = columns.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列排序变化
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 搜索表单
|
// 搜索表单
|
||||||
const searchForm = reactive({
|
const searchForm = reactive({
|
||||||
@@ -377,7 +323,6 @@ onMounted(() => {
|
|||||||
getBuildingListData()
|
getBuildingListData()
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (visibleColumns.value.length === 0) {
|
if (visibleColumns.value.length === 0) {
|
||||||
loadSavedConfig()
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="state.queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form :model="state.queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList" class="search-form">
|
||||||
<el-form-item label="批次" prop="termId">
|
<el-form-item label="批次" prop="termId">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="state.queryForm.termId"
|
v-model="state.queryForm.termId"
|
||||||
@@ -92,77 +100,86 @@
|
|||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="Plus"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
@click="handleAdd">
|
免学费学生列表
|
||||||
新增
|
</span>
|
||||||
</el-button>
|
<div class="header-actions">
|
||||||
<el-button
|
<el-button
|
||||||
icon="Download"
|
icon="Plus"
|
||||||
type="primary"
|
type="primary"
|
||||||
plain
|
@click="handleAdd">
|
||||||
class="ml10"
|
新增
|
||||||
:loading="exportLoading"
|
</el-button>
|
||||||
@click="handleExport">
|
<el-button
|
||||||
导出
|
icon="Download"
|
||||||
</el-button>
|
type="success"
|
||||||
<right-toolbar
|
class="ml10"
|
||||||
v-model:showSearch="showSearch"
|
:loading="exportLoading"
|
||||||
class="ml10"
|
@click="handleExport">
|
||||||
style="float: right;"
|
导出
|
||||||
@queryTable="getDataList">
|
</el-button>
|
||||||
<TableColumnControl
|
<right-toolbar
|
||||||
ref="columnControlRef"
|
v-model:showSearch="showSearch"
|
||||||
:columns="tableColumns"
|
class="ml10"
|
||||||
v-model="visibleColumns"
|
@queryTable="getDataList">
|
||||||
trigger-type="default"
|
<TableColumnControl
|
||||||
trigger-circle
|
ref="columnControlRef"
|
||||||
@change="handleColumnChange"
|
:columns="tableColumns"
|
||||||
@order-change="handleColumnOrderChange"
|
v-model="visibleColumns"
|
||||||
>
|
trigger-type="default"
|
||||||
<template #trigger>
|
trigger-circle
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
@change="handleColumnChange"
|
||||||
<el-button circle style="margin-left: 0;">
|
@order-change="handleColumnOrderChange"
|
||||||
<el-icon><Menu /></el-icon>
|
>
|
||||||
</el-button>
|
<template #trigger>
|
||||||
</el-tooltip>
|
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
||||||
</template>
|
<el-button circle style="margin-left: 0;">
|
||||||
</TableColumnControl>
|
<el-icon><Menu /></el-icon>
|
||||||
</right-toolbar>
|
</el-button>
|
||||||
</div>
|
</el-tooltip>
|
||||||
</el-row>
|
</template>
|
||||||
|
</TableColumnControl>
|
||||||
|
</right-toolbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table
|
<el-table
|
||||||
:data="state.dataList"
|
:data="state.dataList"
|
||||||
v-loading="state.loading"
|
v-loading="state.loading"
|
||||||
border
|
stripe
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle">
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
class="modern-table">
|
||||||
<template #header>
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<template v-for="col in sortedTableColumns" :key="col.prop || col.label">
|
|
||||||
<el-table-column
|
|
||||||
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
|
||||||
:prop="col.prop"
|
|
||||||
:label="col.label"
|
|
||||||
show-overflow-tooltip
|
|
||||||
align="center"
|
|
||||||
:min-width="col.minWidth"
|
|
||||||
:width="col.width">
|
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
<el-icon><List /></el-icon>
|
||||||
<span style="margin-left: 4px">{{ col.label }}</span>
|
|
||||||
</template>
|
</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 !== '操作'"
|
||||||
|
:prop="col.prop"
|
||||||
|
:label="col.label"
|
||||||
|
show-overflow-tooltip
|
||||||
|
align="center"
|
||||||
|
:min-width="col.minWidth"
|
||||||
|
:width="col.width">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
||||||
|
<span style="margin-left: 4px">{{ col.label }}</span>
|
||||||
|
</template>
|
||||||
<!-- 年级列特殊模板 -->
|
<!-- 年级列特殊模板 -->
|
||||||
<template v-if="col.prop === 'gradeCurr'" #default="scope">
|
<template v-if="col.prop === 'gradeCurr'" #default="scope">
|
||||||
<el-tag v-if="scope.row.gradeCurr !== undefined && scope.row.gradeCurr !== null" size="small" type="primary" effect="plain">
|
<el-tag v-if="scope.row.gradeCurr !== undefined && scope.row.gradeCurr !== null" size="small" type="primary" effect="plain">
|
||||||
@@ -203,37 +220,40 @@
|
|||||||
:type-map="{ '0': { type: 'warning', effect: 'light' }, '1': { type: 'success', effect: 'light' } }"
|
:type-map="{ '0': { type: 'warning', effect: 'light' }, '1': { type: 'success', effect: 'light' } }"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</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-column>
|
||||||
</template>
|
</el-table>
|
||||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><Setting /></el-icon>
|
|
||||||
<span style="margin-left: 4px">操作</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
icon="Edit"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleEdit(scope.row)">
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Delete"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row)">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<pagination
|
<div class="pagination-wrapper">
|
||||||
@size-change="sizeChangeHandle"
|
<pagination
|
||||||
@current-change="currentChangeHandle"
|
@size-change="sizeChangeHandle"
|
||||||
v-bind="state.pagination" />
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 新增/编辑表单弹窗 -->
|
<!-- 新增/编辑表单弹窗 -->
|
||||||
@@ -257,9 +277,10 @@ import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
|||||||
import {
|
import {
|
||||||
User, School, Trophy, Phone, Money, CircleCheck, List,
|
User, School, Trophy, Phone, Money, CircleCheck, List,
|
||||||
OfficeBuilding, Reading, UserFilled, Calendar, Sort, Grid,
|
OfficeBuilding, Reading, UserFilled, Calendar, Sort, Grid,
|
||||||
CreditCard, Avatar, Setting, Menu
|
CreditCard, Avatar, Setting, Menu, Search, Document
|
||||||
} from '@element-plus/icons-vue'
|
} from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumnControl'
|
||||||
|
import '/@/styles/modern-page.scss'
|
||||||
|
|
||||||
// 引入组件
|
// 引入组件
|
||||||
const GenderTag = defineAsyncComponent(() => import('/@/components/GenderTag/index.vue'))
|
const GenderTag = defineAsyncComponent(() => import('/@/components/GenderTag/index.vue'))
|
||||||
@@ -320,104 +341,14 @@ const columnConfigMap: Record<string, { icon: any }> = {
|
|||||||
checkStatus: { icon: CircleCheck }
|
checkStatus: { icon: CircleCheck }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns, route.path)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadSavedConfig()
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const sortedTableColumns = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: typeof tableColumns = []
|
|
||||||
const unorderedColumns: typeof tableColumns = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 列显示控制函数
|
|
||||||
const checkColumnVisible = (prop: string): boolean => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return visibleColumns.value.includes(prop)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列变化
|
|
||||||
const handleColumnChange = (columns: string[]) => {
|
|
||||||
visibleColumns.value = columns
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = columns.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列排序变化
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 表格样式
|
// 表格样式
|
||||||
const tableStyle = {
|
const tableStyle = {
|
||||||
@@ -670,7 +601,6 @@ onMounted(() => {
|
|||||||
getMajorLevelDict()
|
getMajorLevelDict()
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (visibleColumns.value.length === 0) {
|
if (visibleColumns.value.length === 0) {
|
||||||
loadSavedConfig()
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="state.queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</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-form-item label="学年" prop="schoolYear">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="state.queryForm.schoolYear"
|
v-model="state.queryForm.schoolYear"
|
||||||
@@ -38,68 +46,78 @@
|
|||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="Plus"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
@click="formDialogRef.openDialog()">
|
免学费批次列表
|
||||||
新增
|
</span>
|
||||||
</el-button>
|
<div class="header-actions">
|
||||||
<right-toolbar
|
<el-button
|
||||||
v-model:showSearch="showSearch"
|
icon="Plus"
|
||||||
class="ml10"
|
type="primary"
|
||||||
style="float: right;"
|
@click="formDialogRef.openDialog()">
|
||||||
@queryTable="getDataList">
|
新增
|
||||||
<TableColumnControl
|
</el-button>
|
||||||
ref="columnControlRef"
|
<right-toolbar
|
||||||
:columns="tableColumns"
|
v-model:showSearch="showSearch"
|
||||||
v-model="visibleColumns"
|
class="ml10"
|
||||||
trigger-type="default"
|
@queryTable="getDataList">
|
||||||
trigger-circle
|
<TableColumnControl
|
||||||
@change="handleColumnChange"
|
ref="columnControlRef"
|
||||||
@order-change="handleColumnOrderChange"
|
:columns="tableColumns"
|
||||||
>
|
v-model="visibleColumns"
|
||||||
<template #trigger>
|
trigger-type="default"
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
trigger-circle
|
||||||
<el-button circle style="margin-left: 0;">
|
@change="handleColumnChange"
|
||||||
<el-icon><Menu /></el-icon>
|
@order-change="handleColumnOrderChange"
|
||||||
</el-button>
|
>
|
||||||
</el-tooltip>
|
<template #trigger>
|
||||||
</template>
|
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
||||||
</TableColumnControl>
|
<el-button circle style="margin-left: 0;">
|
||||||
</right-toolbar>
|
<el-icon><Menu /></el-icon>
|
||||||
</div>
|
</el-button>
|
||||||
</el-row>
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
</TableColumnControl>
|
||||||
|
</right-toolbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table
|
<el-table
|
||||||
:data="state.dataList"
|
:data="state.dataList"
|
||||||
v-loading="state.loading"
|
v-loading="state.loading"
|
||||||
border
|
stripe
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle">
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
class="modern-table">
|
||||||
<template #header>
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<template v-for="col in sortedTableColumns" :key="col.prop || col.label">
|
|
||||||
<el-table-column
|
|
||||||
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
|
||||||
:prop="col.prop"
|
|
||||||
:label="col.label"
|
|
||||||
show-overflow-tooltip
|
|
||||||
align="center"
|
|
||||||
:min-width="col.minWidth"
|
|
||||||
:width="col.width">
|
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
<el-icon><List /></el-icon>
|
||||||
<span style="margin-left: 4px">{{ col.label }}</span>
|
|
||||||
</template>
|
</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 !== '操作'"
|
||||||
|
:prop="col.prop"
|
||||||
|
:label="col.label"
|
||||||
|
show-overflow-tooltip
|
||||||
|
align="center"
|
||||||
|
:min-width="col.minWidth"
|
||||||
|
:width="col.width">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
||||||
|
<span style="margin-left: 4px">{{ col.label }}</span>
|
||||||
|
</template>
|
||||||
<!-- 学期列特殊模板 -->
|
<!-- 学期列特殊模板 -->
|
||||||
<template v-if="col.prop === 'schoolTerm'" #default="scope">
|
<template v-if="col.prop === 'schoolTerm'" #default="scope">
|
||||||
<el-tag size="small" type="primary" effect="plain">
|
<el-tag size="small" type="primary" effect="plain">
|
||||||
@@ -120,37 +138,40 @@
|
|||||||
{{ formatType(scope.row.type) }}
|
{{ formatType(scope.row.type) }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</template>
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</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-column>
|
||||||
</template>
|
</el-table>
|
||||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><Setting /></el-icon>
|
|
||||||
<span style="margin-left: 4px">操作</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
icon="Edit"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleEdit(scope.row)">
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Delete"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row)">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<pagination
|
<div class="pagination-wrapper">
|
||||||
@size-change="sizeChangeHandle"
|
<pagination
|
||||||
@current-change="currentChangeHandle"
|
@size-change="sizeChangeHandle"
|
||||||
v-bind="state.pagination" />
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 新增/编辑表单弹窗 -->
|
<!-- 新增/编辑表单弹窗 -->
|
||||||
@@ -169,8 +190,9 @@ import { useMessage, useMessageBox } from "/@/hooks/message";
|
|||||||
import { parseTime } from "/@/utils/formatTime";
|
import { parseTime } from "/@/utils/formatTime";
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import FormDialog from './form.vue'
|
import FormDialog from './form.vue'
|
||||||
import { List, Document, Calendar, Clock, Collection, Setting, Menu } from '@element-plus/icons-vue'
|
import { List, Document, Calendar, Clock, Collection, Setting, Menu, Search } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumnControl'
|
||||||
|
import '/@/styles/modern-page.scss'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -202,104 +224,14 @@ const columnConfigMap: Record<string, { icon: any }> = {
|
|||||||
type: { icon: Collection }
|
type: { icon: Collection }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns, route.path)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadSavedConfig()
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const sortedTableColumns = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: typeof tableColumns = []
|
|
||||||
const unorderedColumns: typeof tableColumns = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 列显示控制函数
|
|
||||||
const checkColumnVisible = (prop: string): boolean => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return visibleColumns.value.includes(prop)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列变化
|
|
||||||
const handleColumnChange = (columns: string[]) => {
|
|
||||||
visibleColumns.value = columns
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = columns.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列排序变化
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 表格样式
|
// 表格样式
|
||||||
const tableStyle = {
|
const tableStyle = {
|
||||||
@@ -430,7 +362,6 @@ onMounted(() => {
|
|||||||
getTypeDict()
|
getTypeDict()
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (visibleColumns.value.length === 0) {
|
if (visibleColumns.value.length === 0) {
|
||||||
loadSavedConfig()
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch" class="search-form">
|
||||||
<el-form-item label="可用余额" prop="effectiveMoney">
|
<el-form-item label="可用余额" prop="effectiveMoney">
|
||||||
<el-input-number
|
<el-input-number
|
||||||
v-model="searchForm.effectiveMoney"
|
v-model="searchForm.effectiveMoney"
|
||||||
@@ -50,75 +58,84 @@
|
|||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="FolderAdd"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
class="ml10"
|
水电明细列表
|
||||||
@click="formDialogRef.openDialog()">
|
</span>
|
||||||
新 增
|
<div class="header-actions">
|
||||||
</el-button>
|
<el-button
|
||||||
<el-button
|
icon="FolderAdd"
|
||||||
icon="Download"
|
type="primary"
|
||||||
type="success"
|
@click="formDialogRef.openDialog()">
|
||||||
class="ml10"
|
新 增
|
||||||
@click="handleExport">
|
</el-button>
|
||||||
导 出
|
<el-button
|
||||||
</el-button>
|
icon="Download"
|
||||||
<el-button
|
type="success"
|
||||||
icon="Setting"
|
class="ml10"
|
||||||
type="warning"
|
@click="handleExport">
|
||||||
class="ml10"
|
导 出
|
||||||
@click="handleInitWaterOrder">
|
</el-button>
|
||||||
初始化本期水电补贴
|
<el-button
|
||||||
</el-button>
|
icon="Setting"
|
||||||
<right-toolbar
|
type="warning"
|
||||||
v-model:showSearch="showSearch"
|
class="ml10"
|
||||||
class="ml10"
|
@click="handleInitWaterOrder">
|
||||||
style="float: right;"
|
初始化本期水电补贴
|
||||||
@queryTable="getDataList">
|
</el-button>
|
||||||
<TableColumnControl
|
<right-toolbar
|
||||||
ref="columnControlRef"
|
v-model:showSearch="showSearch"
|
||||||
:columns="tableColumns"
|
class="ml10"
|
||||||
v-model="visibleColumns"
|
@queryTable="getDataList">
|
||||||
trigger-type="default"
|
<TableColumnControl
|
||||||
trigger-circle
|
ref="columnControlRef"
|
||||||
@change="handleColumnChange"
|
:columns="tableColumns"
|
||||||
@order-change="handleColumnOrderChange"
|
v-model="visibleColumns"
|
||||||
>
|
trigger-type="default"
|
||||||
<template #trigger>
|
trigger-circle
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
@change="handleColumnChange"
|
||||||
<el-button circle style="margin-left: 0;">
|
@order-change="handleColumnOrderChange"
|
||||||
<el-icon><Menu /></el-icon>
|
>
|
||||||
</el-button>
|
<template #trigger>
|
||||||
</el-tooltip>
|
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
||||||
</template>
|
<el-button circle style="margin-left: 0;">
|
||||||
</TableColumnControl>
|
<el-icon><Menu /></el-icon>
|
||||||
</right-toolbar>
|
</el-button>
|
||||||
</div>
|
</el-tooltip>
|
||||||
</el-row>
|
</template>
|
||||||
|
</TableColumnControl>
|
||||||
|
</right-toolbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table
|
<el-table
|
||||||
:data="state.dataList"
|
:data="state.dataList"
|
||||||
v-loading="state.loading"
|
v-loading="state.loading"
|
||||||
border
|
stripe
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle"
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
@sort-change="sortChangeHandle">
|
class="modern-table"
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
@sort-change="sortChangeHandle">
|
||||||
<template #header>
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
<el-icon><List /></el-icon>
|
<template #header>
|
||||||
</template>
|
<el-icon><List /></el-icon>
|
||||||
</el-table-column>
|
</template>
|
||||||
<template v-for="col in sortedTableColumns" :key="col.prop || col.label">
|
<template #default="{ $index }">
|
||||||
<el-table-column
|
{{ $index + 1 + ((state.pagination?.current || 1) - 1) * (state.pagination?.size || 10) }}
|
||||||
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
</template>
|
||||||
:prop="col.prop"
|
</el-table-column>
|
||||||
|
<template v-for="col in visibleColumnsSorted" :key="col.prop || col.label">
|
||||||
|
<el-table-column
|
||||||
|
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
||||||
|
:prop="col.prop"
|
||||||
:label="col.label"
|
:label="col.label"
|
||||||
show-overflow-tooltip
|
show-overflow-tooltip
|
||||||
align="center"
|
align="center"
|
||||||
@@ -169,44 +186,47 @@
|
|||||||
¥{{ scope.row.effectiveMoney ? Number(scope.row.effectiveMoney).toFixed(2) : '0.00' }}
|
¥{{ scope.row.effectiveMoney ? Number(scope.row.effectiveMoney).toFixed(2) : '0.00' }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</template>
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column label="操作" width="200" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
icon="Edit"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="handleEdit(scope.row)">
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="View"
|
||||||
|
link
|
||||||
|
type="info"
|
||||||
|
@click="handleViewDetail(scope.row)">
|
||||||
|
明细
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="Delete"
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
@click="handleDelete(scope.row)">
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</template>
|
</el-table>
|
||||||
<el-table-column label="操作" width="200" align="center" fixed="right">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><Setting /></el-icon>
|
|
||||||
<span style="margin-left: 4px">操作</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
icon="Edit"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleEdit(scope.row)">
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="View"
|
|
||||||
text
|
|
||||||
type="info"
|
|
||||||
@click="handleViewDetail(scope.row)">
|
|
||||||
明细
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Delete"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row)">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<pagination
|
<div class="pagination-wrapper">
|
||||||
@size-change="sizeChangeHandle"
|
<pagination
|
||||||
@current-change="currentChangeHandle"
|
@size-change="sizeChangeHandle"
|
||||||
v-bind="state.pagination" />
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 表单弹窗 -->
|
<!-- 表单弹窗 -->
|
||||||
@@ -247,8 +267,9 @@ import { useMessage, useMessageBox } from "/@/hooks/message";
|
|||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import FormDialog from './form.vue';
|
import FormDialog from './form.vue';
|
||||||
import DetailDialog from './detail.vue';
|
import DetailDialog from './detail.vue';
|
||||||
import { List, OfficeBuilding, House, UserFilled, Money, Setting, Menu } from '@element-plus/icons-vue'
|
import { List, OfficeBuilding, House, UserFilled, Money, Setting, Menu, Search, Document } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumnControl'
|
||||||
|
import '/@/styles/modern-page.scss'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -286,104 +307,14 @@ const columnConfigMap: Record<string, { icon: any }> = {
|
|||||||
effectiveMoney: { icon: Money }
|
effectiveMoney: { icon: Money }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns, route.path)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadSavedConfig()
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const sortedTableColumns = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: typeof tableColumns = []
|
|
||||||
const unorderedColumns: typeof tableColumns = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 列显示控制函数
|
|
||||||
const checkColumnVisible = (prop: string): boolean => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return visibleColumns.value.includes(prop)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列变化
|
|
||||||
const handleColumnChange = (columns: string[]) => {
|
|
||||||
visibleColumns.value = columns
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = columns.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列排序变化
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 搜索表单
|
// 搜索表单
|
||||||
const searchForm = reactive({
|
const searchForm = reactive({
|
||||||
@@ -515,7 +446,6 @@ onMounted(() => {
|
|||||||
getBuildingListData()
|
getBuildingListData()
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (visibleColumns.value.length === 0) {
|
if (visibleColumns.value.length === 0) {
|
||||||
loadSavedConfig()
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch" class="search-form">
|
||||||
<el-form-item label="宿舍号" prop="roomNo">
|
<el-form-item label="宿舍号" prop="roomNo">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="searchForm.roomNo"
|
v-model="searchForm.roomNo"
|
||||||
@@ -45,68 +53,77 @@
|
|||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="FolderAdd"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
class="ml10"
|
水电订单列表
|
||||||
@click="formDialogRef.openDialog()">
|
</span>
|
||||||
新 增
|
<div class="header-actions">
|
||||||
</el-button>
|
<el-button
|
||||||
<right-toolbar
|
icon="FolderAdd"
|
||||||
v-model:showSearch="showSearch"
|
type="primary"
|
||||||
class="ml10"
|
@click="formDialogRef.openDialog()">
|
||||||
style="float: right;"
|
新 增
|
||||||
@queryTable="getDataList">
|
</el-button>
|
||||||
<TableColumnControl
|
<right-toolbar
|
||||||
ref="columnControlRef"
|
v-model:showSearch="showSearch"
|
||||||
:columns="tableColumns"
|
class="ml10"
|
||||||
v-model="visibleColumns"
|
@queryTable="getDataList">
|
||||||
trigger-type="default"
|
<TableColumnControl
|
||||||
trigger-circle
|
ref="columnControlRef"
|
||||||
@change="handleColumnChange"
|
:columns="tableColumns"
|
||||||
@order-change="handleColumnOrderChange"
|
v-model="visibleColumns"
|
||||||
>
|
trigger-type="default"
|
||||||
<template #trigger>
|
trigger-circle
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
@change="handleColumnChange"
|
||||||
<el-button circle style="margin-left: 0;">
|
@order-change="handleColumnOrderChange"
|
||||||
<el-icon><Menu /></el-icon>
|
>
|
||||||
</el-button>
|
<template #trigger>
|
||||||
</el-tooltip>
|
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
||||||
</template>
|
<el-button circle style="margin-left: 0;">
|
||||||
</TableColumnControl>
|
<el-icon><Menu /></el-icon>
|
||||||
</right-toolbar>
|
</el-button>
|
||||||
</div>
|
</el-tooltip>
|
||||||
</el-row>
|
</template>
|
||||||
|
</TableColumnControl>
|
||||||
|
</right-toolbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table
|
<el-table
|
||||||
:data="state.dataList"
|
:data="state.dataList"
|
||||||
v-loading="state.loading"
|
v-loading="state.loading"
|
||||||
border
|
stripe
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle"
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
@sort-change="sortChangeHandle">
|
class="modern-table"
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
@sort-change="sortChangeHandle">
|
||||||
<template #header>
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<template v-for="col in sortedTableColumns" :key="col.prop || col.label">
|
|
||||||
<el-table-column
|
|
||||||
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
|
||||||
:prop="col.prop"
|
|
||||||
:label="col.label"
|
|
||||||
show-overflow-tooltip
|
|
||||||
align="center">
|
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
<el-icon><List /></el-icon>
|
||||||
<span style="margin-left: 4px">{{ col.label }}</span>
|
|
||||||
</template>
|
</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 !== '操作'"
|
||||||
|
:prop="col.prop"
|
||||||
|
:label="col.label"
|
||||||
|
show-overflow-tooltip
|
||||||
|
align="center">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
||||||
|
<span style="margin-left: 4px">{{ col.label }}</span>
|
||||||
|
</template>
|
||||||
<!-- 学期列特殊模板 -->
|
<!-- 学期列特殊模板 -->
|
||||||
<template v-if="col.prop === 'period'" #default="scope">
|
<template v-if="col.prop === 'period'" #default="scope">
|
||||||
<el-tag size="small" type="primary" effect="plain">
|
<el-tag size="small" type="primary" effect="plain">
|
||||||
@@ -146,37 +163,40 @@
|
|||||||
{{ formatState(scope.row.state) }}
|
{{ formatState(scope.row.state) }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</template>
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</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-column>
|
||||||
</template>
|
</el-table>
|
||||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><Setting /></el-icon>
|
|
||||||
<span style="margin-left: 4px">操作</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
icon="Edit"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleEdit(scope.row)">
|
|
||||||
编辑
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
icon="Delete"
|
|
||||||
text
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete(scope.row)">
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<pagination
|
<div class="pagination-wrapper">
|
||||||
@size-change="sizeChangeHandle"
|
<pagination
|
||||||
@current-change="currentChangeHandle"
|
@size-change="sizeChangeHandle"
|
||||||
v-bind="state.pagination" />
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 表单弹窗 -->
|
<!-- 表单弹窗 -->
|
||||||
@@ -194,8 +214,9 @@ import { getDicts } from "/@/api/admin/dict";
|
|||||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import FormDialog from './form.vue';
|
import FormDialog from './form.vue';
|
||||||
import { List, House, Calendar, Clock, Document, Collection, CreditCard, Money, User, Avatar, CircleCheck, Setting, Menu } from '@element-plus/icons-vue'
|
import { List, House, Calendar, Clock, Document, Collection, CreditCard, Money, User, Avatar, CircleCheck, Setting, Menu, Search } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumnControl'
|
||||||
|
import '/@/styles/modern-page.scss'
|
||||||
import { defineAsyncComponent } from 'vue'
|
import { defineAsyncComponent } from 'vue'
|
||||||
const StatusTag = defineAsyncComponent(() => import('/@/components/StatusTag/index.vue'))
|
const StatusTag = defineAsyncComponent(() => import('/@/components/StatusTag/index.vue'))
|
||||||
|
|
||||||
@@ -242,104 +263,14 @@ const columnConfigMap: Record<string, { icon: any }> = {
|
|||||||
state: { icon: CircleCheck }
|
state: { icon: CircleCheck }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns, route.path)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadSavedConfig()
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const sortedTableColumns = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: typeof tableColumns = []
|
|
||||||
const unorderedColumns: typeof tableColumns = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 列显示控制函数
|
|
||||||
const checkColumnVisible = (prop: string): boolean => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return visibleColumns.value.includes(prop)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列变化
|
|
||||||
const handleColumnChange = (columns: string[]) => {
|
|
||||||
visibleColumns.value = columns
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = columns.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列排序变化
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 搜索表单
|
// 搜索表单
|
||||||
const searchForm = reactive({
|
const searchForm = reactive({
|
||||||
@@ -544,7 +475,6 @@ onMounted(() => {
|
|||||||
getStateDict()
|
getStateDict()
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (visibleColumns.value.length === 0) {
|
if (visibleColumns.value.length === 0) {
|
||||||
loadSavedConfig()
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,9 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form
|
||||||
|
:model="searchForm"
|
||||||
|
ref="searchFormRef"
|
||||||
|
:inline="true"
|
||||||
|
@keyup.enter="handleSearch"
|
||||||
|
class="search-form">
|
||||||
<el-form-item label="学年" prop="schoolYear">
|
<el-form-item label="学年" prop="schoolYear">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="searchForm.schoolYear"
|
v-model="searchForm.schoolYear"
|
||||||
@@ -34,116 +47,135 @@
|
|||||||
style="width: 200px" />
|
style="width: 200px" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" plain icon="Search" @click="handleSearch">查询</el-button>
|
<el-button type="primary" icon="Search" @click="handleSearch">查询</el-button>
|
||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<el-button
|
<div class="card-header">
|
||||||
icon="FolderAdd"
|
<span class="card-title">
|
||||||
type="primary"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
@click="formDialogRef.openDialog()">
|
每周工作计划列表
|
||||||
新 增
|
</span>
|
||||||
</el-button>
|
<div class="header-actions">
|
||||||
<right-toolbar
|
<el-button
|
||||||
v-model:showSearch="showSearch"
|
icon="FolderAdd"
|
||||||
:export="'stuwork_weekPlan_export'"
|
type="primary"
|
||||||
@exportExcel="exportExcel"
|
@click="formDialogRef.openDialog()">
|
||||||
class="ml10"
|
新增
|
||||||
style="float: right;"
|
</el-button>
|
||||||
@queryTable="getDataList">
|
<right-toolbar
|
||||||
<TableColumnControl
|
v-model:showSearch="showSearch"
|
||||||
ref="columnControlRef"
|
:export="'stuwork_weekPlan_export'"
|
||||||
:columns="tableColumns"
|
@exportExcel="exportExcel"
|
||||||
v-model="visibleColumns"
|
class="ml10"
|
||||||
trigger-type="default"
|
@queryTable="getDataList">
|
||||||
trigger-circle
|
<TableColumnControl
|
||||||
@change="handleColumnChange"
|
ref="columnControlRef"
|
||||||
@order-change="handleColumnOrderChange"
|
:columns="tableColumns"
|
||||||
>
|
v-model="visibleColumns"
|
||||||
<template #trigger>
|
trigger-type="default"
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
trigger-circle
|
||||||
<el-button circle style="margin-left: 0;">
|
@change="handleColumnChange"
|
||||||
<el-icon><Menu /></el-icon>
|
@order-change="handleColumnOrderChange"
|
||||||
</el-button>
|
>
|
||||||
</el-tooltip>
|
<template #trigger>
|
||||||
</template>
|
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
||||||
</TableColumnControl>
|
<el-button circle style="margin-left: 0;">
|
||||||
</right-toolbar>
|
<el-icon><Menu /></el-icon>
|
||||||
</div>
|
</el-button>
|
||||||
</el-row>
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
</TableColumnControl>
|
||||||
|
</right-toolbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table
|
<el-table
|
||||||
:data="state.dataList"
|
:data="state.dataList"
|
||||||
v-loading="state.loading"
|
v-loading="state.loading"
|
||||||
border
|
stripe
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle"
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
@sort-change="sortChangeHandle">
|
class="modern-table"
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
@sort-change="sortChangeHandle">
|
||||||
<template #header>
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<template v-for="col in sortedTableColumns" :key="col.prop || col.label">
|
|
||||||
<el-table-column
|
|
||||||
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
|
||||||
:prop="col.prop"
|
|
||||||
:label="col.label"
|
|
||||||
show-overflow-tooltip>
|
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
<el-icon><List /></el-icon>
|
||||||
<span style="margin-left: 4px">{{ col.label }}</span>
|
|
||||||
</template>
|
</template>
|
||||||
<!-- 学期列特殊模板 -->
|
<template #default="{ $index }">
|
||||||
<template v-if="col.prop === 'schoolTerm'" #default="scope">
|
{{ $index + 1 + ((state.pagination?.current || 1) - 1) * (state.pagination?.size || 10) }}
|
||||||
<el-tag size="small" type="primary" effect="plain">
|
|
||||||
{{ scope.row.schoolTerm === 1 ? '上学期' : scope.row.schoolTerm === 2 ? '下学期' : scope.row.schoolTerm }}
|
|
||||||
</el-tag>
|
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</template>
|
<template v-for="col in visibleColumnsSorted" :key="col.prop || col.label">
|
||||||
<el-table-column label="操作" align="center" fixed="right">
|
<el-table-column
|
||||||
<template #header>
|
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
||||||
<el-icon><Setting /></el-icon>
|
:prop="col.prop"
|
||||||
<span style="margin-left: 4px">操作</span>
|
:label="col.label"
|
||||||
|
show-overflow-tooltip
|
||||||
|
:align="col.align || 'center'">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
||||||
|
<span style="margin-left: 4px">{{ col.label }}</span>
|
||||||
|
</template>
|
||||||
|
<!-- 学期列特殊模板 -->
|
||||||
|
<template v-if="col.prop === 'schoolTerm'" #default="scope">
|
||||||
|
<el-tag size="small" type="primary" effect="plain" round>
|
||||||
|
{{ scope.row.schoolTerm === 1 ? '上学期' : scope.row.schoolTerm === 2 ? '下学期' : scope.row.schoolTerm }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
</template>
|
</template>
|
||||||
<template #default="scope">
|
<el-table-column label="操作" width="250" align="center" fixed="right">
|
||||||
<el-button
|
<template #header>
|
||||||
icon="View"
|
<el-icon><Setting /></el-icon>
|
||||||
text
|
<span style="margin-left: 4px">操作</span>
|
||||||
type="primary"
|
</template>
|
||||||
@click="handleViewDetail(scope.row.id)">
|
<template #default="scope">
|
||||||
查看详情
|
<el-button
|
||||||
</el-button>
|
icon="View"
|
||||||
<el-button
|
link
|
||||||
icon="Edit"
|
type="primary"
|
||||||
text
|
@click="handleViewDetail(scope.row.id)">
|
||||||
type="primary"
|
查看详情
|
||||||
@click="formDialogRef.openDialog(scope.row.id)">
|
</el-button>
|
||||||
编辑
|
<el-button
|
||||||
</el-button>
|
icon="EditPen"
|
||||||
<el-button
|
link
|
||||||
icon="Delete"
|
type="primary"
|
||||||
text
|
@click="formDialogRef.openDialog(scope.row.id)">
|
||||||
type="danger"
|
编辑
|
||||||
@click="handleDelete([scope.row.id])">
|
</el-button>
|
||||||
删除
|
<el-button
|
||||||
</el-button>
|
icon="Delete"
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
@click="handleDelete([scope.row.id])">
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<template #empty>
|
||||||
|
<el-empty description="暂无数据" :image-size="120">
|
||||||
|
<el-button type="primary" icon="FolderAdd" @click="formDialogRef.openDialog()">新增计划</el-button>
|
||||||
|
</el-empty>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table>
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<pagination
|
<div class="pagination-wrapper">
|
||||||
@size-change="sizeChangeHandle"
|
<pagination
|
||||||
@current-change="currentChangeHandle"
|
@size-change="sizeChangeHandle"
|
||||||
v-bind="state.pagination" />
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 编辑、新增 -->
|
<!-- 编辑、新增 -->
|
||||||
@@ -155,24 +187,22 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="WeekPlan">
|
<script setup lang="ts" name="WeekPlan">
|
||||||
import { ref, reactive, defineAsyncComponent, onMounted, nextTick, computed } from 'vue'
|
import { ref, reactive, defineAsyncComponent, onMounted, nextTick } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||||
import { fetchList, delObjs } from "/@/api/stuwork/weekplan";
|
import { fetchList, delObjs } from "/@/api/stuwork/weekplan";
|
||||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||||
import { queryAllSchoolYear } from '/@/api/basic/basicyear'
|
import { queryAllSchoolYear } from '/@/api/basic/basicyear'
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import {
|
import {
|
||||||
List, Calendar, Clock, Document, Files, User, Setting, Menu
|
List, Calendar, Clock, Document, User, Setting, Menu, Search, EditPen
|
||||||
} from '@element-plus/icons-vue'
|
} from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
|
||||||
// 引入组件
|
// 引入组件
|
||||||
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
||||||
const DetailDialog = defineAsyncComponent(() => import('./detail.vue'));
|
const DetailDialog = defineAsyncComponent(() => import('./detail.vue'));
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
|
||||||
const formDialogRef = ref()
|
const formDialogRef = ref()
|
||||||
const detailDialogRef = ref()
|
const detailDialogRef = ref()
|
||||||
const searchFormRef = ref()
|
const searchFormRef = ref()
|
||||||
@@ -188,12 +218,12 @@ const searchForm = reactive({
|
|||||||
author: ''
|
author: ''
|
||||||
})
|
})
|
||||||
|
|
||||||
// 表格列配置(不包含操作列,操作列单独渲染)
|
// 表格列配置
|
||||||
const tableColumns = [
|
const tableColumns = [
|
||||||
{ prop: 'schoolYear', label: '学年' },
|
{ prop: 'schoolYear', label: '学年', icon: Calendar },
|
||||||
{ prop: 'schoolTerm', label: '学期' },
|
{ prop: 'schoolTerm', label: '学期', icon: Clock },
|
||||||
{ prop: 'title', label: '标题' },
|
{ prop: 'title', label: '标题', icon: Document },
|
||||||
{ prop: 'author', label: '作者' }
|
{ prop: 'author', label: '作者', icon: User }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 列配置映射(用于图标)
|
// 列配置映射(用于图标)
|
||||||
@@ -204,108 +234,14 @@ const columnConfigMap: Record<string, { icon: any }> = {
|
|||||||
author: { icon: User }
|
author: { icon: User }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 当前显示的列(从 localStorage 读取或默认全部显示)
|
// 使用表格列控制 Hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
const validColumns = tableColumns
|
|
||||||
.filter(col => !col.alwaysShow && !col.fixed)
|
|
||||||
.map(col => col.prop || col.label)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = validColumns
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = validColumns
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// 立即加载保存的配置
|
|
||||||
loadSavedConfig()
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const sortedTableColumns = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return col.alwaysShow || col.fixed || visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: typeof tableColumns = []
|
|
||||||
const unorderedColumns: typeof tableColumns = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 列显示控制函数
|
|
||||||
const checkColumnVisible = (prop: string): boolean => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return visibleColumns.value.includes(prop)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列变化
|
|
||||||
const handleColumnChange = (columns: string[]) => {
|
|
||||||
visibleColumns.value = columns
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = columns.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列排序变化
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 配置 useTable
|
// 配置 useTable
|
||||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||||
@@ -314,7 +250,8 @@ const state: BasicTableProps = reactive<BasicTableProps>({
|
|||||||
props: {
|
props: {
|
||||||
item: 'records',
|
item: 'records',
|
||||||
totalCount: 'total'
|
totalCount: 'total'
|
||||||
}
|
},
|
||||||
|
createdIsNeed: true
|
||||||
})
|
})
|
||||||
|
|
||||||
// table hook
|
// table hook
|
||||||
@@ -392,11 +329,9 @@ const getSchoolYearList = async () => {
|
|||||||
// 初始化
|
// 初始化
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getSchoolYearList()
|
getSchoolYearList()
|
||||||
// 确保配置已同步
|
|
||||||
nextTick(() => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
loadSavedConfig()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import '/@/assets/styles/modern-page.scss';
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-padding">
|
<div class="modern-page-container">
|
||||||
<div class="layout-padding-auto layout-padding-view">
|
<div class="page-wrapper">
|
||||||
<!-- 搜索表单 -->
|
<!-- 搜索表单卡片 -->
|
||||||
<el-row v-show="showSearch">
|
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||||
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch">
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<el-icon class="title-icon"><Search /></el-icon>
|
||||||
|
筛选条件
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch" class="search-form">
|
||||||
<el-form-item label="学院" prop="deptCode">
|
<el-form-item label="学院" prop="deptCode">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="searchForm.deptCode"
|
v-model="searchForm.deptCode"
|
||||||
@@ -51,62 +59,72 @@
|
|||||||
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
<el-button icon="Refresh" @click="handleReset">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-card>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 内容卡片 -->
|
||||||
<el-row>
|
<el-card class="content-card" shadow="never">
|
||||||
<div class="mb8" style="width: 100%">
|
<template #header>
|
||||||
<right-toolbar
|
<div class="card-header">
|
||||||
v-model:showSearch="showSearch"
|
<span class="card-title">
|
||||||
class="ml10"
|
<el-icon class="title-icon"><Document /></el-icon>
|
||||||
style="float: right;"
|
勤工助学考勤列表
|
||||||
@queryTable="getDataList">
|
</span>
|
||||||
<TableColumnControl
|
<div class="header-actions">
|
||||||
ref="columnControlRef"
|
<right-toolbar
|
||||||
:columns="tableColumns"
|
v-model:showSearch="showSearch"
|
||||||
v-model="visibleColumns"
|
class="ml10"
|
||||||
trigger-type="default"
|
@queryTable="getDataList">
|
||||||
trigger-circle
|
<TableColumnControl
|
||||||
@change="handleColumnChange"
|
ref="columnControlRef"
|
||||||
@order-change="handleColumnOrderChange"
|
:columns="tableColumns"
|
||||||
>
|
v-model="visibleColumns"
|
||||||
<template #trigger>
|
trigger-type="default"
|
||||||
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
trigger-circle
|
||||||
<el-button circle style="margin-left: 0;">
|
@change="handleColumnChange"
|
||||||
<el-icon><Menu /></el-icon>
|
@order-change="handleColumnOrderChange"
|
||||||
</el-button>
|
>
|
||||||
</el-tooltip>
|
<template #trigger>
|
||||||
</template>
|
<el-tooltip class="item" effect="dark" content="列设置" placement="top">
|
||||||
</TableColumnControl>
|
<el-button circle style="margin-left: 0;">
|
||||||
</right-toolbar>
|
<el-icon><Menu /></el-icon>
|
||||||
</div>
|
</el-button>
|
||||||
</el-row>
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
</TableColumnControl>
|
||||||
|
</right-toolbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 表格 -->
|
<!-- 表格 -->
|
||||||
<el-table
|
<el-table
|
||||||
:data="state.dataList"
|
:data="state.dataList"
|
||||||
v-loading="state.loading"
|
v-loading="state.loading"
|
||||||
border
|
stripe
|
||||||
:cell-style="tableStyle.cellStyle"
|
:cell-style="tableStyle.cellStyle"
|
||||||
:header-cell-style="tableStyle.headerCellStyle">
|
:header-cell-style="tableStyle.headerCellStyle"
|
||||||
<el-table-column type="index" label="序号" width="60" align="center">
|
class="modern-table">
|
||||||
<template #header>
|
<el-table-column type="index" label="序号" width="70" align="center">
|
||||||
<el-icon><List /></el-icon>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<template v-for="col in sortedTableColumns" :key="col.prop || col.label">
|
|
||||||
<el-table-column
|
|
||||||
v-if="checkColumnVisible(col.prop || '') && col.prop !== '操作'"
|
|
||||||
:prop="col.prop"
|
|
||||||
:label="col.label"
|
|
||||||
show-overflow-tooltip
|
|
||||||
align="center"
|
|
||||||
:min-width="col.minWidth"
|
|
||||||
:width="col.width">
|
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
<el-icon><List /></el-icon>
|
||||||
<span style="margin-left: 4px">{{ col.label }}</span>
|
|
||||||
</template>
|
</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 !== '操作'"
|
||||||
|
:prop="col.prop"
|
||||||
|
:label="col.label"
|
||||||
|
show-overflow-tooltip
|
||||||
|
align="center"
|
||||||
|
:min-width="col.minWidth"
|
||||||
|
:width="col.width">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><component :is="columnConfigMap[col.prop]?.icon || Calendar" /></el-icon>
|
||||||
|
<span style="margin-left: 4px">{{ col.label }}</span>
|
||||||
|
</template>
|
||||||
<!-- 学期列特殊模板 -->
|
<!-- 学期列特殊模板 -->
|
||||||
<template v-if="col.prop === 'schoolTerm'" #default="scope">
|
<template v-if="col.prop === 'schoolTerm'" #default="scope">
|
||||||
<el-tag size="small" type="primary" effect="plain">
|
<el-tag size="small" type="primary" effect="plain">
|
||||||
@@ -123,30 +141,33 @@
|
|||||||
<template v-else-if="col.prop === 'remarks'" #default="scope">
|
<template v-else-if="col.prop === 'remarks'" #default="scope">
|
||||||
<span>{{ scope.row.remarks || '-' }}</span>
|
<span>{{ scope.row.remarks || '-' }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</template>
|
||||||
|
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span style="margin-left: 4px">操作</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
icon="View"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="handleView(scope.row)">
|
||||||
|
查看详情
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</template>
|
</el-table>
|
||||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
|
||||||
<template #header>
|
|
||||||
<el-icon><Setting /></el-icon>
|
|
||||||
<span style="margin-left: 4px">操作</span>
|
|
||||||
</template>
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
icon="View"
|
|
||||||
text
|
|
||||||
type="primary"
|
|
||||||
@click="handleView(scope.row)">
|
|
||||||
查看详情
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<pagination
|
<div class="pagination-wrapper">
|
||||||
@size-change="sizeChangeHandle"
|
<pagination
|
||||||
@current-change="currentChangeHandle"
|
@size-change="sizeChangeHandle"
|
||||||
v-bind="state.pagination" />
|
@current-change="currentChangeHandle"
|
||||||
|
v-bind="state.pagination" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -160,8 +181,9 @@ import { getDeptList } from "/@/api/basic/basicclass";
|
|||||||
import { getClassListByRole } from "/@/api/basic/basicclass";
|
import { getClassListByRole } from "/@/api/basic/basicclass";
|
||||||
import { useMessage } from "/@/hooks/message";
|
import { useMessage } from "/@/hooks/message";
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import { List, Calendar, Clock, OfficeBuilding, Grid, Document, CreditCard, Avatar, Collection, Setting, Menu } from '@element-plus/icons-vue'
|
import { List, Calendar, Clock, OfficeBuilding, Grid, Document, CreditCard, Avatar, Collection, Setting, Menu, Search } from '@element-plus/icons-vue'
|
||||||
import { getTableConfigFromLocal, saveTableConfigToLocal, updateUserTableConfig } from '/@/api/admin/usertable'
|
import { useTableColumnControl } from '/@/hooks/tableColumnControl'
|
||||||
|
import '/@/styles/modern-page.scss'
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -200,104 +222,14 @@ const columnConfigMap: Record<string, { icon: any }> = {
|
|||||||
remarks: { icon: Document }
|
remarks: { icon: Document }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 当前显示的列
|
// 使用表格列控制hook
|
||||||
const visibleColumns = ref<string[]>([])
|
const {
|
||||||
// 列排序顺序
|
visibleColumns,
|
||||||
const columnOrder = ref<string[]>([])
|
visibleColumnsSorted,
|
||||||
|
checkColumnVisible,
|
||||||
// 从本地统一存储加载配置
|
handleColumnChange,
|
||||||
const loadSavedConfig = () => {
|
handleColumnOrderChange
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
} = useTableColumnControl(tableColumns, route.path)
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const savedConfig = getTableConfigFromLocal(storageKey)
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.visibleColumns) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
const filteredSaved = savedConfig.visibleColumns.filter((col: string) => validColumns.includes(col))
|
|
||||||
visibleColumns.value = filteredSaved.length > 0 ? filteredSaved : validColumns
|
|
||||||
} else {
|
|
||||||
visibleColumns.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedConfig && savedConfig.columnOrder) {
|
|
||||||
const validColumns = tableColumns.map(col => col.prop || col.label)
|
|
||||||
columnOrder.value = savedConfig.columnOrder.filter((key: string) => validColumns.includes(key))
|
|
||||||
validColumns.forEach(key => {
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
columnOrder.value.push(key)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
columnOrder.value = tableColumns.map(col => col.prop || col.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadSavedConfig()
|
|
||||||
|
|
||||||
// 排序后的表格列
|
|
||||||
const sortedTableColumns = computed(() => {
|
|
||||||
const columns = tableColumns.filter(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
return visibleColumns.value.includes(key)
|
|
||||||
})
|
|
||||||
|
|
||||||
if (columnOrder.value.length > 0) {
|
|
||||||
const orderedColumns: typeof tableColumns = []
|
|
||||||
const unorderedColumns: typeof tableColumns = []
|
|
||||||
|
|
||||||
columnOrder.value.forEach(key => {
|
|
||||||
const col = columns.find(c => (c.prop || c.label) === key)
|
|
||||||
if (col) {
|
|
||||||
orderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
columns.forEach(col => {
|
|
||||||
const key = col.prop || col.label
|
|
||||||
if (!columnOrder.value.includes(key)) {
|
|
||||||
unorderedColumns.push(col)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return [...orderedColumns, ...unorderedColumns]
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns
|
|
||||||
})
|
|
||||||
|
|
||||||
// 列显示控制函数
|
|
||||||
const checkColumnVisible = (prop: string): boolean => {
|
|
||||||
if (visibleColumns.value.length === 0) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return visibleColumns.value.includes(prop)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列变化
|
|
||||||
const handleColumnChange = (columns: string[]) => {
|
|
||||||
visibleColumns.value = columns
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
const selectableColumns = columns.filter(col => {
|
|
||||||
const column = tableColumns.find(c => (c.prop || c.label) === col)
|
|
||||||
return column && !column.alwaysShow && !column.fixed
|
|
||||||
})
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { visibleColumns: selectableColumns })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { visibleColumns: selectableColumns }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听列排序变化
|
|
||||||
const handleColumnOrderChange = (order: string[]) => {
|
|
||||||
columnOrder.value = order
|
|
||||||
const routePath = route.path.replace(/^\//, '').replace(/\//g, '-')
|
|
||||||
const storageKey = `table-columns-${routePath}`
|
|
||||||
// 保存到本地统一存储
|
|
||||||
saveTableConfigToLocal(storageKey, { columnOrder: order })
|
|
||||||
// 异步保存到后端
|
|
||||||
updateUserTableConfig(storageKey, { columnOrder: order }).catch(() => {})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 搜索表单
|
// 搜索表单
|
||||||
const searchForm = reactive({
|
const searchForm = reactive({
|
||||||
@@ -446,7 +378,6 @@ onMounted(() => {
|
|||||||
getClassListData()
|
getClassListData()
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (visibleColumns.value.length === 0) {
|
if (visibleColumns.value.length === 0) {
|
||||||
loadSavedConfig()
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user