准备调整通讯录

This commit is contained in:
ywyonui 2025-07-08 22:20:22 +08:00
parent a8a0edc5d4
commit 4caeb1d349
5 changed files with 306 additions and 139 deletions

View File

@ -108,3 +108,19 @@ export const xsQjSpApi = async (params: any) => {
return await post("/api/xsQj/sp", params); return await post("/api/xsQj/sp", params);
}; };
// 获取所有班级
export const bjFindAllApi = async (params: any) => {
return await get("/api/bj/findAll", params);
};
// 获取学生列表
export const xsFindList = async (params: any) => {
return await get("/api/xs/findPage", params);
};
// 获取学生家长列表
export const xsJzListByXsIdApi = async (params: any) => {
return await get("/api/jz/getListByXsId", params);
};

View File

@ -0,0 +1,93 @@
<template>
<picker mode="multiSelector" :range="njBjRange" :value="curIndex" @change="onMultiChange"
@columnchange="onColumnChange">
<view class="picker-item">
<text>{{ curNjBjLabel || "选择班级" }}</text>
<uni-icons type="bottom" size="14" color="#666"></uni-icons>
</view>
</picker>
</template>
<script lang="ts" setup>
import { findAllNjBjTreeApi } from "@/api/base/server";
import { on } from "events";
//
const props = withDefaults(defineProps<{
defaultValue: any
}>(), {
defaultValue: []
});
// emit
const emit = defineEmits(['change'])
const bjList = ref<any>([]);
const njBjRange = ref<any>([[], []]);
const curIndex = ref([0, 0]);
const curNjBjLabel = ref("");
const rebuildNjBjList = (njIndex: number) => {
const bjRange = bjList.value[njIndex].children.map((bj: any) => bj.title);
njBjRange.value[1] = bjRange;
}
const onChange = (index: any) => {
curIndex.value = index;
if (index.length === 2 && index[0] >= 0 && index[1] >= 0) {
curNjBjLabel.value = njBjRange.value[0][index[0]] + " " + njBjRange.value[1][index[1]];
emit("change", bjList.value[index[0]], bjList.value[index[0]].children[index[1]]);
} else {
curNjBjLabel.value = "";
emit("change", null, null);
}
}
const onMultiChange = (e: any) => {
const index = e.detail.value;
onChange(index);
}
const onColumnChange = (e: any) => {
const column = e.detail.column;
const index = e.detail.value;
if (column == 0) {
rebuildNjBjList(index);
}
}
onMounted(async () => {
const res = await findAllNjBjTreeApi();
bjList.value = res.result;
const njRange = bjList.value.map((nj: any) => nj.title);
const bjRange = bjList.value[0].children.map((bj: any) => bj.title);
njBjRange.value = [njRange, bjRange];
onChange([0, 0]);
});
if (props.defaultValue && props.defaultValue.length > 0) {
curIndex.value = props.defaultValue;
}
</script>
<style scoped lang="scss">
.picker-item {
display: flex;
align-items: center;
padding: 7px 15px;
background-color: #f7f7f7;
border: 1px solid #eee;
border-radius: 16px;
font-size: 14px;
color: #333;
justify-content: space-between;
text {
margin-right: 8px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
</style>

View File

@ -3,44 +3,44 @@
<view class="address-book-page"> <view class="address-book-page">
<!-- 1. 班级选择器 人数显示 --> <!-- 1. 班级选择器 人数显示 -->
<view class="class-selector"> <view class="class-selector">
<picker <view class="bj-picker">
mode="selector" <NjBjPicker @change="changeNjBj" />
:range="combinedClassRange"
:value="selectedCombinedClassIndex"
@change="onCombinedClassChange"
>
<view class="picker-item">
<text>{{ selectedCombinedClassName || "选择班级" }}</text>
<uni-icons type="bottom" size="14" color="#666"></uni-icons>
</view> </view>
</picker> <!-- 添加一个搜索框搜索学生姓名 -->
<view class="search-section">
<view class="search-box">
<uni-icons type="search" size="18" color="#999"></uni-icons>
<input class="search-input" type="text" placeholder="搜索学生姓名..." v-model="searchKeyword" @input="onSearchInput"
@confirm="onSearchConfirm" />
<view class="search-clear" v-if="searchKeyword" @click="clearSearch">
<uni-icons type="clear" size="16" color="#999"></uni-icons>
</view>
</view>
</view>
<text class="student-count" v-if="!isLoading && studentList.length > 0"> {{ studentList.length }} </text> <text class="student-count" v-if="!isLoading && studentList.length > 0"> {{ studentList.length }} </text>
</view> </view>
<!-- 2. 学生列表 --> <!-- 2. 学生列表 -->
<scroll-view scroll-y class="student-list-container"> <scroll-view scroll-y class="student-list-container">
<view v-if="isLoading" class="loading-indicator">加载中...</view> <view v-if="isLoading" class="loading-indicator">加载中...</view>
<template v-else-if="studentList.length > 0"> <template v-else-if="xsList.length > 0">
<view <view v-for="xs in xsList" :key="xs.id" class="student-item" @click="goToDetail(xs.id)">
v-for="student in studentList"
:key="student.id"
class="student-item"
@click="goToDetail(student.id)"
>
<view class="student-info"> <view class="student-info">
<image class="avatar" :src="student.avatar" mode="aspectFill"></image> <image class="avatar" :src="imagUrl(xs.avatar)" mode="aspectFill"></image>
<view class="details"> <view class="details">
<view class="name-role"> <view class="name-role">
<text class="name">{{ student.name }}</text> <text class="name">{{ xs.name }}</text>
<text v-if="student.role" class="role-tag">{{ student.role }}</text> <text v-if="xs.role" class="role-tag">{{ xs.role }}</text>
</view> </view>
<view class="gender"> <view class="gender">
<uni-icons :type="student.gender === '女' ? 'person-filled' : 'person'" size="14" :color="student.gender === '女' ? '#ff5a5f' : '#007aff'"></uni-icons> <uni-icons :type="xs.gender === '女' ? 'person-filled' : 'person'" size="14"
<text>{{ student.gender }}</text> :color="xs.gender === '女' ? '#ff5a5f' : '#007aff'"></uni-icons>
<text>{{ xs.gender }}</text>
</view> </view>
</view> </view>
</view> </view>
<button class="contact-button" @click.stop="contactParent(student.id)">联系家长</button> <button class="contact-button" @click.stop="contactParent(xs.id)">联系家长</button>
</view> </view>
</template> </template>
<view v-else class="empty-state">暂无学生数据</view> <view v-else class="empty-state">暂无学生数据</view>
@ -50,7 +50,17 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import NjBjPicker from "@/pages/components/NjBjPicker/index.vue";
import { ref, computed, onMounted } from 'vue'; import { ref, computed, onMounted } from 'vue';
import { imagUrl } from "@/utils";
import { xsFindList } from "@/api/base/server";
const xsList = ref<any>([]);
//
const changeNjBj = (nj: string, bj: string) => {
console.log(nj, bj);
};
// --- --- // --- ---
interface CombinedClass { interface CombinedClass {
@ -59,7 +69,6 @@ interface CombinedClass {
} }
const combinedClassList = ref<CombinedClass[]>([]); const combinedClassList = ref<CombinedClass[]>([]);
const selectedCombinedClassId = ref<string>(''); const selectedCombinedClassId = ref<string>('');
const selectedCombinedClassIndex = ref(-1);
const isLoading = ref(false); const isLoading = ref(false);
const combinedClassRange = computed(() => combinedClassList.value.map(c => c.name)); const combinedClassRange = computed(() => combinedClassList.value.map(c => c.name));
@ -68,6 +77,22 @@ const selectedCombinedClassName = computed(() => {
return cls ? cls.name : ''; return cls ? cls.name : '';
}); });
// --- Search State ---
const searchKeyword = ref<string>('');
const onSearchInput = () => {
fetchStudentList();
};
const onSearchConfirm = () => {
fetchStudentList();
};
const clearSearch = () => {
searchKeyword.value = '';
fetchStudentList();
};
// //
const fetchCombinedClassList = async () => { const fetchCombinedClassList = async () => {
console.log("Fetching combined class list..."); console.log("Fetching combined class list...");
@ -84,7 +109,7 @@ const fetchCombinedClassList = async () => {
// //
if (combinedClassList.value.length > 0 && !selectedCombinedClassId.value) { if (combinedClassList.value.length > 0 && !selectedCombinedClassId.value) {
selectedCombinedClassId.value = combinedClassList.value[0].id; selectedCombinedClassId.value = combinedClassList.value[0].id;
selectedCombinedClassIndex.value = 0; // curBjIndex.value = 0;
} }
isLoading.value = false; // isLoading.value = false; //
}; };
@ -92,7 +117,7 @@ const fetchCombinedClassList = async () => {
// //
const onCombinedClassChange = (e: any) => { const onCombinedClassChange = (e: any) => {
const index = parseInt(e.detail.value); const index = parseInt(e.detail.value);
selectedCombinedClassIndex.value = index; // curBjIndex.value = index;
const selectedClass = combinedClassList.value[index]; const selectedClass = combinedClassList.value[index];
if (selectedClass && selectedClass.id !== selectedCombinedClassId.value) { if (selectedClass && selectedClass.id !== selectedCombinedClassId.value) {
selectedCombinedClassId.value = selectedClass.id; selectedCombinedClassId.value = selectedClass.id;
@ -192,30 +217,61 @@ onMounted(async () => {
border-bottom: 1px solid #eee; border-bottom: 1px solid #eee;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
.picker-item {
display: flex;
align-items: center;
padding: 5px 15px;
background-color: #f7f7f7;
border: 1px solid #eee;
border-radius: 16px;
font-size: 14px;
color: #333;
min-width: 160px;
justify-content: space-between;
text {
margin-right: 8px;
}
}
.student-count { .student-count {
font-size: 14px; font-size: 14px;
color: #666; color: #666;
white-space: nowrap; // white-space: nowrap; //
} }
.bj-picker {
flex: 1 0 1px;
max-width: 45%;
}
.search-section {
flex: 1 0 1px;
}
} }
.search-section {
padding: 0rpx 15rpx;
background-color: #ffffff;
position: sticky;
top: 0;
z-index: 10;
.search-box {
display: flex;
align-items: center;
background-color: #f5f5f5;
border-radius: 50rpx;
padding: 0 20rpx;
height: 70rpx;
uni-icons {
margin-right: 15rpx;
}
.search-input {
flex: 1;
height: 100%;
border: none;
background: transparent;
font-size: 28rpx;
color: #333;
&::placeholder {
color: #999;
}
}
.search-clear {
margin-left: 15rpx;
cursor: pointer;
}
}
}
.student-list-container { .student-list-container {
flex: 1; flex: 1;
@ -298,6 +354,7 @@ onMounted(async () => {
uni-icons { uni-icons {
margin-right: 3px; margin-right: 3px;
} }
text { text {
line-height: 1; // line-height: 1; //
} }
@ -319,6 +376,7 @@ onMounted(async () => {
&::after { &::after {
border: none; border: none;
} }
// //
&:active { &:active {
background-color: #f0f0f0; background-color: #f0f0f0;

View File

@ -28,7 +28,7 @@ import { useForm } from "@/components/BasicForm/hooks/useForm";
import { dicApi } from "@/api/system/dic"; import { dicApi } from "@/api/system/dic";
import { useDataStore } from "@/store/modules/data"; import { useDataStore } from "@/store/modules/data";
import { import {
findAllNjBjTree, findAllNjBjTreeApi,
findAllXxXqNjTree, findAllXxXqNjTree,
kmFindAllApi, kmFindAllApi,
} from "@/api/base/server"; } from "@/api/base/server";
@ -82,7 +82,7 @@ const [register, { getValue, setValue }] = useForm({
// label: "", // label: "",
// component: "BasicPicker", // component: "BasicPicker",
// componentProps: { // componentProps: {
// api: findAllNjBjTree, // api: findAllNjBjTreeApi,
// rangeKey: "title", // rangeKey: "title",
// savaKey: "key", // savaKey: "key",
// }, // },
@ -102,7 +102,7 @@ const [register, { getValue, setValue }] = useForm({
// label: "", // label: "",
// component: "BasicPicker", // component: "BasicPicker",
// componentProps: { // componentProps: {
// api: findAllNjBjTree, // api: findAllNjBjTreeApi,
// rangeKey: "title", // rangeKey: "title",
// savaKey: "key", // savaKey: "key",
// }, // },
@ -122,7 +122,7 @@ const [register, { getValue, setValue }] = useForm({
// label: "", // label: "",
// component: "BasicTree", // component: "BasicTree",
// componentProps: { // componentProps: {
// api: findAllNjBjTree, // api: findAllNjBjTreeApi,
// rangeKey: "title", // rangeKey: "title",
// savaKey: "key", // savaKey: "key",
// }, // },

View File

@ -223,7 +223,7 @@ import CustomUpload from "/src/components/BasicUpload/CustomUpload.vue";
import BasicTree from "@/components/BasicTree/Tree.vue"; import BasicTree from "@/components/BasicTree/Tree.vue";
import { attachmentUpload } from "@/api/system/upload"; import { attachmentUpload } from "@/api/system/upload";
import { imagUrl } from "@/utils"; import { imagUrl } from "@/utils";
import { findAllNjBjTree, mobilejlstudentListApi } from "@/api/base/server"; import { findAllNjBjTreeApi, mobilejlstudentListApi } from "@/api/base/server";
interface Attachment { interface Attachment {
name: string; name: string;
@ -286,7 +286,7 @@ const displayNames = computed(() => {
// //
const loadTreeData = async () => { const loadTreeData = async () => {
try { try {
const res = await findAllNjBjTree(); const res = await findAllNjBjTreeApi();
if (res.resultCode === 1 && res.result) { if (res.resultCode === 1 && res.result) {
// BasicTree // BasicTree
treeData.value = res.result.map((item: any) => ({ treeData.value = res.result.map((item: any) => ({