提交 c7a36aff authored 作者: 王鹏飞's avatar 王鹏飞

chore: 学生管理新增批量修改

上级 72c8ad47
......@@ -4,12 +4,16 @@ import httpRequest from '@/utils/axios'
export function getStudentList(params?: { name?: string; organ_id?: string; page?: string; 'per-page'?: string }) {
return httpRequest.get('/api/resource/v1/learning/student/list', { params })
}
// // 导入学生
export function importStudent(data: { file: any }) {
return httpRequest.post('/api/resource/v1/learning/student/import', data, {
headers: { 'Content-Type': 'multipart/form-data' },
})
// 导入学生
export function importStudent(data: { url: string; name: string; size: number }) {
return httpRequest.post('/api/resource/v1/learning/student/import', data)
}
// 批量修改学生
export function batchUpdateStudent(data: { url: string; name: string; size: number }) {
return httpRequest.post('/api/resource/v1/learning/student/batch-update', data)
}
// 导出学生
export function exportStudent(params: { name: string; organ_id: string }) {
return httpRequest.get('/api/resource/v1/learning/student/download', { params, responseType: 'blob' })
......@@ -67,3 +71,8 @@ export function getClassList(params?: {
}) {
return httpRequest.get('/api/resource/v1/learning/class/list', { params })
}
// 学生导入进度查询
export function getStudentProgress(params: { id: string }) {
return httpRequest.get('/api/resource/v1/learning/student/progress', { params })
}
<script lang="ts" setup>
import { ElMessage } from 'element-plus'
import { UploadFilled } from '@element-plus/icons-vue'
import { UploadFilled, Loading } from '@element-plus/icons-vue'
import { splitStrLast } from '@/utils/util'
import { importStudent } from '../api'
import { importStudent, getStudentProgress } from '../api'
import { upload } from '@/utils/upload'
const emit = defineEmits<Emits>()
const upload = ref()
const uploadRef = ref()
const fileList = ref([]) // 文件列表
defineProps({
isShowImportDialog: {
type: Boolean
}
type: Boolean,
},
})
interface Emits {
(e: 'update:isShowImportDialog', isShowImportDialog: boolean): void
......@@ -31,26 +33,64 @@ const beforeUpload = (file: any) => {
return true
}
}
const fetchFileUpload = (option: any) => {
return new Promise(() => {
importStudent({ file: option.file }).then(() => {
ElMessage.success('导入数据成功')
emit('update:isShowImportDialog', false)
const loading = ref(false)
const progress = reactive({
progress: '-',
total: '-',
message: '导入中',
status: 2,
})
let timer = null
const fetchFileUpload = async (option: any) => {
const file = option.file
const url = await upload(file)
const res = await importStudent({ url, name: file.name, size: file.size })
const id = res.data.id
loading.value = true
timer && clearInterval(timer)
timer = setInterval(() => {
getStudentProgress({ id })
.then((res) => {
const data = res.data
Object.assign(progress, data)
if (data.status === 3 || data.status === 4) {
clearInterval(timer)
loading.value = false
ElMessage({
type: data.status === 3 ? 'success' : 'error',
message: data.message,
})
emit('create')
}
})
.catch(() => {
clearInterval(timer)
loading.value = false
})
}, 1000)
}
onUnmounted(() => {
timer && clearInterval(timer)
})
const handleSubmitUpload = () => {
upload.value.submit()
uploadRef.value.submit()
}
</script>
<template>
<el-dialog :model-value="isShowImportDialog" draggable :before-close="handleCancel" title="批量导入学生" width="30%">
<el-dialog
draggable
:model-value="isShowImportDialog"
:close-on-click-modal="false"
:before-close="handleCancel"
title="批量导入学生"
width="500px"
custom-class="my-dialog">
<el-upload
style="text-align: center"
class="file-import"
ref="upload"
ref="uploadRef"
action="#"
accept=".xls,.xlsx"
drag
......@@ -62,14 +102,18 @@ const handleSubmitUpload = () => {
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
<div class="el-upload__text">将文件拖至此处,点击上传</div>
</el-upload>
<div style="margin-bottom: 10px; text-align: center">
导入模板下载:<a
<div style="margin-bottom: 10px; text-align: right">
<a
href="/center_resource/%E5%AD%A6%E7%94%9F%E5%AF%BC%E5%85%A5%E6%A8%A1%E6%9D%BF.xlsx"
download="学生模板"
><el-link type="primary">学生模板.xlsx</el-link></a
>
download="批量导入学生模板">
<el-link type="primary">下载模板</el-link>
</a>
</div>
<div class="dialog-loading" v-if="loading">
<el-icon class="is-loading"><Loading /></el-icon>
<p>批量导入中</p>
<p>{{ progress.progress }}/{{ progress.total }}</p>
</div>
<template #footer>
<span>
<el-button @click="handleCancel">取消</el-button>
......@@ -78,3 +122,24 @@ const handleSubmitUpload = () => {
</template>
</el-dialog>
</template>
<style lang="scss">
.my-dialog {
position: relative;
}
.dialog-loading {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.6);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
font-size: 16px;
line-height: 30px;
color: #fff;
}
</style>
<script lang="ts" setup>
import { ElMessage } from 'element-plus'
import { UploadFilled, Loading } from '@element-plus/icons-vue'
import { splitStrLast } from '@/utils/util'
import { batchUpdateStudent, getStudentProgress } from '../api'
import { upload } from '@/utils/upload'
const emit = defineEmits<Emits>()
const uploadRef = ref()
const fileList = ref([]) // 文件列表
defineProps({
modelValue: { type: Boolean },
})
interface Emits {
(e: 'update:modelValue', modelValue: boolean): void
(e: 'create'): void
}
// 取消
const handleCancel = () => {
emit('update:modelValue', false)
}
const beforeUpload = (file: any) => {
const suffix = splitStrLast(file.name, '.')
if (!['xlsx', 'xls'].includes(suffix)) {
ElMessage.warning('只能上传excel文件')
return false
} else {
return true
}
}
const loading = ref(false)
const progress = reactive({
progress: '-',
total: '-',
message: '导入中',
status: 2,
})
let timer = null
const fetchFileUpload = async (option: any) => {
const file = option.file
const url = await upload(file)
const res = await batchUpdateStudent({ url, name: file.name, size: file.size })
const id = res.data.id
loading.value = true
timer && clearInterval(timer)
timer = setInterval(() => {
getStudentProgress({ id })
.then((res) => {
const data = res.data
Object.assign(progress, data)
if (data.status === 3 || data.status === 4) {
clearInterval(timer)
loading.value = false
ElMessage({
type: data.status === 3 ? 'success' : 'error',
message: data.message,
})
emit('create')
}
})
.catch(() => {
clearInterval(timer)
loading.value = false
})
}, 1000)
}
const handleSubmitUpload = () => {
uploadRef.value.submit()
}
</script>
<template>
<el-dialog
draggable
:model-value="modelValue"
:close-on-click-modal="false"
:before-close="handleCancel"
title="批量修改学生"
width="500px">
<el-upload
style="text-align: center"
class="file-import"
ref="uploadRef"
action="#"
accept=".xls,.xlsx"
drag
:auto-upload="false"
:file-list="fileList"
:limit="1"
:before-upload="beforeUpload"
:http-request="fetchFileUpload">
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
<div class="el-upload__text">将文件拖至此处,点击上传</div>
</el-upload>
<div style="margin-bottom: 10px; text-align: right">
<a href="/center_resource/%E5%AD%A6%E7%94%9F%E5%AF%BC%E5%85%A5%E6%A8%A1%E6%9D%BF.xlsx" download="批量修改学生模板"
><el-link type="primary">下载模板</el-link></a
>
</div>
<div class="dialog-loading" v-if="loading">
<el-icon class="is-loading"><Loading /></el-icon>
<p>批量修改中</p>
<p>{{ progress.progress }}/{{ progress.total }}</p>
</div>
<template #footer>
<span>
<el-button @click="handleCancel">取消</el-button>
<el-button type="primary" @click="handleSubmitUpload">确认</el-button>
</span>
</template>
</el-dialog>
</template>
<style lang="scss">
.my-dialog {
position: relative;
}
.dialog-loading {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.6);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
font-size: 16px;
line-height: 30px;
color: #fff;
}
</style>
......@@ -3,6 +3,7 @@ import { ElMessage } from 'element-plus'
import { useProjectList } from '@/composables/useGetProjectList'
import AddStudent from '../components/AddStudent.vue'
import ImportStudent from '../components/ImportStudent.vue'
import UpdateStudent from '../components/UpdateStudent.vue'
// import SourceAnalysis from '../components/SourceAnalysis.vue'
import { getStudentList, exportStudent, updateStudent } from '../api'
......@@ -67,7 +68,7 @@ const listOptions = computed(() => {
{ type: 'input', prop: 'mobile', label: '学生电话:', placeholder: '学生电话' },
],
columns: [
{ type: 'selection' },
// { type: 'selection' },
{ label: '序号', type: 'index', align: 'center' },
{ label: '学号', prop: 'sno_number', align: 'center', minWidth: '200' },
{ label: '姓名', prop: 'name', align: 'center', minWidth: '100' },
......@@ -125,20 +126,25 @@ const handleChangeStatus = (row: any) => {
const handleImport = () => {
isShowImportDialog.value = true
}
// 批量修改
const updateDialogVisible = ref(false)
const handleUpdate = () => {
updateDialogVisible.value = true
}
// 导出
const handleExport = () => {
const params = Object.assign({}, appList.value?.params)
exportStudent(params).then((r: any) => {
const blob = new Blob([r], { type: 'application/vnd.ms-excel' })
if ('download' in document.createElement('a')) {
const elink = document.createElement('a')
elink.download = '学员数据.xlsx'
elink.style.display = 'none'
elink.href = URL.createObjectURL(blob)
document.body.appendChild(elink)
elink.click()
URL.revokeObjectURL(elink.href)
document.body.removeChild(elink)
const link = document.createElement('a')
link.download = '学员数据.xlsx'
link.style.display = 'none'
link.href = URL.createObjectURL(blob)
document.body.appendChild(link)
link.click()
URL.revokeObjectURL(link.href)
document.body.removeChild(link)
}
})
}
......@@ -171,7 +177,10 @@ const handleAnalysis = () => {
>批量导入</el-button
>
<el-button type="primary" round @click="handleExport" v-permission="'v1-learning-student-download'"
>导出</el-button
>批量导出</el-button
>
<el-button type="primary" round @click="handleUpdate" v-permission="'v1-learning-student-import'"
>批量修改</el-button
>
<el-button type="primary" round @click="handleAnalysis">生源地分析</el-button>
<template #status="{ row }">
......@@ -207,6 +216,8 @@ const handleAnalysis = () => {
@create="handleRefresh" />
<!-- 导入学生 -->
<ImportStudent v-if="isShowImportDialog" v-model:isShowImportDialog="isShowImportDialog" @create="handleRefresh" />
<!-- 导入学生 -->
<UpdateStudent v-if="updateDialogVisible" v-model="updateDialogVisible" @create="handleRefresh" />
<!-- 生源地分析 -->
<!-- <SourceAnalysis v-if="isShowAnalysisDialog" v-model:isShowAnalysisDialog="isShowAnalysisDialog" /> -->
</template>
import md5 from 'blueimp-md5'
import { getSignature, uploadFile } from '@/api/base'
export async function upload(blob: Blob) {
const key = 'upload/resource-center/' + md5(new Date().getTime() + Math.random().toString(36).slice(-8)) + '.png'
const response: any = await getSignature()
const params = {
key,
host: response.host,
OSSAccessKeyId: response.accessid,
policy: response.policy,
signature: response.signature,
success_action_status: '200',
file: blob,
url: `${response.host}/${key}`,
}
await uploadFile(params)
return params.url
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论