b7fa0a3d7e
对接 AuthCoreJS 通知枚举与 clazz_id;打卡与通知页使用 clazz 字段;补充 package-lock。 Made-with: Cursor
235 lines
6.7 KiB
Vue
235 lines
6.7 KiB
Vue
<template>
|
|
<div class="main">
|
|
<van-search v-model="state.keyword" />
|
|
<div class="checked-line" v-if="state.multiple" ref="checkedLine">
|
|
<van-tag type="primary" v-for="user in state.checked">{{user.real_name}}</van-tag>
|
|
</div>
|
|
<div class="list" :style="listStyle">
|
|
<van-pull-refresh v-model="state.refreshing" @refresh="onRefresh">
|
|
<van-list v-model:loading="state.loading"
|
|
:finished="state.finished" finished-text="没有更多数据了"
|
|
@load="onRefresh">
|
|
<van-cell-group inset>
|
|
<van-cell :title="item.real_name + (item.meta?.nickName ? ' (' + item.meta.nickName + ')' : '')"
|
|
v-for="item in dataset" :key="item.hty_id" @click="onClick(item)">
|
|
<template #icon >
|
|
<van-image v-if="item.meta?.avatarUrl" round class="avatar" :src="item.meta?.avatarUrl" />
|
|
<van-icon class="avatar" v-else size="25" name="https://b.yzcdn.cn/vant/icon-demo-1126.png" />
|
|
</template>
|
|
<template #right-icon>
|
|
<van-icon v-if="state.checked.some(x => x.user_id === item.hty_id)" size="25" name="checked" color="green" />
|
|
<van-icon v-else size="25" name="circle" />
|
|
</template>
|
|
</van-cell>
|
|
</van-cell-group>
|
|
</van-list>
|
|
</van-pull-refresh>
|
|
</div>
|
|
<div class="footer" v-if="state.multiple">
|
|
<van-button @click="$router.back()">返回</van-button>
|
|
<van-button type="primary" @click="onConfirm">确认</van-button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import { computed, defineComponent, reactive, onBeforeMount, watch, ref, nextTick } from 'vue';
|
|
import { Cell, Search, Toast, Icon, PullRefresh, List, Image, CellGroup, Tag, Button } from 'vant';
|
|
import { useRouter } from 'vue-router';
|
|
import useJihua from "~/store/jihua";
|
|
import useUser from "~/store/user";
|
|
import useUserGroup from '~/store/user-group';
|
|
import useClazz from "~/store/clazz";
|
|
import useDaka from "~/store/daka";
|
|
import {PickTargets, TeacherStudentStates, User} from '~/types'
|
|
|
|
|
|
export default defineComponent({
|
|
name: "student-index",
|
|
components: {
|
|
[Search.name]: Search,
|
|
[Icon.name]: Icon,
|
|
[Image.name]: Image,
|
|
[Cell.name]: Cell,
|
|
[CellGroup.name]: CellGroup,
|
|
[Toast.name]: Toast,
|
|
[PullRefresh.name]: PullRefresh,
|
|
[List.name]: List,
|
|
[Tag.name]: Tag,
|
|
[Button.name]: Button
|
|
},
|
|
props: ['params'],
|
|
setup({params}) {
|
|
|
|
const usingGroup = useUserGroup();
|
|
const usingClazz = useClazz();
|
|
const usingDaka = useDaka();
|
|
|
|
const state = reactive({
|
|
keyword: '',
|
|
loading: false,
|
|
refreshing: false,
|
|
finished: true,
|
|
checked: [],
|
|
multiple: !!params.multiple
|
|
});
|
|
const checkedLine = ref()
|
|
|
|
const usingJihua = useJihua();
|
|
|
|
const { store, getStudents } = useUser();
|
|
|
|
const router = useRouter();
|
|
|
|
const listStyle = ref({});
|
|
|
|
const dataset = computed(() => {
|
|
let list = store.students[TeacherStudentStates.Approved];
|
|
if (state.keyword) {
|
|
list = list.filter(x => x.real_name.includes(state.keyword))
|
|
}
|
|
return list;
|
|
})
|
|
|
|
|
|
|
|
const search = async () => {
|
|
state.loading = true;
|
|
await getStudents(TeacherStudentStates.Approved);
|
|
state.finished = true;
|
|
state.loading = false;
|
|
}
|
|
|
|
const onRefresh = () => {
|
|
state.refreshing = false;
|
|
search();
|
|
}
|
|
|
|
|
|
|
|
watch(() => state.checked.length, () => {
|
|
let el = checkedLine.value as unknown as HTMLDivElement;
|
|
if (!el) {
|
|
listStyle.value = {}
|
|
}
|
|
nextTick(() => {
|
|
listStyle.value = {height: `calc(100% - 1.08rem - 1.4rem - ${el.clientHeight}px)`};
|
|
})
|
|
})
|
|
|
|
onBeforeMount(async () => {
|
|
await search()
|
|
switch (params.target) {
|
|
case PickTargets.USER_GROUP:
|
|
state.checked = [...(usingGroup.store.current.users.vals || [])]
|
|
break;
|
|
case PickTargets.CLAZZ:
|
|
state.checked = [...(usingClazz.store.current.students?.val?.users?.vals || [])]
|
|
break;
|
|
case PickTargets.DAKA:
|
|
state.checked = [...(usingClazz.store.current.students?.val?.users?.vals || [])]
|
|
break;
|
|
}
|
|
})
|
|
|
|
const onClick = ({hty_id, real_name}: User) => {
|
|
if (state.multiple) {
|
|
if (state.checked.some(x => x.user_id === hty_id)) {
|
|
state.checked = state.checked.filter(x => x.user_id !== hty_id);
|
|
} else {
|
|
state.checked.push({user_id: hty_id, real_name});
|
|
}
|
|
} else {
|
|
usingJihua.store.current.student_id = hty_id;
|
|
usingJihua.store.current.student_name = real_name
|
|
router.back();
|
|
}
|
|
};
|
|
|
|
const onConfirm = () => {
|
|
let ids;
|
|
switch (params.target) {
|
|
case PickTargets.USER_GROUP:
|
|
usingGroup.store.current.users.vals = [...state.checked];
|
|
break;
|
|
case PickTargets.CLAZZ:
|
|
ids = new Set((usingClazz.store.current.students?.val.users.vals || []).map(x => x.user_id));
|
|
if (ids.size > 0) {
|
|
usingClazz.store.current.students.val.users.vals.push(...state.checked.filter(x => !ids.has(x.user_id)))
|
|
} else {
|
|
usingClazz.store.current.students = {val: {users: {vals: [...state.checked]}}}
|
|
}
|
|
break;
|
|
case PickTargets.DAKA:
|
|
ids = new Set((usingDaka.store.current.students?.val.users.vals || []).map(x => x.user_id));
|
|
if (ids.size > 0) {
|
|
usingDaka.store.current.students.val.users.vals.push(...state.checked.filter(x => !ids.has(x.user_id)))
|
|
} else {
|
|
usingDaka.store.current.students = {val: {users: {vals: [...state.checked]}}}
|
|
}
|
|
}
|
|
|
|
router.back();
|
|
}
|
|
|
|
return {
|
|
state, dataset, onClick, onRefresh, checkedLine, listStyle, onConfirm
|
|
};
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<style scoped lang="less">
|
|
.main {
|
|
width: 100%;
|
|
height: calc(100% - 1rem);
|
|
position: relative;
|
|
|
|
.checked-line {
|
|
padding: 0.08rem 0.28rem;
|
|
|
|
&:empty {
|
|
padding: 0;
|
|
}
|
|
|
|
:deep(.van-tag) {
|
|
margin: 0.04rem 0.06rem;
|
|
}
|
|
}
|
|
|
|
.list {
|
|
width: 100%;
|
|
height: calc(100% - 1.08rem - 1.4rem);
|
|
overflow-y: auto;
|
|
padding: 0.32rem 0;
|
|
|
|
&::-webkit-scrollbar {
|
|
display: none;
|
|
}
|
|
|
|
.avatar {
|
|
width: 0.5rem;
|
|
height: 0.5rem;
|
|
margin-right: 0.1rem;
|
|
}
|
|
|
|
:deep(.van-cell) {
|
|
align-items: center;
|
|
}
|
|
}
|
|
.footer {
|
|
width: 100%;
|
|
position: absolute;
|
|
bottom: 0.4rem;
|
|
padding: 0.32rem;
|
|
height: 1rem;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
|
|
:deep(.van-button) {
|
|
width: 45%;
|
|
}
|
|
}
|
|
}
|
|
</style>
|