|
@@ -1,8 +1,487 @@
|
|
|
-import { defineComponent } from "vue";
|
|
|
+import { computed, defineComponent, onMounted, reactive, ref } from 'vue';
|
|
|
+import styles from './index.module.less';
|
|
|
+import MSticky from '@/components/m-sticky';
|
|
|
+import MHeader from '@/components/m-header';
|
|
|
+import icon_detail_bg from '../images/icon_detail_bg.png';
|
|
|
+import icon_music from '@common/images/icon-music.png';
|
|
|
+import {
|
|
|
+ Button,
|
|
|
+ Cell,
|
|
|
+ CellGroup,
|
|
|
+ Checkbox,
|
|
|
+ CheckboxGroup,
|
|
|
+ Field,
|
|
|
+ Grid,
|
|
|
+ GridItem,
|
|
|
+ Image,
|
|
|
+ Picker,
|
|
|
+ Popup,
|
|
|
+ showToast
|
|
|
+} from 'vant';
|
|
|
+import MStudent from '../component/m-student';
|
|
|
+import { IMusicGroup, IStudentDetail } from '../type';
|
|
|
+import Assignment from '../component/Assignment';
|
|
|
+import Attendance from '../component/Attendance';
|
|
|
+import icon_pen from '@common/images/icon_pen.png';
|
|
|
+import icon_phone from '../images/icon-phone.png';
|
|
|
+import icon_message from '../images/icon-message.png';
|
|
|
+import SkeletionDetail from './skeletion-detail';
|
|
|
+import { useRoute } from 'vue-router';
|
|
|
+import {
|
|
|
+ api_organizationGetGradeList,
|
|
|
+ api_studentManageQuitMusicGroup,
|
|
|
+ api_studentManageUpdateGrade,
|
|
|
+ api_studentManageUserDetail,
|
|
|
+ api_studentManageUserMusicGroup
|
|
|
+} from '../api';
|
|
|
|
|
|
export default defineComponent({
|
|
|
- name: 'student-manage-detail',
|
|
|
- setup(){
|
|
|
- return () => <div></div>
|
|
|
- }
|
|
|
-})
|
|
|
+ name: 'student-manage-detail',
|
|
|
+ setup() {
|
|
|
+ const route = useRoute();
|
|
|
+ const studentId: string | undefined =
|
|
|
+ route.query?.studentId?.toString() || '';
|
|
|
+ const musicGroupIds: string[] =
|
|
|
+ route.query.musicGroupIds?.toString()?.split(',') || [];
|
|
|
+ const detailData = reactive({
|
|
|
+ skelet: true,
|
|
|
+ /** 加载 */
|
|
|
+ loading: false,
|
|
|
+ /** 乐团 */
|
|
|
+ groupShow: false,
|
|
|
+ /** 退团 */
|
|
|
+ quitShow: false,
|
|
|
+ /** 确定退团 */
|
|
|
+ quitConfirmShow: false,
|
|
|
+ /** 联系方式 */
|
|
|
+ cancelShow: false,
|
|
|
+ /** 年级 */
|
|
|
+ gradeShow: false,
|
|
|
+ gradeOptions: [[], []] as any,
|
|
|
+ musicGroup: [] as IMusicGroup[],
|
|
|
+ musicGroupTitle: '全部乐团',
|
|
|
+ musicGroupId: musicGroupIds[0] || '',
|
|
|
+ student: {} as IStudentDetail,
|
|
|
+ /** 年级列表 */
|
|
|
+ gradeList: null as any,
|
|
|
+ /** 退团列表 */
|
|
|
+ quitList: [] as any[],
|
|
|
+ /** 退团原因 */
|
|
|
+ reason: '',
|
|
|
+ quitLoading: false
|
|
|
+ });
|
|
|
+ const checkboxRefs = ref<any[]>([]);
|
|
|
+ /** 获取学生乐团 */
|
|
|
+ const getMusicGroup = () => {
|
|
|
+ api_studentManageUserMusicGroup(studentId).then(res => {
|
|
|
+ if (Array.isArray(res.data)) {
|
|
|
+ detailData.musicGroup = res.data.map((item: any) => {
|
|
|
+ return {
|
|
|
+ text: item.name,
|
|
|
+ value: item.id,
|
|
|
+ gradeType: item.gradeType
|
|
|
+ };
|
|
|
+ });
|
|
|
+ if (detailData.musicGroup.length === 1) {
|
|
|
+ detailData.musicGroupTitle = detailData.musicGroup[0].text;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ /** 获取年级分布 */
|
|
|
+ const getGradeList = () => {
|
|
|
+ if (detailData.student.organId && detailData.musicGroup.length) {
|
|
|
+ if (detailData.gradeList) return;
|
|
|
+ console.log(detailData.musicGroup);
|
|
|
+ const gradeType = Array.from(
|
|
|
+ new Set(detailData.musicGroup.map(group => group.gradeType))
|
|
|
+ ).join(',');
|
|
|
+ console.log('🚀 ~ gradeType:', gradeType);
|
|
|
+ api_organizationGetGradeList(
|
|
|
+ detailData.student.organId,
|
|
|
+ gradeType
|
|
|
+ ).then(res => {
|
|
|
+ detailData.gradeList = res.data;
|
|
|
+ detailData.gradeOptions[0] = Object.entries(res.data).map(
|
|
|
+ (value: any) => {
|
|
|
+ return {
|
|
|
+ text: value[1],
|
|
|
+ value: value[0]
|
|
|
+ };
|
|
|
+ }
|
|
|
+ );
|
|
|
+ detailData.gradeOptions[1] = new Array(30)
|
|
|
+ .fill(1)
|
|
|
+ .map((_, index: number) => ({
|
|
|
+ text: `${index + 1}班`,
|
|
|
+ value: `${index + 1}班`
|
|
|
+ }));
|
|
|
+ });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ setTimeout(() => {
|
|
|
+ getGradeList();
|
|
|
+ }, 30);
|
|
|
+ };
|
|
|
+
|
|
|
+ const getDatail = () => {
|
|
|
+ detailData.loading = true;
|
|
|
+
|
|
|
+ api_studentManageUserDetail({
|
|
|
+ studentId: studentId,
|
|
|
+ musicGroupId: detailData.musicGroupId || ''
|
|
|
+ })
|
|
|
+ .then(res => {
|
|
|
+ if (res.data) {
|
|
|
+ if (res.data.phone) {
|
|
|
+ res.data.phone =
|
|
|
+ res.data.phone.slice(0, 3) + '****' + res.data.phone.slice(-4);
|
|
|
+ }
|
|
|
+ detailData.student = res.data;
|
|
|
+ getGradeList();
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .finally(() => {
|
|
|
+ setTimeout(() => {
|
|
|
+ detailData.loading = false;
|
|
|
+ detailData.skelet = false;
|
|
|
+ }, 500);
|
|
|
+ });
|
|
|
+ };
|
|
|
+ onMounted(() => {
|
|
|
+ getMusicGroup();
|
|
|
+ getDatail();
|
|
|
+ });
|
|
|
+
|
|
|
+ const quitName = computed(() => {
|
|
|
+ const text = detailData.musicGroup
|
|
|
+ .filter(group => detailData.quitList.includes(group.value))
|
|
|
+ .map(group => {
|
|
|
+ return '“' + group.text + '”';
|
|
|
+ })
|
|
|
+ .join('、');
|
|
|
+ return `${detailData.student.studentName}从${text}`;
|
|
|
+ });
|
|
|
+
|
|
|
+ /** 设置学生班级 */
|
|
|
+ const handleSetGrade = async (selectedOptions: any[]) => {
|
|
|
+ const res = await api_studentManageUpdateGrade({
|
|
|
+ currentClass: selectedOptions[1].value, // 班级
|
|
|
+ currentGrade: selectedOptions[0].text, // 年级
|
|
|
+ currentGradeNum: selectedOptions[0].value, // 年级(数字表示)
|
|
|
+ musicGroupId: detailData.musicGroupId, // 乐团ID
|
|
|
+ studentId: detailData.student.studentId // 学生ID
|
|
|
+ });
|
|
|
+ console.log(res);
|
|
|
+ if (res.code === 200) {
|
|
|
+ showToast('修改成功');
|
|
|
+ }
|
|
|
+ getDatail();
|
|
|
+ };
|
|
|
+
|
|
|
+ /** 退团 */
|
|
|
+ const handleQuite = async () => {
|
|
|
+ if (!detailData.reason) {
|
|
|
+ showToast('请填写退团原因');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ detailData.quitLoading = true;
|
|
|
+ try {
|
|
|
+ const res = await api_studentManageQuitMusicGroup({
|
|
|
+ musicGroupId: detailData.quitList.join(','),
|
|
|
+ reason: detailData.reason,
|
|
|
+ reasonEnum: 'OTHER',
|
|
|
+ userId: detailData.student.studentId
|
|
|
+ });
|
|
|
+ detailData.quitConfirmShow = false;
|
|
|
+ if (res.code === 200) {
|
|
|
+ detailData.quitList = [];
|
|
|
+ getDatail();
|
|
|
+ }
|
|
|
+ } catch (error) {}
|
|
|
+ detailData.quitLoading = false;
|
|
|
+ };
|
|
|
+
|
|
|
+ /** 去聊天 */
|
|
|
+ const openIm = () => {
|
|
|
+ postMessage({
|
|
|
+ api: 'joinChatGroup',
|
|
|
+ content: {
|
|
|
+ type: 'single', // single 单人 multi 多人
|
|
|
+ id: detailData.student.studentId
|
|
|
+ }
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ /** 打电话 */
|
|
|
+ const hanldeCallPhone = () => {
|
|
|
+ postMessage({
|
|
|
+ api: 'callPhone',
|
|
|
+ content: {
|
|
|
+ phone: detailData.student.phone
|
|
|
+ }
|
|
|
+ });
|
|
|
+ };
|
|
|
+ return () => (
|
|
|
+ <div class={styles.studentDetail}>
|
|
|
+ <Image class={styles.bg} src={icon_detail_bg} />
|
|
|
+ <MSticky position="top">
|
|
|
+ <MHeader background="transparent" />
|
|
|
+ </MSticky>
|
|
|
+
|
|
|
+ <SkeletionDetail loading={detailData.skelet}>
|
|
|
+ <Cell
|
|
|
+ class={styles.musicGroup}
|
|
|
+ title={detailData.musicGroupTitle}
|
|
|
+ isLink={detailData.musicGroup.length > 1 ? true : false}
|
|
|
+ clickable={detailData.musicGroup.length > 1 ? true : false}
|
|
|
+ center
|
|
|
+ border={false}
|
|
|
+ onClick={() => {
|
|
|
+ if (detailData.musicGroup.length < 2) return;
|
|
|
+ detailData.groupShow = true;
|
|
|
+ }}>
|
|
|
+ {{
|
|
|
+ icon: () => <Image class={styles.iconMusic} src={icon_music} />
|
|
|
+ }}
|
|
|
+ </Cell>
|
|
|
+
|
|
|
+ <div class={styles.box}>
|
|
|
+ <MStudent
|
|
|
+ item={detailData.student}
|
|
|
+ valueType={
|
|
|
+ detailData.student.inGroupStatus === 'OUT'
|
|
|
+ ? 'statued'
|
|
|
+ : detailData.student.inGroupStatus === 'APPLY_OUT'
|
|
|
+ ? 'statuing'
|
|
|
+ : 'status'
|
|
|
+ }
|
|
|
+ isLink={false}
|
|
|
+ onQuit={() => (detailData.quitShow = true)}
|
|
|
+ onContact={() => (detailData.cancelShow = true)}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class={styles.infobox}>
|
|
|
+ <div class={styles.attendanceTitle}>
|
|
|
+ <span>基本信息</span>
|
|
|
+ </div>
|
|
|
+ <div class={styles.infoItem}>
|
|
|
+ <div>性别</div>
|
|
|
+ <div>{detailData.student.gender ? '男' : '女'}</div>
|
|
|
+ </div>
|
|
|
+ <div class={styles.infoItem}>
|
|
|
+ <div>联系电话</div>
|
|
|
+ <div>{detailData.student.phone}</div>
|
|
|
+ </div>
|
|
|
+ <div class={styles.infoItem}>
|
|
|
+ <div>年级</div>
|
|
|
+ <div
|
|
|
+ class={styles.edit}
|
|
|
+ onClick={() => {
|
|
|
+ if (detailData.student.inGroupStatus === 'OUT') return;
|
|
|
+ detailData.gradeShow = true;
|
|
|
+ }}>
|
|
|
+ {detailData.student.currentGrade}年级
|
|
|
+ {detailData.student.currentClass}
|
|
|
+ {detailData.student.inGroupStatus !== 'OUT' && (
|
|
|
+ <Image class={styles.iconPen} src={icon_pen} />
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class={styles.infoItem}>
|
|
|
+ <div>艺术实践</div>
|
|
|
+ <div>{detailData.student.artPracticeCount}次</div>
|
|
|
+ </div>
|
|
|
+ {detailData.student.quitTime && (
|
|
|
+ <div class={styles.infoItem}>
|
|
|
+ <div>退团时间</div>
|
|
|
+ <div style={{ color: '#FF5A56' }}>
|
|
|
+ {detailData.student.quitTime}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class={styles.box}>
|
|
|
+ <Assignment item={detailData.student} />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class={styles.box}>
|
|
|
+ <Attendance item={detailData.student} />
|
|
|
+ </div>
|
|
|
+ </SkeletionDetail>
|
|
|
+
|
|
|
+ {/* 切换乐团 */}
|
|
|
+ <Popup v-model:show={detailData.groupShow} position="bottom" round>
|
|
|
+ <Picker
|
|
|
+ visibleOptionNum={5}
|
|
|
+ columns={detailData.musicGroup}
|
|
|
+ onCancel={() => (detailData.groupShow = false)}
|
|
|
+ onConfirm={value => {
|
|
|
+ const option = value.selectedOptions[0];
|
|
|
+ const oldGroupId = detailData.musicGroupId;
|
|
|
+ detailData.musicGroupId = option.value;
|
|
|
+ detailData.musicGroupTitle = option.text;
|
|
|
+ detailData.groupShow = false;
|
|
|
+ if (oldGroupId != option.value) {
|
|
|
+ getDatail();
|
|
|
+ }
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ </Popup>
|
|
|
+
|
|
|
+ {/* 联系方式 */}
|
|
|
+ <Popup
|
|
|
+ v-model:show={detailData.cancelShow}
|
|
|
+ position="bottom"
|
|
|
+ round
|
|
|
+ closeable>
|
|
|
+ <div class={styles.concatBox}>
|
|
|
+ <div class={styles.concatTitle}>联系方式</div>
|
|
|
+ <div class={styles.concatContent}>
|
|
|
+ <Grid columnNum={2} border={false} center>
|
|
|
+ <GridItem text="发送消息" onClick={openIm}>
|
|
|
+ {{
|
|
|
+ icon: () => (
|
|
|
+ <Image class={styles.concatIcon} src={icon_message} />
|
|
|
+ )
|
|
|
+ }}
|
|
|
+ </GridItem>
|
|
|
+ <GridItem text="拨打电话" onClick={hanldeCallPhone}>
|
|
|
+ {{
|
|
|
+ icon: () => (
|
|
|
+ <Image class={styles.concatIcon} src={icon_phone} />
|
|
|
+ )
|
|
|
+ }}
|
|
|
+ </GridItem>
|
|
|
+ </Grid>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </Popup>
|
|
|
+
|
|
|
+ {/* 设置班级 */}
|
|
|
+ <Popup v-model:show={detailData.gradeShow} position="bottom" round>
|
|
|
+ <Picker
|
|
|
+ visibleOptionNum={5}
|
|
|
+ columns={detailData.gradeOptions}
|
|
|
+ onCancel={() => (detailData.gradeShow = false)}
|
|
|
+ onConfirm={value => {
|
|
|
+ detailData.gradeShow = false;
|
|
|
+ handleSetGrade(value.selectedOptions);
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ </Popup>
|
|
|
+
|
|
|
+ {/* 选择退团列表 */}
|
|
|
+ <Popup
|
|
|
+ v-model:show={detailData.quitShow}
|
|
|
+ class={['popup-custom', 'van-scale']}
|
|
|
+ transition="van-scale">
|
|
|
+ <div class={styles.quitBox}>
|
|
|
+ <div class={styles.quitTitle}>选择乐团</div>
|
|
|
+ <div class={styles.quitDes}>请选择要退出的乐团:</div>
|
|
|
+ <CheckboxGroup
|
|
|
+ v-model:modelValue={detailData.quitList}
|
|
|
+ class={styles.optionBox}>
|
|
|
+ <CellGroup border={false}>
|
|
|
+ {detailData.musicGroup.map(
|
|
|
+ (group: IMusicGroup, index: number) => {
|
|
|
+ return (
|
|
|
+ <Cell
|
|
|
+ class={[
|
|
|
+ detailData.quitList.includes(group.value) &&
|
|
|
+ styles.cellActive
|
|
|
+ ]}
|
|
|
+ title={group.text}
|
|
|
+ center
|
|
|
+ border={false}
|
|
|
+ onClick={() => {
|
|
|
+ checkboxRefs.value[index]?.toggle();
|
|
|
+ }}>
|
|
|
+ {{
|
|
|
+ value: () => (
|
|
|
+ <Checkbox
|
|
|
+ ref={el => (checkboxRefs.value[index] = el)}
|
|
|
+ shape="square"
|
|
|
+ name={group.value}
|
|
|
+ onClick={(e: Event) =>
|
|
|
+ e.stopPropagation()
|
|
|
+ }></Checkbox>
|
|
|
+ )
|
|
|
+ }}
|
|
|
+ </Cell>
|
|
|
+ );
|
|
|
+ }
|
|
|
+ )}
|
|
|
+ </CellGroup>
|
|
|
+ </CheckboxGroup>
|
|
|
+ <div class={['btnGroupPopup']}>
|
|
|
+ <Button round onClick={() => (detailData.quitShow = false)}>
|
|
|
+ 取消
|
|
|
+ </Button>
|
|
|
+ <Button
|
|
|
+ type="primary"
|
|
|
+ round
|
|
|
+ disabled={!detailData.quitList.length}
|
|
|
+ onClick={() => {
|
|
|
+ // detailData.quitShow = false
|
|
|
+ detailData.quitConfirmShow = true;
|
|
|
+ }}>
|
|
|
+ 下一步
|
|
|
+ </Button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </Popup>
|
|
|
+
|
|
|
+ {/* 确定退团 */}
|
|
|
+ <Popup
|
|
|
+ v-model:show={detailData.quitConfirmShow}
|
|
|
+ class={['popup-custom', 'van-scale']}
|
|
|
+ transition="van-scale">
|
|
|
+ <div class={styles.quitBox}>
|
|
|
+ <div class={styles.quitTitle}>学员退团</div>
|
|
|
+ <div class={styles.quitDes}>
|
|
|
+ 确认要将学员
|
|
|
+ <span style={{ color: '#FF5A56' }}>{quitName.value}</span>
|
|
|
+ 中退团吗?
|
|
|
+ </div>
|
|
|
+ <div style={{ color: '#333' }} class={styles.quitLabel}>
|
|
|
+ <span style={{ color: '#FF5A56' }}>*</span>退团原因:
|
|
|
+ </div>
|
|
|
+ <div class={styles.quitLabel}>
|
|
|
+ <Field
|
|
|
+ style={{ padding: 0 }}
|
|
|
+ v-model={detailData.reason}
|
|
|
+ type="textarea"
|
|
|
+ rows={3}
|
|
|
+ required
|
|
|
+ placeholder="请填写退团原因"></Field>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class={styles.quitLabel}>
|
|
|
+ 确认后,我们将在7个工作日内与学生联系退费事宜
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class={['btnGroupPopup']}>
|
|
|
+ <Button
|
|
|
+ round
|
|
|
+ onClick={() => (detailData.quitConfirmShow = false)}>
|
|
|
+ 取消
|
|
|
+ </Button>
|
|
|
+ <Button
|
|
|
+ loading={detailData.quitLoading}
|
|
|
+ type="primary"
|
|
|
+ round
|
|
|
+ onClick={() => handleQuite()}>
|
|
|
+ 确定
|
|
|
+ </Button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </Popup>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+ }
|
|
|
+});
|