|
@@ -0,0 +1,246 @@
|
|
|
+<template>
|
|
|
+ <div class="d-flex flex-column align-center">
|
|
|
+ <CtForm ref="CtFormRef" :items="formItems" style="width: 700px;">
|
|
|
+ <template #headImg="{ item }">
|
|
|
+ <div style="color: #7a7a7a;">头像</div>
|
|
|
+ <div class="avatarsBox" @mouseover="showIcon = true" @mouseleave="showIcon = false">
|
|
|
+ <v-avatar class="elevation-5" size=80 :image="getUserAvatar(item.value, baseInfo?.sex)"></v-avatar>
|
|
|
+ <div v-show="showIcon" @click="openFileInput" v-bind="$attrs" class="mdi mdi-camera-outline">
|
|
|
+ <input
|
|
|
+ type="file"
|
|
|
+ ref="fileInput"
|
|
|
+ accept="image/png, image/jpg, image/jpeg"
|
|
|
+ style="display: none;"
|
|
|
+ @change="handleUploadFile"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div style="font-size: 14px; color: var(--color-999);">只支持JPG、JPEG、PNG类型的图片,大小不超过20M</div>
|
|
|
+ </template>
|
|
|
+ <template #department>
|
|
|
+ <div class="pa-5 mb-3" style="width: 100%; border: 1px dashed #ccc; border-radius: 4px;">
|
|
|
+ <p class="color-999 font-size-14 mb-3">
|
|
|
+ <span class="color-error">*</span>
|
|
|
+ 负责院系
|
|
|
+ </p>
|
|
|
+ <div v-for="(k, index) in departmentList" :key="index" class="d-flex align-center mb-5">
|
|
|
+ <TextInput v-model="k.departmentTitle" :item="textItem" />
|
|
|
+ <v-icon v-if="index > 0" class="ml-3 cursor-pointer" @click="handleDeleteDepartment(index)" color="error">mdi-close-circle</v-icon>
|
|
|
+ </div>
|
|
|
+ <v-btn class="mt-3" color="primary" prepend-icon="mdi-plus" size="small" @click="handleAddDepartment">添加院系</v-btn>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </CtForm>
|
|
|
+ <v-btn class="buttons my-10" color="primary" @click.stop="handleSubmit">{{ $t('common.save') }}</v-btn>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <Loading :visible="overlay"></Loading>
|
|
|
+ <ImgCropper :visible="isShowCopper" :image="selectPic" :cropBoxResizable="true" @submit="handleHideCopper" :aspectRatio="1 / 1" @close="isShowCopper = false"></ImgCropper>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+defineOptions({ name: 'teacherInfo'})
|
|
|
+import { ref, onMounted } from 'vue'
|
|
|
+import { getUserAvatar } from '@/utils/avatar'
|
|
|
+import { getDict } from '@/hooks/web/useDictionaries'
|
|
|
+import { useI18n } from '@/hooks/web/useI18n'
|
|
|
+import { useUserStore } from '@/store/user'
|
|
|
+import { cloneDeep } from 'lodash'
|
|
|
+import { uploadFile } from '@/api/common'
|
|
|
+import Snackbar from '@/plugins/snackbar'
|
|
|
+import { schoolBaseInfoEdit, schoolDepartmentEdit } from '@/api/school'
|
|
|
+
|
|
|
+const showIcon = ref(false)
|
|
|
+const CtFormRef = ref()
|
|
|
+const departmentList = ref([{ departmentTitle: '' }])
|
|
|
+const textItem = {
|
|
|
+ type: 'text',
|
|
|
+ key: 'departmentTitle',
|
|
|
+ width: 450,
|
|
|
+ label: '院系名称 *',
|
|
|
+ hideDetails: true,
|
|
|
+ rules: [v => !!v || '请输入您负责的院系名称']
|
|
|
+}
|
|
|
+const formItems = ref({
|
|
|
+ options: [
|
|
|
+ {
|
|
|
+ slotName: 'headImg',
|
|
|
+ key: 'headImg',
|
|
|
+ value: '',
|
|
|
+ flexStyle: 'align-center'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ type: 'ifRadio',
|
|
|
+ key: 'teacherSex',
|
|
|
+ value: '1',
|
|
|
+ defaultValue: '1',
|
|
|
+ label: '性别 *',
|
|
|
+ width: 50,
|
|
|
+ dictTypeName: 'menduner_sex',
|
|
|
+ rules: [v => !!v || '请选择您的性别'],
|
|
|
+ items: []
|
|
|
+ },
|
|
|
+ {
|
|
|
+ type: 'text',
|
|
|
+ key: 'teacherNickname',
|
|
|
+ value: '',
|
|
|
+ label: '昵称 *',
|
|
|
+ slotName: 'teacherNickname',
|
|
|
+ rules: [v => !!v || '请输入您的昵称']
|
|
|
+ },
|
|
|
+ {
|
|
|
+ slotName: 'department',
|
|
|
+ key: 'departmentTitle',
|
|
|
+ noParam: true,
|
|
|
+ label: '负责院系 *',
|
|
|
+ rules: [v => !!v || '请填写您在学校负责的院系']
|
|
|
+ }
|
|
|
+ ]
|
|
|
+})
|
|
|
+
|
|
|
+const { t } = useI18n()
|
|
|
+const userStore = useUserStore()
|
|
|
+const schoolInfo = ref(localStorage.getItem('schoolInfo') ? JSON.parse(localStorage.getItem('schoolInfo')) : {})
|
|
|
+// 监听store变化
|
|
|
+userStore.$subscribe((mutation, state) => {
|
|
|
+ schoolInfo.value = state.schoolInfo
|
|
|
+})
|
|
|
+
|
|
|
+onMounted(async () => {
|
|
|
+ await userStore.getSchoolInfo()
|
|
|
+
|
|
|
+ // 获取性别字典数据
|
|
|
+ const sexItem = formItems.value.options.find(e => e.key === 'teacherSex')
|
|
|
+ if (!sexItem || !Object.keys(sexItem).length) return
|
|
|
+ const { data } = await getDict(sexItem.dictTypeName)
|
|
|
+ sexItem.items = data || []
|
|
|
+
|
|
|
+ formItems.value.options.forEach(item => {
|
|
|
+ if (!item.noParam) item.value = schoolInfo.value?.school[item.key] || item.defaultValue
|
|
|
+ else {
|
|
|
+ departmentList.value = schoolInfo.value?.schoolDepartments && schoolInfo.value.schoolDepartments.length ? schoolInfo.value.schoolDepartments : [{ departmentTitle: '' }]
|
|
|
+ }
|
|
|
+ })
|
|
|
+})
|
|
|
+
|
|
|
+// 添加院系
|
|
|
+const handleAddDepartment = () => {
|
|
|
+ departmentList.value.push({ departmentTitle: '' })
|
|
|
+}
|
|
|
+// 删除院系
|
|
|
+const handleDeleteDepartment = (index) => {
|
|
|
+ departmentList.value.splice(index, 1)
|
|
|
+}
|
|
|
+
|
|
|
+// 图片裁剪
|
|
|
+const overlay = ref(false)
|
|
|
+const selectPic = ref('')
|
|
|
+const isShowCopper = ref(false)
|
|
|
+
|
|
|
+// 选择文件
|
|
|
+const fileInput = ref()
|
|
|
+const clicked = ref(false)
|
|
|
+const openFileInput = () => {
|
|
|
+ if (clicked.value) return
|
|
|
+ clicked.value = true
|
|
|
+ fileInput.value.click()
|
|
|
+ clicked.value = false
|
|
|
+}
|
|
|
+
|
|
|
+// 上传头像
|
|
|
+const accept = ['jpg', 'png', 'jpeg']
|
|
|
+const handleUploadFile = async (e) => {
|
|
|
+ const file = e.target.files[0]
|
|
|
+ if (!file) return
|
|
|
+
|
|
|
+ const arr = file.name.split('.')
|
|
|
+ const fileType = arr?.length ? arr[arr.length-1] : ''
|
|
|
+ if (!accept.includes(fileType)) return Snackbar.warning('请上传图片格式文件')
|
|
|
+
|
|
|
+ const size = file.size
|
|
|
+ if (size / (1024*1024) > 20) {
|
|
|
+ Snackbar.warning(t('common.fileSizeExceed'))
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ const reader = new FileReader()
|
|
|
+ reader.readAsDataURL(file)
|
|
|
+ reader.onload = () => {
|
|
|
+ selectPic.value = String(reader.result)
|
|
|
+ isShowCopper.value = true
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const handleHideCopper = (data) => {
|
|
|
+ isShowCopper.value = false
|
|
|
+ if (data) {
|
|
|
+ const { file } = data
|
|
|
+ if (!file) return
|
|
|
+
|
|
|
+ const formData = new FormData()
|
|
|
+ formData.append('file', file)
|
|
|
+ formData.append('path', 'img')
|
|
|
+ uploadFile(formData).then(async ({ data }) => {
|
|
|
+ if (!data) return
|
|
|
+ formItems.value.options.find(e => e.key === 'headImg').value = data
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 保存
|
|
|
+const handleSubmit = async () => {
|
|
|
+ const { valid } = await CtFormRef.value.formRef.validate()
|
|
|
+ if (!valid) return
|
|
|
+
|
|
|
+ const isCheck = departmentList.value.every(item => item.departmentTitle)
|
|
|
+ if (!isCheck) return Snackbar.warning('请将院系信息填写完整')
|
|
|
+
|
|
|
+ overlay.value = true
|
|
|
+
|
|
|
+ let obj = cloneDeep(schoolInfo.value?.school)
|
|
|
+ formItems.value.options.forEach(item => {
|
|
|
+ if (item.noParam) return
|
|
|
+ obj[item.key] = item.value
|
|
|
+ })
|
|
|
+
|
|
|
+ try {
|
|
|
+ await schoolBaseInfoEdit(obj)
|
|
|
+ // 院系编辑
|
|
|
+ await schoolDepartmentEdit(departmentList.value)
|
|
|
+
|
|
|
+ setTimeout(async () => {
|
|
|
+ await userStore.getSchoolInfo()
|
|
|
+ Snackbar.success(t('common.saveMsg'))
|
|
|
+ overlay.value = false
|
|
|
+ }, 1000)
|
|
|
+ } catch {
|
|
|
+ overlay.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped lang="scss">
|
|
|
+.avatarsBox {
|
|
|
+ height: 80px;
|
|
|
+ width: 80px;
|
|
|
+ position: relative;
|
|
|
+ cursor: pointer;
|
|
|
+ margin: 32px;
|
|
|
+ margin-right: 40px;
|
|
|
+ .img {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ }
|
|
|
+ .mdi {
|
|
|
+ font-size: 42px;
|
|
|
+ color: #fff;
|
|
|
+ }
|
|
|
+ div {
|
|
|
+ position: absolute;
|
|
|
+ top: 50%;
|
|
|
+ left: 50%;
|
|
|
+ transform: translate(-50%, -50%);
|
|
|
+ border-radius: 50%;
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|