一日常规
This commit is contained in:
parent
b8015b7a01
commit
43ea733eac
@ -46,7 +46,7 @@ export const gradeClassFindByGradeApi = async (params: any) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const cmsArticleFindPageApi = async (params: any) => {
|
export const cmsArticleFindPageApi = async (params: any) => {
|
||||||
return await get("/api/cmsArticle/findPage", params);
|
return await get("/api/cms/article/findPage", params);
|
||||||
};
|
};
|
||||||
//根据id查询部门
|
//根据id查询部门
|
||||||
export const deptFindAllGradeClassApi = async (param: { pid: number }) => {
|
export const deptFindAllGradeClassApi = async (param: { pid: number }) => {
|
||||||
|
|||||||
34
src/api/base/student.ts
Normal file
34
src/api/base/student.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { get, post } from "@/utils/request";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据年级ID和班级ID查询学生及家长信息
|
||||||
|
*/
|
||||||
|
export const findStudentInfoByNjAndBjApi = async (params: {
|
||||||
|
njId?: string,
|
||||||
|
bjIds?: string[]
|
||||||
|
}) => {
|
||||||
|
return await post("/api/xs/findByNjAndBj", params);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据学生ID列表查询学生及家长信息
|
||||||
|
*/
|
||||||
|
export const findStudentInfoByIdsApi = async (params: {
|
||||||
|
xsIdList?: string
|
||||||
|
}) => {
|
||||||
|
return await post("/api/xs/findStudentInfoWithParentIds", params);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据学生身份证号查询学生信息
|
||||||
|
*/
|
||||||
|
export const findStudentBySfzhApi = async (sfzh: string) => {
|
||||||
|
return await get("/api/xs/findXsBySfzh", { sfzh });
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据学生ID查询学生详情
|
||||||
|
*/
|
||||||
|
export const getStudentDetailByIdApi = async (id: string) => {
|
||||||
|
return await get("/api/xs/getDetailById", { id });
|
||||||
|
};
|
||||||
@ -11,7 +11,7 @@
|
|||||||
<FormsItem v-bind="item">
|
<FormsItem v-bind="item">
|
||||||
<template v-if="item.component && !item.slot">
|
<template v-if="item.component && !item.slot">
|
||||||
<BasicComponent
|
<BasicComponent
|
||||||
v-bind="Object.assign({ ...attrs }, item, props.formsProps)"
|
v-bind="Object.assign({ ...attrs }, item, item.componentProps, props.formsProps)"
|
||||||
v-model="formModel.model[item.field]"
|
v-model="formModel.model[item.field]"
|
||||||
:model="formModel.model"
|
:model="formModel.model"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -16,11 +16,15 @@
|
|||||||
<FormBasicSearchList v-bind="attrs" v-if="isShow('BasicSearchList')" v-model="newValue"/>
|
<FormBasicSearchList v-bind="attrs" v-if="isShow('BasicSearchList')" v-model="newValue"/>
|
||||||
<FormBasicDateTimes v-bind="attrs" v-if="isShow('BasicDateTimes')" v-model="newValue"/>
|
<FormBasicDateTimes v-bind="attrs" v-if="isShow('BasicDateTimes')" v-model="newValue"/>
|
||||||
<FormBasicTree v-bind="attrs" v-if="isShow('BasicTree')" v-model="newValue"/>
|
<FormBasicTree v-bind="attrs" v-if="isShow('BasicTree')" v-model="newValue"/>
|
||||||
|
<FormBasicNjBjPicker v-bind="attrs" v-if="isShow('BasicNjBjPicker')" v-model="newValue"/>
|
||||||
|
<FormBasicXsPicker v-bind="attrs" v-if="isShow('BasicXsPicker')" v-model="newValue"/>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {useAttrs} from "vue";
|
import {useAttrs} from "vue";
|
||||||
|
import FormBasicNjBjPicker from "@/components/BasicNjBjPicker/index.vue";
|
||||||
|
import FormBasicXsPicker from "@/components/BasicXsPicker/index.vue";
|
||||||
|
|
||||||
const attrs = useAttrs()
|
const attrs = useAttrs()
|
||||||
|
|
||||||
|
|||||||
3
src/components/BasicForm/type/useForm.d.ts
vendored
3
src/components/BasicForm/type/useForm.d.ts
vendored
@ -28,6 +28,8 @@ type Component =
|
|||||||
| 'BasicSearchList'
|
| 'BasicSearchList'
|
||||||
| 'BasicDateTimes'
|
| 'BasicDateTimes'
|
||||||
| 'BasicTree'
|
| 'BasicTree'
|
||||||
|
| 'BasicNjBjPicker'
|
||||||
|
| 'BasicXsPicker'
|
||||||
|
|
||||||
interface FormsSchema {
|
interface FormsSchema {
|
||||||
field?: string,
|
field?: string,
|
||||||
@ -52,6 +54,7 @@ interface Methods {
|
|||||||
setValue: (e: any) => void,
|
setValue: (e: any) => void,
|
||||||
setDisabled: (e: boolean) => void,
|
setDisabled: (e: boolean) => void,
|
||||||
closeModel: () => void,
|
closeModel: () => void,
|
||||||
|
resetFields: () => void,
|
||||||
}
|
}
|
||||||
|
|
||||||
type FormOptions = {
|
type FormOptions = {
|
||||||
|
|||||||
193
src/components/BasicNjBjPicker/example.vue
Normal file
193
src/components/BasicNjBjPicker/example.vue
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
<template>
|
||||||
|
<view class="example-container">
|
||||||
|
<view class="section">
|
||||||
|
<view class="section-title">基础用法</view>
|
||||||
|
<BasicNjBjPicker
|
||||||
|
v-model="basicValue"
|
||||||
|
@change="onBasicChange"
|
||||||
|
/>
|
||||||
|
<view class="result">
|
||||||
|
选中值: {{ JSON.stringify(basicValue) }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="section">
|
||||||
|
<view class="section-title">在 useForm 中使用</view>
|
||||||
|
<BasicForm @register="register" />
|
||||||
|
<view class="result">
|
||||||
|
表单值: {{ JSON.stringify(formValue) }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="section">
|
||||||
|
<view class="section-title">自定义样式</view>
|
||||||
|
<BasicNjBjPicker
|
||||||
|
v-model="customValue"
|
||||||
|
placeholder="请选择年级班级"
|
||||||
|
:customStyle="{
|
||||||
|
backgroundColor: '#e6f7ff',
|
||||||
|
borderColor: '#1890ff',
|
||||||
|
borderRadius: '8px',
|
||||||
|
padding: '12px 16px'
|
||||||
|
}"
|
||||||
|
icon-arrow="right"
|
||||||
|
icon-color="#1890ff"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="section">
|
||||||
|
<view class="section-title">禁用状态</view>
|
||||||
|
<BasicNjBjPicker
|
||||||
|
v-model="disabledValue"
|
||||||
|
:disabled="true"
|
||||||
|
placeholder="已禁用"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="section">
|
||||||
|
<view class="section-title">操作按钮</view>
|
||||||
|
<view class="button-group">
|
||||||
|
<button @click="resetBasic">重置基础选择器</button>
|
||||||
|
<button @click="setBasicValue">设置基础选择器值</button>
|
||||||
|
<button @click="resetForm">重置表单</button>
|
||||||
|
<button @click="setFormValue">设置表单值</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { BasicNjBjPicker } from '@/components/BasicNjBjPicker'
|
||||||
|
import { useForm } from '@/components/BasicForm/hooks/useForm'
|
||||||
|
|
||||||
|
// 基础用法
|
||||||
|
const basicValue = ref(null)
|
||||||
|
const customValue = ref(null)
|
||||||
|
const disabledValue = ref({
|
||||||
|
nj: { key: '1', title: '一年级' },
|
||||||
|
bj: { key: '1-1', title: '1班' }
|
||||||
|
})
|
||||||
|
|
||||||
|
const onBasicChange = (nj: any, bj: any) => {
|
||||||
|
console.log('基础选择器变化:', nj, bj)
|
||||||
|
}
|
||||||
|
|
||||||
|
// useForm 用法
|
||||||
|
const [register, { getValue, setValue, resetFields }] = useForm({
|
||||||
|
schema: [
|
||||||
|
{
|
||||||
|
field: 'classInfo',
|
||||||
|
label: '选择班级',
|
||||||
|
component: BasicNjBjPicker,
|
||||||
|
required: true,
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请选择班级',
|
||||||
|
customStyle: {
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
borderRadius: '8px',
|
||||||
|
padding: '12px 15px'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'studentName',
|
||||||
|
label: '学生姓名',
|
||||||
|
component: 'BasicInput',
|
||||||
|
required: true,
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请输入学生姓名'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
const formValue = ref({})
|
||||||
|
|
||||||
|
// 监听表单值变化
|
||||||
|
watch(() => getValue(), (newVal) => {
|
||||||
|
formValue.value = newVal
|
||||||
|
}, { deep: true, immediate: true })
|
||||||
|
|
||||||
|
// 操作按钮方法
|
||||||
|
const resetBasic = () => {
|
||||||
|
basicValue.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
const setBasicValue = () => {
|
||||||
|
basicValue.value = {
|
||||||
|
nj: { key: '2', title: '二年级' },
|
||||||
|
bj: { key: '2-1', title: '1班' },
|
||||||
|
njId: '2',
|
||||||
|
bjId: '2-1',
|
||||||
|
njmc: '二年级',
|
||||||
|
bjmc: '1班'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetForm = () => {
|
||||||
|
resetFields()
|
||||||
|
}
|
||||||
|
|
||||||
|
const setFormValue = () => {
|
||||||
|
setValue({
|
||||||
|
classInfo: {
|
||||||
|
nj: { key: '3', title: '三年级' },
|
||||||
|
bj: { key: '3-1', title: '1班' },
|
||||||
|
njId: '3',
|
||||||
|
bjId: '3-1',
|
||||||
|
njmc: '三年级',
|
||||||
|
bjmc: '1班'
|
||||||
|
},
|
||||||
|
studentName: '张三'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.example-container {
|
||||||
|
padding: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section {
|
||||||
|
margin-bottom: 40rpx;
|
||||||
|
padding: 30rpx;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result {
|
||||||
|
margin-top: 20rpx;
|
||||||
|
padding: 20rpx;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #666;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-group {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-group button {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 200rpx;
|
||||||
|
height: 70rpx;
|
||||||
|
background-color: #007aff;
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 35rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
4
src/components/BasicNjBjPicker/index.ts
Normal file
4
src/components/BasicNjBjPicker/index.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import BasicNjBjPicker from './index.vue'
|
||||||
|
|
||||||
|
export { BasicNjBjPicker }
|
||||||
|
export default BasicNjBjPicker
|
||||||
216
src/components/BasicNjBjPicker/index.vue
Normal file
216
src/components/BasicNjBjPicker/index.vue
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
<template>
|
||||||
|
<picker mode="multiSelector" :range="njBjRange" :value="curIndex" @change="onMultiChange"
|
||||||
|
@columnchange="onColumnChange">
|
||||||
|
<view class="picker-item" :style="customStyle">
|
||||||
|
<text>{{ curNjBjLabel || placeholder }}</text>
|
||||||
|
<uni-icons :type="iconArrow" size="14" :color="iconColor"></uni-icons>
|
||||||
|
</view>
|
||||||
|
</picker>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { findAllNjBjTreeApi } from "@/api/base/server";
|
||||||
|
|
||||||
|
// 接收外部传入属性并设置默认值
|
||||||
|
const props = withDefaults(defineProps<{
|
||||||
|
modelValue?: any,
|
||||||
|
defaultValue?: any,
|
||||||
|
customStyle?: any,
|
||||||
|
iconArrow?: string,
|
||||||
|
iconColor?: string,
|
||||||
|
placeholder?: string,
|
||||||
|
disabled?: boolean
|
||||||
|
}>(), {
|
||||||
|
modelValue: null,
|
||||||
|
defaultValue: null,
|
||||||
|
customStyle: {},
|
||||||
|
iconArrow: "bottom",
|
||||||
|
iconColor: "#666",
|
||||||
|
placeholder: "选择班级",
|
||||||
|
disabled: false
|
||||||
|
});
|
||||||
|
|
||||||
|
// 定义一个上级传入的emit响应事件用于接收数据变更
|
||||||
|
const emit = defineEmits(['change', 'update:modelValue'])
|
||||||
|
|
||||||
|
const bjList = ref<any>([]);
|
||||||
|
const njBjRange = ref<any>([[], []]);
|
||||||
|
const curIndex = ref([0, 0]);
|
||||||
|
const curNjBjLabel = ref("");
|
||||||
|
const isLoading = ref(false);
|
||||||
|
|
||||||
|
// 监听 modelValue 变化
|
||||||
|
watch(() => props.modelValue, (newVal) => {
|
||||||
|
if (newVal && newVal.nj && newVal.bj) {
|
||||||
|
setValueByData(newVal.nj, newVal.bj);
|
||||||
|
}
|
||||||
|
}, { immediate: true });
|
||||||
|
|
||||||
|
// 根据数据设置选择器值
|
||||||
|
const setValueByData = (nj: any, bj: any) => {
|
||||||
|
if (!nj || !bj || !bjList.value.length) return;
|
||||||
|
|
||||||
|
const njIndex = bjList.value.findIndex((item: any) => item.key === nj.key);
|
||||||
|
if (njIndex === -1) return;
|
||||||
|
|
||||||
|
const bjIndex = bjList.value[njIndex].children.findIndex((item: any) => item.key === bj.key);
|
||||||
|
if (bjIndex === -1) return;
|
||||||
|
|
||||||
|
curIndex.value = [njIndex, bjIndex];
|
||||||
|
curNjBjLabel.value = nj.title + " " + bj.title;
|
||||||
|
rebuildNjBjList(njIndex);
|
||||||
|
};
|
||||||
|
|
||||||
|
const rebuildNjBjList = (njIndex: number) => {
|
||||||
|
if (!bjList.value[njIndex] || !bjList.value[njIndex].children) return;
|
||||||
|
const bjRange = bjList.value[njIndex].children.map((bj: any) => bj.title);
|
||||||
|
njBjRange.value[1] = bjRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
const onChange = (index: any) => {
|
||||||
|
if (props.disabled) return;
|
||||||
|
|
||||||
|
curIndex.value = index;
|
||||||
|
if (index.length === 2 && index[0] >= 0 && index[1] >= 0) {
|
||||||
|
const selectedNj = bjList.value[index[0]];
|
||||||
|
const selectedBj = bjList.value[index[0]].children[index[1]];
|
||||||
|
|
||||||
|
curNjBjLabel.value = selectedNj.title + " " + selectedBj.title;
|
||||||
|
|
||||||
|
// 发送数据给父组件
|
||||||
|
const result = {
|
||||||
|
nj: selectedNj,
|
||||||
|
bj: selectedBj,
|
||||||
|
njId: selectedNj.key,
|
||||||
|
bjId: selectedBj.key,
|
||||||
|
njmc: selectedNj.title,
|
||||||
|
bjmc: selectedBj.title,
|
||||||
|
njmcId: selectedNj.njmcId // 添加 njmcId 字段用于保存
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('📤 发送班级数据给父组件:', result);
|
||||||
|
console.log('🔍 班级数据字段映射:', {
|
||||||
|
'selectedNj.njmcId': selectedNj.njmcId,
|
||||||
|
'result.njmcId': result.njmcId,
|
||||||
|
'selectedNj.key': selectedNj.key,
|
||||||
|
'result.njId': result.njId
|
||||||
|
});
|
||||||
|
|
||||||
|
emit("change", selectedNj, selectedBj);
|
||||||
|
emit("update:modelValue", result);
|
||||||
|
} else {
|
||||||
|
curNjBjLabel.value = "";
|
||||||
|
emit("change", null, null);
|
||||||
|
emit("update:modelValue", 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载数据
|
||||||
|
const loadData = async () => {
|
||||||
|
if (isLoading.value) return;
|
||||||
|
|
||||||
|
isLoading.value = true;
|
||||||
|
try {
|
||||||
|
const res = await findAllNjBjTreeApi();
|
||||||
|
bjList.value = res.result || [];
|
||||||
|
|
||||||
|
if (bjList.value.length > 0) {
|
||||||
|
const njRange = bjList.value.map((nj: any) => nj.title);
|
||||||
|
const bjRange = bjList.value[0].children?.map((bj: any) => bj.title) || [];
|
||||||
|
njBjRange.value = [njRange, bjRange];
|
||||||
|
|
||||||
|
// 如果有默认值,设置默认选择
|
||||||
|
if (props.defaultValue) {
|
||||||
|
setValueByData(props.defaultValue.nj, props.defaultValue.bj);
|
||||||
|
} else {
|
||||||
|
onChange([0, 0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载年级班级数据失败:', error);
|
||||||
|
uni.showToast({
|
||||||
|
title: '加载数据失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 暴露方法给父组件
|
||||||
|
const reset = () => {
|
||||||
|
curIndex.value = [0, 0];
|
||||||
|
curNjBjLabel.value = "";
|
||||||
|
emit("change", null, null);
|
||||||
|
emit("update:modelValue", null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const setValue = (nj: any, bj: any) => {
|
||||||
|
setValueByData(nj, bj);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 暴露方法
|
||||||
|
defineExpose({
|
||||||
|
reset,
|
||||||
|
setValue,
|
||||||
|
loadData
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loadData();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 如果有默认值,在数据加载后设置
|
||||||
|
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;
|
||||||
|
min-height: 40px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background-color: #e6f7ff;
|
||||||
|
border-color: #1890ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
text {
|
||||||
|
margin-right: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
flex: 0 0 80%; // 内容占80%宽度
|
||||||
|
max-width: 80%; // 确保最大宽度不超过80%
|
||||||
|
}
|
||||||
|
|
||||||
|
// 箭头容器,确保箭头始终显示
|
||||||
|
.uni-icons {
|
||||||
|
flex: 0 0 auto; // 箭头不缩放,保持固定大小
|
||||||
|
margin-left: 8px; // 与文字保持间距
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
283
src/components/BasicXsPicker/example.vue
Normal file
283
src/components/BasicXsPicker/example.vue
Normal file
@ -0,0 +1,283 @@
|
|||||||
|
<template>
|
||||||
|
<view class="example-container">
|
||||||
|
<view class="section">
|
||||||
|
<view class="section-title">基础用法(单独使用)</view>
|
||||||
|
<BasicXsPicker
|
||||||
|
v-model="basicValue"
|
||||||
|
:nj-id="'1'"
|
||||||
|
:bj-id="'1-1'"
|
||||||
|
@change="onBasicChange"
|
||||||
|
/>
|
||||||
|
<view class="result">
|
||||||
|
选中值: {{ JSON.stringify(basicValue) }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="section">
|
||||||
|
<view class="section-title">与 BasicNjBjPicker 联动使用</view>
|
||||||
|
<BasicNjBjPicker
|
||||||
|
v-model="classValue"
|
||||||
|
@change="onClassChange"
|
||||||
|
placeholder="请选择年级班级"
|
||||||
|
/>
|
||||||
|
<BasicXsPicker
|
||||||
|
v-model="studentValue"
|
||||||
|
:nj-id="classValue?.njId"
|
||||||
|
:bj-id="classValue?.bjId"
|
||||||
|
@change="onStudentChange"
|
||||||
|
placeholder="请选择学生"
|
||||||
|
/>
|
||||||
|
<view class="result">
|
||||||
|
班级信息: {{ JSON.stringify(classValue) }}
|
||||||
|
</view>
|
||||||
|
<view class="result">
|
||||||
|
学生信息: {{ JSON.stringify(studentValue) }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="section">
|
||||||
|
<view class="section-title">在 useForm 中使用</view>
|
||||||
|
<BasicForm @register="register" />
|
||||||
|
<view class="result">
|
||||||
|
表单值: {{ JSON.stringify(formValue) }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="section">
|
||||||
|
<view class="section-title">多年级班级选择</view>
|
||||||
|
<BasicXsPicker
|
||||||
|
v-model="multiValue"
|
||||||
|
:nj-ids="['1', '2']"
|
||||||
|
:bj-ids="['1-1', '2-1']"
|
||||||
|
@change="onMultiChange"
|
||||||
|
placeholder="请选择学生(多班级)"
|
||||||
|
/>
|
||||||
|
<view class="result">
|
||||||
|
选中值: {{ JSON.stringify(multiValue) }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="section">
|
||||||
|
<view class="section-title">操作按钮</view>
|
||||||
|
<view class="button-group">
|
||||||
|
<button @click="resetBasic">重置基础选择器</button>
|
||||||
|
<button @click="setBasicValue">设置基础选择器值</button>
|
||||||
|
<button @click="resetForm">重置表单</button>
|
||||||
|
<button @click="setFormValue">设置表单值</button>
|
||||||
|
<button @click="reloadStudents">重新加载学生数据</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, watch } from 'vue'
|
||||||
|
import { BasicNjBjPicker } from '@/components/BasicNjBjPicker'
|
||||||
|
import { BasicXsPicker } from '@/components/BasicXsPicker'
|
||||||
|
import { useForm } from '@/components/BasicForm/hooks/useForm'
|
||||||
|
|
||||||
|
// 基础用法
|
||||||
|
const basicValue = ref(null)
|
||||||
|
const multiValue = ref(null)
|
||||||
|
|
||||||
|
// 联动用法
|
||||||
|
const classValue = ref<any>(null)
|
||||||
|
const studentValue = ref<any>(null)
|
||||||
|
|
||||||
|
// useForm 用法
|
||||||
|
const [register, { getValue, setValue, resetFields }] = useForm({
|
||||||
|
schema: [
|
||||||
|
{
|
||||||
|
field: 'classInfo',
|
||||||
|
label: '选择班级',
|
||||||
|
component: 'BasicNjBjPicker',
|
||||||
|
required: true,
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请选择班级'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'studentInfo',
|
||||||
|
label: '选择学生',
|
||||||
|
component: 'BasicXsPicker',
|
||||||
|
required: true,
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请先选择班级',
|
||||||
|
// 通过计算属性获取班级信息
|
||||||
|
njId: () => getValue().classInfo?.njId,
|
||||||
|
bjId: () => getValue().classInfo?.bjId
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'studentName',
|
||||||
|
label: '学生姓名',
|
||||||
|
component: 'BasicInput',
|
||||||
|
required: true,
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请输入学生姓名'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
const formValue = ref({})
|
||||||
|
|
||||||
|
// 监听表单值变化
|
||||||
|
watch(() => getValue(), (newVal) => {
|
||||||
|
formValue.value = newVal
|
||||||
|
}, { deep: true, immediate: true })
|
||||||
|
|
||||||
|
// 基础功能测试
|
||||||
|
const onBasicChange = (student: any) => {
|
||||||
|
console.log('基础选择器变化:', student)
|
||||||
|
uni.showToast({
|
||||||
|
title: `选择了学生: ${student?.xsxm}`,
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 联动功能测试
|
||||||
|
const onClassChange = (nj: any, bj: any) => {
|
||||||
|
console.log('班级变化:', nj, bj)
|
||||||
|
// 班级变化时清空学生选择
|
||||||
|
studentValue.value = null
|
||||||
|
uni.showToast({
|
||||||
|
title: `选择了班级: ${nj?.title} ${bj?.title}`,
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const onStudentChange = (student: any) => {
|
||||||
|
console.log('学生变化:', student)
|
||||||
|
uni.showToast({
|
||||||
|
title: `选择了学生: ${student?.xsxm}`,
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const onMultiChange = (student: any) => {
|
||||||
|
console.log('多班级选择器变化:', student)
|
||||||
|
uni.showToast({
|
||||||
|
title: `选择了学生: ${student?.xsxm}`,
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试方法
|
||||||
|
const resetBasic = () => {
|
||||||
|
basicValue.value = null
|
||||||
|
uni.showToast({
|
||||||
|
title: '已重置',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const setBasicValue = () => {
|
||||||
|
// 这里需要根据实际的学生数据设置
|
||||||
|
basicValue.value = {
|
||||||
|
xsId: '1',
|
||||||
|
xsxm: '张三',
|
||||||
|
njId: '1',
|
||||||
|
bjId: '1-1',
|
||||||
|
njmc: '一年级',
|
||||||
|
bjmc: '1班'
|
||||||
|
} as any
|
||||||
|
uni.showToast({
|
||||||
|
title: '已设置测试值',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetForm = () => {
|
||||||
|
resetFields()
|
||||||
|
uni.showToast({
|
||||||
|
title: '表单已重置',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const setFormValue = () => {
|
||||||
|
setValue({
|
||||||
|
classInfo: {
|
||||||
|
njId: '2',
|
||||||
|
bjId: '2-1',
|
||||||
|
njmc: '二年级',
|
||||||
|
bjmc: '1班'
|
||||||
|
},
|
||||||
|
studentInfo: {
|
||||||
|
xsId: '2',
|
||||||
|
xsxm: '李四',
|
||||||
|
njId: '2',
|
||||||
|
bjId: '2-1',
|
||||||
|
njmc: '二年级',
|
||||||
|
bjmc: '1班'
|
||||||
|
},
|
||||||
|
studentName: '李四'
|
||||||
|
})
|
||||||
|
uni.showToast({
|
||||||
|
title: '已设置表单值',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const reloadStudents = () => {
|
||||||
|
// 这里可以调用组件的 reload 方法
|
||||||
|
uni.showToast({
|
||||||
|
title: '重新加载学生数据',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.example-container {
|
||||||
|
padding: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section {
|
||||||
|
margin-bottom: 40rpx;
|
||||||
|
padding: 30rpx;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
padding-bottom: 10rpx;
|
||||||
|
border-bottom: 2rpx solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result {
|
||||||
|
margin-top: 20rpx;
|
||||||
|
padding: 20rpx;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
border-left: 4rpx solid #007aff;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #333;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-group {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 20rpx;
|
||||||
|
margin-top: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-group button {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 200rpx;
|
||||||
|
height: 70rpx;
|
||||||
|
background-color: #007aff;
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 35rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
4
src/components/BasicXsPicker/index.ts
Normal file
4
src/components/BasicXsPicker/index.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import BasicXsPicker from './index.vue'
|
||||||
|
|
||||||
|
export { BasicXsPicker }
|
||||||
|
export default BasicXsPicker
|
||||||
816
src/components/BasicXsPicker/index.vue
Normal file
816
src/components/BasicXsPicker/index.vue
Normal file
@ -0,0 +1,816 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 选择器触发按钮 -->
|
||||||
|
<view class="picker-item" :style="customStyle" @click="showPicker">
|
||||||
|
<text>{{ curStudentLabel || displayPlaceholder }}</text>
|
||||||
|
<uni-icons :type="iconArrow" size="14" :color="iconColor"></uni-icons>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 弹窗选择器 -->
|
||||||
|
<u-popup :show="showPopup" @close="showPopup=false">
|
||||||
|
<view class="student-picker-container">
|
||||||
|
<!-- 头部 -->
|
||||||
|
<view class="popup-header">
|
||||||
|
<view class="header-left" @click="handleCancel">
|
||||||
|
<text class="cancel-text">取消</text>
|
||||||
|
</view>
|
||||||
|
<view class="header-center">
|
||||||
|
<text class="header-title">请选择学生</text>
|
||||||
|
</view>
|
||||||
|
<view class="header-right" @click="handleConfirm">
|
||||||
|
<text class="confirm-text">确定</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 搜索框 -->
|
||||||
|
<view class="search-section">
|
||||||
|
<view class="search-box">
|
||||||
|
<uni-icons type="search" size="16" color="#999" class="search-icon"></uni-icons>
|
||||||
|
<input
|
||||||
|
v-model="searchKeyword"
|
||||||
|
class="search-input"
|
||||||
|
placeholder="输入学生姓名查询"
|
||||||
|
@input="onSearchInput"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 学生列表 -->
|
||||||
|
<view class="student-list">
|
||||||
|
<view
|
||||||
|
v-for="(student, index) in filteredStudentList"
|
||||||
|
:key="student.id || index"
|
||||||
|
class="student-item"
|
||||||
|
:class="{ selected: selectedStudent && selectedStudent.id === student.id }"
|
||||||
|
@click="selectStudent(student)"
|
||||||
|
>
|
||||||
|
<text class="student-name">{{ student.xm }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 空状态 -->
|
||||||
|
<view v-if="filteredStudentList.length === 0 && !isLoading" class="empty-state">
|
||||||
|
<uni-icons type="info" size="60" color="#ccc"></uni-icons>
|
||||||
|
<text class="empty-text">
|
||||||
|
{{ searchKeyword ? '未找到匹配的学生' : '暂无学生数据' }}
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 加载状态 -->
|
||||||
|
<view v-if="isLoading" class="loading-state">
|
||||||
|
<uni-icons type="spinner-cycle" size="40" color="#007aff"></uni-icons>
|
||||||
|
<text class="loading-text">加载中...</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</u-popup>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { findStudentInfoByNjAndBjApi } from "@/api/base/student";
|
||||||
|
|
||||||
|
// 接收外部传入属性并设置默认值
|
||||||
|
const props = withDefaults(defineProps<{
|
||||||
|
modelValue?: any,
|
||||||
|
defaultValue?: any,
|
||||||
|
customStyle?: any,
|
||||||
|
iconArrow?: string,
|
||||||
|
iconColor?: string,
|
||||||
|
placeholder?: string,
|
||||||
|
disabled?: boolean,
|
||||||
|
njId?: string,
|
||||||
|
bjId?: string,
|
||||||
|
njIds?: string[],
|
||||||
|
bjIds?: string[],
|
||||||
|
autoLoad?: boolean,
|
||||||
|
classInfo?: any,
|
||||||
|
studentData?: any[],
|
||||||
|
range?: string[] // 参考 BasicPicker 的设计,直接传递选项数组
|
||||||
|
}>(), {
|
||||||
|
modelValue: null,
|
||||||
|
defaultValue: null,
|
||||||
|
customStyle: {},
|
||||||
|
iconArrow: "bottom",
|
||||||
|
iconColor: "#666",
|
||||||
|
placeholder: "选择学生",
|
||||||
|
disabled: false,
|
||||||
|
njId: "",
|
||||||
|
bjId: "",
|
||||||
|
njIds: () => [],
|
||||||
|
bjIds: () => [],
|
||||||
|
autoLoad: true,
|
||||||
|
classInfo: null,
|
||||||
|
studentData: () => [],
|
||||||
|
range: () => []
|
||||||
|
});
|
||||||
|
|
||||||
|
// 定义一个上级传入的emit响应事件用于接收数据变更
|
||||||
|
const emit = defineEmits(['change', 'update:modelValue', 'getClassInfo'])
|
||||||
|
|
||||||
|
const studentList = ref<any>([]);
|
||||||
|
const studentRange = ref<string[]>([]);
|
||||||
|
const curIndex = ref(0);
|
||||||
|
const curStudentLabel = ref("");
|
||||||
|
const isLoading = ref(false);
|
||||||
|
const isUserSelecting = ref(false); // 标志用户是否正在选择
|
||||||
|
|
||||||
|
// 弹窗相关状态
|
||||||
|
const showPopup = ref(false);
|
||||||
|
const searchKeyword = ref("");
|
||||||
|
const selectedStudent = ref<any>(null);
|
||||||
|
const filteredStudentList = ref<any>([]);
|
||||||
|
|
||||||
|
// 辅助函数:安全地获取值
|
||||||
|
const getValue = (value: any) => {
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 计算占位符文本
|
||||||
|
const displayPlaceholder = computed(() => {
|
||||||
|
// 如果没有年级班级信息,显示提示信息
|
||||||
|
if (!props.njId && !props.bjId && (!props.njIds || props.njIds.length === 0) && (!props.bjIds || props.bjIds.length === 0)) {
|
||||||
|
return "请先选择年级班级";
|
||||||
|
}
|
||||||
|
return props.placeholder;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 搜索过滤逻辑
|
||||||
|
const filterStudents = (students: any[], keyword: string) => {
|
||||||
|
if (!keyword.trim()) {
|
||||||
|
return students;
|
||||||
|
}
|
||||||
|
return students.filter(student =>
|
||||||
|
student.xm && student.xm.toLowerCase().includes(keyword.toLowerCase())
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 根据数据设置选择器值
|
||||||
|
const setValueByData = (student: any) => {
|
||||||
|
if (!student || !student.id || !studentList.value.length) return;
|
||||||
|
|
||||||
|
const studentIndex = studentList.value.findIndex((item: any) => item.id === student.id);
|
||||||
|
if (studentIndex === -1) return;
|
||||||
|
|
||||||
|
curIndex.value = studentIndex;
|
||||||
|
curStudentLabel.value = student.xm;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 显示选择器弹窗
|
||||||
|
const showPicker = async () => {
|
||||||
|
console.log('🖱️ 用户点击学生选择器');
|
||||||
|
|
||||||
|
// 设置用户选择标志
|
||||||
|
isUserSelecting.value = true;
|
||||||
|
|
||||||
|
// 清空搜索关键词
|
||||||
|
searchKeyword.value = "";
|
||||||
|
|
||||||
|
// 如果已经有学生数据,直接显示弹窗
|
||||||
|
if (studentList.value.length > 0) {
|
||||||
|
console.log('✅ 学生数据已存在,直接显示弹窗');
|
||||||
|
filteredStudentList.value = studentList.value;
|
||||||
|
showPopup.value = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果正在加载中,不重复加载
|
||||||
|
if (isLoading.value) {
|
||||||
|
console.log('⏳ 正在加载中,请稍候');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 直接尝试从父组件获取班级信息
|
||||||
|
console.log('📞 尝试从父组件获取班级信息');
|
||||||
|
|
||||||
|
// 通过 emit 请求父组件提供班级信息
|
||||||
|
emit('getClassInfo');
|
||||||
|
|
||||||
|
// 延迟一下,给父组件时间处理
|
||||||
|
setTimeout(() => {
|
||||||
|
console.log('⏰ 延迟检查是否获取到班级信息');
|
||||||
|
console.log('🔍 当前 props.classInfo:', props.classInfo);
|
||||||
|
|
||||||
|
// 如果 props.classInfo 有值,直接使用
|
||||||
|
if (props.classInfo && props.classInfo.njId && props.classInfo.bjId) {
|
||||||
|
console.log('✅ 发现 props.classInfo,直接加载学生数据');
|
||||||
|
loadStudentDataByClassInfo(props.classInfo);
|
||||||
|
} else {
|
||||||
|
console.log('❌ 没有获取到班级信息,尝试直接调用API');
|
||||||
|
// 尝试直接调用API,使用默认的年级班级ID
|
||||||
|
// 这里需要从全局状态或者其他地方获取班级信息
|
||||||
|
// 暂时先显示提示
|
||||||
|
uni.showToast({
|
||||||
|
title: '请先选择年级班级',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 200);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 暴露方法给父组件调用
|
||||||
|
const loadStudentDataByClassInfo = async (classInfo: any) => {
|
||||||
|
console.log('🔄 通过班级信息加载学生数据:', classInfo);
|
||||||
|
|
||||||
|
if (!classInfo || !classInfo.njId || !classInfo.bjId) {
|
||||||
|
console.log('⚠️ 班级信息不完整,清空学生数据');
|
||||||
|
clearStudentData();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
isLoading.value = true;
|
||||||
|
console.log('🚀 开始加载学生数据');
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
njId: classInfo.njId,
|
||||||
|
bjIds: [classInfo.bjId]
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('📤 发送API请求,参数:', params);
|
||||||
|
const res = await findStudentInfoByNjAndBjApi(params);
|
||||||
|
|
||||||
|
if (res && res.result && res.result.length > 0) {
|
||||||
|
console.log('✅ 学生数据加载成功:', res.result);
|
||||||
|
studentList.value = res.result;
|
||||||
|
studentRange.value = res.result.map((item: any) => item.xm || '');
|
||||||
|
|
||||||
|
// 更新过滤列表
|
||||||
|
filteredStudentList.value = studentList.value;
|
||||||
|
|
||||||
|
// 如果有默认值,设置选中项
|
||||||
|
if (props.modelValue) {
|
||||||
|
const index = studentList.value.findIndex((item: any) => item.id === props.modelValue);
|
||||||
|
if (index >= 0) {
|
||||||
|
curIndex.value = index;
|
||||||
|
curStudentLabel.value = studentRange.value[index];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 没有默认值时,不设置 curIndex,保持弹窗打开状态
|
||||||
|
console.log('🎯 不自动选择,等待用户手动选择');
|
||||||
|
curStudentLabel.value = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果弹窗已打开,显示学生列表
|
||||||
|
if (showPopup.value) {
|
||||||
|
console.log('📋 弹窗已打开,显示学生列表');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log('⚠️ 没有找到学生数据');
|
||||||
|
clearStudentData();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ 加载学生数据失败:', error);
|
||||||
|
clearStudentData();
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 直接设置学生数据的方法
|
||||||
|
const setStudentData = (data: any[]) => {
|
||||||
|
console.log('🔄 直接设置学生数据:', data?.length, '个学生');
|
||||||
|
|
||||||
|
if (data && data.length > 0) {
|
||||||
|
console.log('✅ 设置学生数据成功');
|
||||||
|
studentList.value = data;
|
||||||
|
studentRange.value = data.map((item: any) => item.xm || '');
|
||||||
|
|
||||||
|
// 更新过滤列表
|
||||||
|
filteredStudentList.value = studentList.value;
|
||||||
|
|
||||||
|
console.log('📋 学生选项列表:', studentRange.value.slice(0, 5)); // 只显示前5个选项
|
||||||
|
|
||||||
|
// 如果有默认值,设置选中项
|
||||||
|
if (props.modelValue) {
|
||||||
|
const index = studentList.value.findIndex((item: any) => item.id === props.modelValue);
|
||||||
|
if (index >= 0) {
|
||||||
|
curIndex.value = index;
|
||||||
|
curStudentLabel.value = studentRange.value[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('🎯 学生选择器状态更新完成:', {
|
||||||
|
studentListLength: studentList.value.length,
|
||||||
|
studentRangeLength: studentRange.value.length,
|
||||||
|
curIndex: curIndex.value,
|
||||||
|
curStudentLabel: curStudentLabel.value
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log('⚠️ 学生数据为空,清空数据');
|
||||||
|
clearStudentData();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 清空学生数据
|
||||||
|
const clearStudentData = () => {
|
||||||
|
studentList.value = [];
|
||||||
|
studentRange.value = [];
|
||||||
|
filteredStudentList.value = [];
|
||||||
|
curIndex.value = 0;
|
||||||
|
curStudentLabel.value = "";
|
||||||
|
selectedStudent.value = null;
|
||||||
|
emit('update:modelValue', null);
|
||||||
|
emit('change', null);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 搜索输入处理
|
||||||
|
const onSearchInput = () => {
|
||||||
|
console.log('🔍 搜索关键词:', searchKeyword.value);
|
||||||
|
filteredStudentList.value = filterStudents(studentList.value, searchKeyword.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 选择学生
|
||||||
|
const selectStudent = (student: any) => {
|
||||||
|
console.log('🎯 选择学生:', student);
|
||||||
|
selectedStudent.value = student;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 取消操作
|
||||||
|
const handleCancel = () => {
|
||||||
|
console.log('❌ 取消选择');
|
||||||
|
showPopup.value = false;
|
||||||
|
selectedStudent.value = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 确认选择
|
||||||
|
const handleConfirm = () => {
|
||||||
|
console.log('✅ 确认选择:', selectedStudent.value);
|
||||||
|
|
||||||
|
if (props.disabled) {
|
||||||
|
console.log('⏸️ 组件已禁用,跳过处理');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedStudent.value) {
|
||||||
|
const student = selectedStudent.value;
|
||||||
|
console.log('✅ 选中的学生:', student);
|
||||||
|
curStudentLabel.value = student.xm;
|
||||||
|
|
||||||
|
// 发送数据给父组件
|
||||||
|
const result = {
|
||||||
|
id: student.id,
|
||||||
|
xsId: student.id, // 添加 xsId 字段,与 id 保持一致
|
||||||
|
xm: student.xm,
|
||||||
|
xsxm: student.xm, // 添加 xsxm 字段,与 xm 保持一致
|
||||||
|
studentName: student.xm, // 添加 studentName 字段,与 xm 保持一致
|
||||||
|
njId: student.njId,
|
||||||
|
bjId: student.bjId,
|
||||||
|
njmc: student.njmc,
|
||||||
|
bjmc: student.bjmc,
|
||||||
|
jzIds: student.jzIds,
|
||||||
|
jzxm: student.jzxm,
|
||||||
|
student: student
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('📤 发送给父组件的数据:', result);
|
||||||
|
console.log('🔍 学生数据字段映射:', {
|
||||||
|
'student.id': student.id,
|
||||||
|
'result.xsId': result.xsId,
|
||||||
|
'student.xm': student.xm,
|
||||||
|
'result.xsxm': result.xsxm,
|
||||||
|
'result.studentName': result.studentName
|
||||||
|
});
|
||||||
|
emit("change", student);
|
||||||
|
emit("update:modelValue", result);
|
||||||
|
} else {
|
||||||
|
console.log('❌ 未选择学生');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭弹窗
|
||||||
|
showPopup.value = false;
|
||||||
|
selectedStudent.value = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 加载学生数据
|
||||||
|
const loadStudentData = async () => {
|
||||||
|
console.log('🔄 加载学生数据,年级班级:', {
|
||||||
|
njId: props.njId,
|
||||||
|
bjId: props.bjId,
|
||||||
|
njIds: props.njIds,
|
||||||
|
bjIds: props.bjIds,
|
||||||
|
autoLoad: props.autoLoad
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isLoading.value) {
|
||||||
|
console.log('⏳ 正在加载中,跳过');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否有年级班级信息
|
||||||
|
if (!props.njId && !props.bjId && (!props.njIds || props.njIds.length === 0) && (!props.bjIds || props.bjIds.length === 0)) {
|
||||||
|
console.log('⚠️ 没有年级班级信息,清空数据');
|
||||||
|
// 如果没有年级班级信息,清空学生列表并显示占位符
|
||||||
|
studentList.value = [];
|
||||||
|
studentRange.value = [];
|
||||||
|
curStudentLabel.value = "";
|
||||||
|
// 发送空值给父组件
|
||||||
|
emit("change", null);
|
||||||
|
emit("update:modelValue", null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('🚀 开始加载学生数据');
|
||||||
|
isLoading.value = true;
|
||||||
|
try {
|
||||||
|
const params: any = {};
|
||||||
|
|
||||||
|
// 设置查询参数
|
||||||
|
if (props.njId) {
|
||||||
|
params.njId = props.njId;
|
||||||
|
} else if (props.njIds && props.njIds.length > 0) {
|
||||||
|
params.njId = props.njIds[0]; // 取第一个年级ID
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.bjId) {
|
||||||
|
params.bjIds = [props.bjId];
|
||||||
|
} else if (props.bjIds && props.bjIds.length > 0) {
|
||||||
|
params.bjIds = props.bjIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('📤 发送API请求,参数:', params);
|
||||||
|
const res = await findStudentInfoByNjAndBjApi(params);
|
||||||
|
console.log('📥 API响应:', res);
|
||||||
|
studentList.value = res.result || [];
|
||||||
|
|
||||||
|
if (studentList.value.length > 0) {
|
||||||
|
console.log('✅ 成功加载学生数据,数量:', studentList.value.length);
|
||||||
|
studentRange.value = studentList.value.map((student: any) => student.xm);
|
||||||
|
|
||||||
|
// 更新过滤列表
|
||||||
|
filteredStudentList.value = studentList.value;
|
||||||
|
|
||||||
|
console.log('📋 学生选项列表:', studentRange.value);
|
||||||
|
|
||||||
|
// 如果有默认值,设置默认选择
|
||||||
|
if (props.defaultValue) {
|
||||||
|
console.log('🎯 设置默认值:', props.defaultValue);
|
||||||
|
setValueByData(props.defaultValue);
|
||||||
|
} else {
|
||||||
|
console.log('🎯 不自动选择,等待用户手动选择');
|
||||||
|
// 不自动选择第一个学生,让用户手动选择
|
||||||
|
// 不设置 curIndex,保持弹窗打开状态
|
||||||
|
curStudentLabel.value = "";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log('❌ 没有加载到学生数据');
|
||||||
|
studentRange.value = [];
|
||||||
|
filteredStudentList.value = [];
|
||||||
|
curStudentLabel.value = "";
|
||||||
|
emit("change", null);
|
||||||
|
emit("update:modelValue", null);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ 加载学生数据失败:', error);
|
||||||
|
uni.showToast({
|
||||||
|
title: '加载学生数据失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
studentList.value = [];
|
||||||
|
studentRange.value = [];
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false;
|
||||||
|
console.log('🏁 loadStudentData 完成');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 监听 modelValue 变化
|
||||||
|
watch(() => props.modelValue, (newVal) => {
|
||||||
|
if (newVal && newVal.id) {
|
||||||
|
setValueByData(newVal);
|
||||||
|
}
|
||||||
|
}, { immediate: true });
|
||||||
|
|
||||||
|
// 监听年级班级变化,自动加载学生数据
|
||||||
|
watch([() => props.njId, () => props.bjId, () => props.njIds, () => props.bjIds], (newValues, oldValues) => {
|
||||||
|
console.log('👀 年级班级变化:', {
|
||||||
|
newValues,
|
||||||
|
oldValues,
|
||||||
|
autoLoad: props.autoLoad
|
||||||
|
});
|
||||||
|
|
||||||
|
if (props.autoLoad) {
|
||||||
|
console.log('🔄 触发自动加载学生数据');
|
||||||
|
loadStudentData();
|
||||||
|
} else {
|
||||||
|
console.log('⏸️ autoLoad 为 false,跳过加载');
|
||||||
|
}
|
||||||
|
}, { immediate: true, deep: true });
|
||||||
|
|
||||||
|
// 监听 classInfo 变化,自动加载学生数据
|
||||||
|
watch(() => props.classInfo, (newClassInfo, oldClassInfo) => {
|
||||||
|
console.log('👀 classInfo 变化:', {
|
||||||
|
newClassInfo,
|
||||||
|
oldClassInfo
|
||||||
|
});
|
||||||
|
|
||||||
|
if (newClassInfo && newClassInfo.njId && newClassInfo.bjId) {
|
||||||
|
console.log('🔄 通过 classInfo 加载学生数据');
|
||||||
|
loadStudentDataByClassInfo(newClassInfo);
|
||||||
|
} else {
|
||||||
|
console.log('⚠️ classInfo 不完整,清空学生数据');
|
||||||
|
clearStudentData();
|
||||||
|
}
|
||||||
|
}, { immediate: true, deep: true });
|
||||||
|
|
||||||
|
// 监听 studentData 变化,直接设置学生数据(参考 BasicPicker 的设计模式)
|
||||||
|
watch(() => props.studentData, (newStudentData, oldStudentData) => {
|
||||||
|
console.log('👀 studentData 变化:', {
|
||||||
|
newStudentData,
|
||||||
|
oldStudentData,
|
||||||
|
newStudentDataLength: newStudentData?.length,
|
||||||
|
oldStudentDataLength: oldStudentData?.length
|
||||||
|
});
|
||||||
|
|
||||||
|
if (newStudentData && newStudentData.length > 0) {
|
||||||
|
console.log('✅ 接收到学生数据,直接设置');
|
||||||
|
console.log('📊 学生数据详情:', newStudentData.slice(0, 3)); // 只显示前3个学生
|
||||||
|
|
||||||
|
// 直接设置数据,参考 BasicPicker 的模式
|
||||||
|
studentList.value = newStudentData;
|
||||||
|
studentRange.value = newStudentData.map((item: any) => item.xm || ''); // 使用正确的字段名 xm
|
||||||
|
|
||||||
|
// 更新过滤列表
|
||||||
|
filteredStudentList.value = studentList.value;
|
||||||
|
|
||||||
|
console.log('📋 学生选项列表:', studentRange.value.slice(0, 5)); // 只显示前5个选项
|
||||||
|
|
||||||
|
// 如果有默认值,设置选中项
|
||||||
|
if (props.modelValue) {
|
||||||
|
const index = studentList.value.findIndex((item: any) => item.id === props.modelValue);
|
||||||
|
if (index >= 0) {
|
||||||
|
curIndex.value = index;
|
||||||
|
curStudentLabel.value = studentRange.value[index];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 没有默认值时,不设置 curIndex,保持弹窗打开状态
|
||||||
|
curStudentLabel.value = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('🎯 学生选择器状态更新完成:', {
|
||||||
|
studentListLength: studentList.value.length,
|
||||||
|
studentRangeLength: studentRange.value.length,
|
||||||
|
curIndex: curIndex.value,
|
||||||
|
curStudentLabel: curStudentLabel.value
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log('⚠️ 学生数据为空,清空数据');
|
||||||
|
clearStudentData();
|
||||||
|
}
|
||||||
|
}, { immediate: true, deep: true });
|
||||||
|
|
||||||
|
// 监听 range 变化,直接设置学生数据(参考 BasicPicker 的设计模式)
|
||||||
|
watch(() => props.range, (newRange, oldRange) => {
|
||||||
|
console.log('👀 range 变化:', {
|
||||||
|
newRange,
|
||||||
|
oldRange,
|
||||||
|
newRangeLength: newRange?.length,
|
||||||
|
oldRangeLength: oldRange?.length
|
||||||
|
});
|
||||||
|
|
||||||
|
if (newRange && newRange.length > 0) {
|
||||||
|
console.log('✅ 接收到 range 数据,直接设置');
|
||||||
|
|
||||||
|
// 直接设置数据,参考 BasicPicker 的模式
|
||||||
|
studentRange.value = newRange;
|
||||||
|
|
||||||
|
console.log('📋 学生选项列表:', studentRange.value.slice(0, 5)); // 只显示前5个选项
|
||||||
|
|
||||||
|
console.log('🎯 学生选择器状态更新完成:', {
|
||||||
|
studentRangeLength: studentRange.value.length,
|
||||||
|
curIndex: curIndex.value,
|
||||||
|
curStudentLabel: curStudentLabel.value
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log('⚠️ range 数据为空,清空数据');
|
||||||
|
clearStudentData();
|
||||||
|
}
|
||||||
|
}, { immediate: true, deep: true });
|
||||||
|
|
||||||
|
// 暴露方法给父组件
|
||||||
|
const reset = () => {
|
||||||
|
curIndex.value = 0;
|
||||||
|
curStudentLabel.value = "";
|
||||||
|
emit("change", null);
|
||||||
|
emit("update:modelValue", null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const setValue = (student: any) => {
|
||||||
|
setValueByData(student);
|
||||||
|
};
|
||||||
|
|
||||||
|
const reload = () => {
|
||||||
|
loadStudentData();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 暴露方法
|
||||||
|
defineExpose({
|
||||||
|
reset,
|
||||||
|
setValue,
|
||||||
|
reload,
|
||||||
|
loadStudentData,
|
||||||
|
loadStudentDataByClassInfo,
|
||||||
|
setStudentData,
|
||||||
|
showPicker
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
console.log('🚀 组件已挂载,props:', {
|
||||||
|
njId: props.njId,
|
||||||
|
bjId: props.bjId,
|
||||||
|
autoLoad: props.autoLoad,
|
||||||
|
classInfo: props.classInfo,
|
||||||
|
studentData: props.studentData,
|
||||||
|
range: props.range,
|
||||||
|
studentDataLength: props.studentData?.length,
|
||||||
|
rangeLength: props.range?.length
|
||||||
|
});
|
||||||
|
if (props.autoLoad) {
|
||||||
|
loadStudentData();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 监听 props 变化
|
||||||
|
watch(() => [props.njId, props.bjId, props.njIds, props.bjIds], (newValues, oldValues) => {
|
||||||
|
console.log('🔍 Props 变化监听:', {
|
||||||
|
newValues,
|
||||||
|
oldValues,
|
||||||
|
props: {
|
||||||
|
njId: props.njId,
|
||||||
|
bjId: props.bjId,
|
||||||
|
njIds: props.njIds,
|
||||||
|
bjIds: props.bjIds
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, { immediate: true, deep: true });
|
||||||
|
</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;
|
||||||
|
min-height: 40px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background-color: #e6f7ff;
|
||||||
|
border-color: #1890ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
text {
|
||||||
|
margin-right: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
flex: 0 0 80%; // 内容占80%宽度
|
||||||
|
max-width: 80%; // 确保最大宽度不超过80%
|
||||||
|
}
|
||||||
|
|
||||||
|
// 箭头容器,确保箭头始终显示
|
||||||
|
.uni-icons {
|
||||||
|
flex: 0 0 auto; // 箭头不缩放,保持固定大小
|
||||||
|
margin-left: 8px; // 与文字保持间距
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 弹窗样式
|
||||||
|
.student-picker-container {
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 16px 16px 0 0;
|
||||||
|
max-height: 80vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 15px 20px;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
background-color: #fff;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-left, .header-right {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
padding: 8px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cancel-text {
|
||||||
|
color: #666;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm-text {
|
||||||
|
color: #007aff;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-center {
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-section {
|
||||||
|
padding: 15px 20px;
|
||||||
|
background-color: #fff;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-box {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
border: 1px solid #e9ecef;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-icon {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input {
|
||||||
|
flex: 1;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
background: transparent;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.student-list {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.student-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 15px 20px;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
background-color: #e6f7ff;
|
||||||
|
|
||||||
|
.student-name {
|
||||||
|
color: #007aff;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.student-name {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #333;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state, .loading-state {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 60px 20px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-text, .loading-text {
|
||||||
|
margin-top: 15px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-state {
|
||||||
|
.loading-text {
|
||||||
|
color: #007aff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -249,13 +249,20 @@ const sections = reactive<Section[]>([
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "r5",
|
id: "r5",
|
||||||
icon: "hc-fill",
|
icon: "stxc",
|
||||||
text: "食堂巡查",
|
text: "食堂巡查",
|
||||||
show: true,
|
show: true,
|
||||||
permissionKey: "routine-stxc", // 食堂巡查权限编码
|
permissionKey: "routine-stxc", // 食堂巡查权限编码
|
||||||
path: "/pages/view/routine/ShiTangXunCha/index",
|
path: "/pages/view/routine/ShiTangXunCha/index",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: "r5",
|
||||||
|
icon: "yrcg",
|
||||||
|
text: "一日常规",
|
||||||
|
show: true,
|
||||||
|
permissionKey: "routine-yrcg", // 一日常规权限编码
|
||||||
|
path: "/pages/view/quantitativeAssessment/index/index",
|
||||||
|
},
|
||||||
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@ -105,6 +105,7 @@ import {onShow} from "@dcloudio/uni-app";
|
|||||||
import {deptFindByPidApi, dicApi} from "@/api/system/dic";
|
import {deptFindByPidApi, dicApi} from "@/api/system/dic";
|
||||||
import {useForm} from "@/components/BasicForm/hooks/useForm";
|
import {useForm} from "@/components/BasicForm/hooks/useForm";
|
||||||
import {getUserViewApi} from "@/api/system/login";
|
import {getUserViewApi} from "@/api/system/login";
|
||||||
|
import {BasicNjBjPicker} from "@/components/BasicNjBjPicker";
|
||||||
|
|
||||||
function setItemValue() {
|
function setItemValue() {
|
||||||
console.log(444,value.value)
|
console.log(444,value.value)
|
||||||
@ -122,36 +123,17 @@ function setItemValue() {
|
|||||||
const [register, {getValue, setSchema, setValue}] = useForm({
|
const [register, {getValue, setSchema, setValue}] = useForm({
|
||||||
schema: [
|
schema: [
|
||||||
{
|
{
|
||||||
field: "grade",
|
field: "classInfo",
|
||||||
label: "年级",
|
label: "年级班级",
|
||||||
component: "BasicPickerCheckbox",
|
component: "BasicNjBjPicker",
|
||||||
required: true,
|
required: true,
|
||||||
componentProps: {
|
componentProps: {
|
||||||
api: deptFindByPidApi,
|
placeholder: "请选择年级班级",
|
||||||
param: {pid: 1},
|
customStyle: {
|
||||||
rangeKey: 'deptName',
|
backgroundColor: '#fff',
|
||||||
savaKey: 'id',
|
borderRadius: '8px',
|
||||||
ok: async (ysave: any, svalue: any, range: any) => {
|
padding: '12px 15px'
|
||||||
let res = await deptFindByPidApi({pid: svalue});
|
|
||||||
setSchema([{
|
|
||||||
field: "gradeClassId",
|
|
||||||
componentProps: {
|
|
||||||
range: res.result,
|
|
||||||
rangeKey: 'deptName',
|
|
||||||
savaKey: 'id'
|
|
||||||
}
|
}
|
||||||
}])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
field: "gradeClassId",
|
|
||||||
label: "班级",
|
|
||||||
component: "BasicPickerCheckbox",
|
|
||||||
required: true,
|
|
||||||
componentProps: {
|
|
||||||
// api: gradeClassFindAllsApi,
|
|
||||||
rangeKey: 'deptName',
|
|
||||||
savaKey: 'id'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -272,10 +254,12 @@ async function submit() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let values = await getValue();
|
let values = await getValue();
|
||||||
if (values.gradeClassId) {
|
|
||||||
|
// 处理 classInfo 数据,提取 bjId
|
||||||
|
if (values.classInfo && values.classInfo.bjId) {
|
||||||
await evaluationSaveApi({
|
await evaluationSaveApi({
|
||||||
itemList: itemList,
|
itemList: itemList,
|
||||||
gradeClassId: values.gradeClassId
|
bjId: values.classInfo.bjId
|
||||||
});
|
});
|
||||||
showToast({title: "操作成功"});
|
showToast({title: "操作成功"});
|
||||||
navigateBack({delta: 1})
|
navigateBack({delta: 1})
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<BasicLayout>
|
<BasicLayout>
|
||||||
<view class="p-15">
|
<view class="p-15">
|
||||||
<BasicForm @register="register"></BasicForm>
|
<BasicForm @register="register" @getClassInfo="handleGetClassInfo"></BasicForm>
|
||||||
</view>
|
</view>
|
||||||
<template #bottom>
|
<template #bottom>
|
||||||
<view class="white-bg-color py-5" v-if="!isDisabled">
|
<view class="white-bg-color py-5" v-if="!isDisabled">
|
||||||
@ -15,33 +15,29 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import {useForm} from "@/components/BasicForm/hooks/useForm";
|
import {useForm} from "@/components/BasicForm/hooks/useForm";
|
||||||
import {deptFindByPidApi} from "@/api/system/dic";
|
import {readyToGoSaveApi} from "@/api/base/assesment";
|
||||||
import {deptFindAllGradeClassApi, gradeClassFindAllsApi, readyToGoSaveApi} from "@/api/base/assesment";
|
|
||||||
import {navigateBack, showToast} from "@/utils/uniapp";
|
import {navigateBack, showToast} from "@/utils/uniapp";
|
||||||
import {useDataStore} from "@/store/modules/data";
|
import {useDataStore} from "@/store/modules/data";
|
||||||
|
import {BasicNjBjPicker} from "@/components/BasicNjBjPicker";
|
||||||
|
import { nextTick } from 'vue';
|
||||||
|
|
||||||
const {getData} = useDataStore()
|
const {getData} = useDataStore()
|
||||||
|
|
||||||
|
|
||||||
const classData: any = ref([]);
|
|
||||||
getClassData();
|
|
||||||
|
|
||||||
async function getClassData() {
|
|
||||||
let res = await gradeClassFindAllsApi();
|
|
||||||
classData.value = res.result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function reloadGrade(params) {
|
|
||||||
let arr = [];
|
|
||||||
for (let i = 0; i < classData.value.length; i++) {
|
|
||||||
if (classData.value[i].grade == params.grade) {
|
|
||||||
arr.push(classData.value[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return arr;
|
|
||||||
}
|
|
||||||
|
|
||||||
const isDisabled = ref(false)
|
const isDisabled = ref(false)
|
||||||
|
const studentPickerKey = ref(0) // 用于强制重新渲染学生选择器
|
||||||
|
const studentPickerRef = ref<any>(null) // 学生选择器组件引用
|
||||||
|
|
||||||
|
// 处理学生选择器请求班级信息
|
||||||
|
const handleGetClassInfo = async () => {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// 监听学生选择器的点击事件
|
||||||
|
const handleStudentPickerClick = async () => {
|
||||||
|
console.log('🖱️ 监听到学生选择器点击事件');
|
||||||
|
await handleGetClassInfo();
|
||||||
|
};
|
||||||
|
|
||||||
const [register, {getValue, setSchema, setValue, setDisabled}] = useForm({
|
const [register, {getValue, setSchema, setValue, setDisabled}] = useForm({
|
||||||
schema: [
|
schema: [
|
||||||
{
|
{
|
||||||
@ -56,47 +52,32 @@ const [register, {getValue, setSchema, setValue, setDisabled}] = useForm({
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
field: "grade",
|
field: "classInfo",
|
||||||
label: "年级",
|
label: "年级班级",
|
||||||
component: "BasicPickerCheckbox",
|
component: "BasicNjBjPicker",
|
||||||
required: false,
|
required: false,
|
||||||
ifShow(item) {
|
ifShow(item) {
|
||||||
return getData._show;
|
return getData._show;
|
||||||
},
|
},
|
||||||
componentProps: {
|
componentProps: {
|
||||||
api: deptFindByPidApi,
|
placeholder: "请选择年级班级",
|
||||||
param: {pid: 1},
|
customStyle: {
|
||||||
rangeKey: 'deptName',
|
backgroundColor: '#fff',
|
||||||
savaKey: 'id',
|
borderRadius: '8px',
|
||||||
ok: async (ysave: any, svalue: any, range: any) => {
|
padding: '12px 15px'
|
||||||
let res = await deptFindByPidApi({pid: svalue});
|
|
||||||
console.log(222, res)
|
|
||||||
setSchema([{
|
|
||||||
field: "gradeClassId",
|
|
||||||
componentProps: {
|
|
||||||
range: res.result,
|
|
||||||
rangeKey: 'deptName',
|
|
||||||
savaKey: 'id'
|
|
||||||
}
|
|
||||||
}])
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
field: "gradeClassId",
|
field: "studentInfo",
|
||||||
label: "班级",
|
|
||||||
component: "BasicPickerCheckbox",
|
|
||||||
required: false,
|
|
||||||
componentProps: {
|
|
||||||
api: deptFindAllGradeClassApi,
|
|
||||||
rangeKey: 'deptName',
|
|
||||||
savaKey: 'id'
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
field: "studentName",
|
|
||||||
label: "学生",
|
label: "学生",
|
||||||
component: "BasicInput",
|
component: "BasicXsPicker",
|
||||||
required: false,
|
required: false,
|
||||||
componentProps: {}
|
componentProps: {
|
||||||
|
placeholder: "请选择学生",
|
||||||
|
key: studentPickerKey.value,
|
||||||
|
ref: studentPickerRef,
|
||||||
|
onClick: handleStudentPickerClick
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: "content",
|
field: "content",
|
||||||
@ -175,6 +156,79 @@ const [register, {getValue, setSchema, setValue, setDisabled}] = useForm({
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 延迟启动监听,确保表单完全初始化
|
||||||
|
setTimeout(() => {
|
||||||
|
|
||||||
|
// 使用 ref 来存储班级信息
|
||||||
|
const classInfo = ref(null);
|
||||||
|
|
||||||
|
// 定期检查班级信息变化
|
||||||
|
const checkClassInfo = async () => {
|
||||||
|
try {
|
||||||
|
const formValue = await getValue(false); // 不验证,只获取值
|
||||||
|
const newClassInfo = formValue?.classInfo;
|
||||||
|
|
||||||
|
if (JSON.stringify(newClassInfo) !== JSON.stringify(classInfo.value)) {
|
||||||
|
classInfo.value = newClassInfo;
|
||||||
|
|
||||||
|
if (newClassInfo && newClassInfo.njId && newClassInfo.bjId) {
|
||||||
|
|
||||||
|
// 先清空学生选择器的值
|
||||||
|
setValue({ studentInfo: null });
|
||||||
|
|
||||||
|
// 强制重新渲染学生选择器
|
||||||
|
studentPickerKey.value++;
|
||||||
|
|
||||||
|
// 更新学生选择器的配置
|
||||||
|
setSchema([{
|
||||||
|
field: 'studentInfo',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: "请选择学生",
|
||||||
|
njId: newClassInfo.njId,
|
||||||
|
bjId: newClassInfo.bjId,
|
||||||
|
key: studentPickerKey.value // 添加 key 强制重新渲染
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
|
||||||
|
// 使用 nextTick 确保组件更新
|
||||||
|
nextTick(() => {
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
|
||||||
|
setValue({ studentInfo: null });
|
||||||
|
setSchema([{
|
||||||
|
field: 'studentInfo',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: "请先选择年级班级"
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log('⚠️ 获取表单值失败:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 立即检查一次
|
||||||
|
checkClassInfo();
|
||||||
|
|
||||||
|
// 定期检查(每500ms)
|
||||||
|
const interval = setInterval(checkClassInfo, 500);
|
||||||
|
|
||||||
|
// 组件卸载时清理
|
||||||
|
const cleanup = () => {
|
||||||
|
clearInterval(interval);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 在组件卸载时清理
|
||||||
|
if (typeof onUnmounted === 'function') {
|
||||||
|
onUnmounted(cleanup);
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
|
||||||
if (/\.(jpg|jpeg|png|gif|bmp|webp|tiff|svg)$/i.test(getData.pic)) {
|
if (/\.(jpg|jpeg|png|gif|bmp|webp|tiff|svg)$/i.test(getData.pic)) {
|
||||||
getData['scfj'] = '照片'
|
getData['scfj'] = '照片'
|
||||||
setSchema([{
|
setSchema([{
|
||||||
@ -195,9 +249,41 @@ if (/\.(mp4|avi|mov|wmv|flv|mkv|webm)$/i.test(getData.pic)) {
|
|||||||
}
|
}
|
||||||
}])
|
}])
|
||||||
}
|
}
|
||||||
|
// 处理传入的数据,将 njId 和 bjId 转换为 classInfo 格式,将 xsId 转换为 studentInfo 格式
|
||||||
|
const processFormData = (data: any) => {
|
||||||
|
if (data.njId && data.bjId) {
|
||||||
|
// 如果有 njId 和 bjId,构造 classInfo 对象
|
||||||
|
data.classInfo = {
|
||||||
|
njId: data.njId,
|
||||||
|
bjId: data.bjId,
|
||||||
|
nj: { key: data.njId, title: data.njmc || '' },
|
||||||
|
bj: { key: data.bjId, title: data.bjmc || '' },
|
||||||
|
njmc: data.njmc || '',
|
||||||
|
bjmc: data.bjmc || '',
|
||||||
|
njmcId: data.njmcId || '' // 添加 njmcId 字段
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.xsId) {
|
||||||
|
// 如果有 xsId,构造 studentInfo 对象
|
||||||
|
data.studentInfo = {
|
||||||
|
xsId: data.xsId,
|
||||||
|
xsxm: data.xsxm || data.studentName || '', // 兼容 xsxm 和 studentName
|
||||||
|
studentName: data.studentName || data.xsxm || '', // 优先使用 studentName
|
||||||
|
njId: data.njId || '',
|
||||||
|
bjId: data.bjId || '',
|
||||||
|
njmc: data.njmc || '',
|
||||||
|
bjmc: data.bjmc || ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
if (getData._show) {
|
if (getData._show) {
|
||||||
|
const processedData = processFormData(getData);
|
||||||
setValue({})
|
setValue({})
|
||||||
setValue(getData)
|
setValue(processedData)
|
||||||
setDisabled(true)
|
setDisabled(true)
|
||||||
isDisabled.value = true
|
isDisabled.value = true
|
||||||
} else {
|
} else {
|
||||||
@ -206,8 +292,35 @@ if (getData._show) {
|
|||||||
isDisabled.value = false;
|
isDisabled.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async function submit() {
|
async function submit() {
|
||||||
let values = await getValue();
|
let values = await getValue();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 处理 classInfo 数据,提取 njId、bjId 和 njmcId
|
||||||
|
if (values.classInfo) {
|
||||||
|
values.njId = values.classInfo.njId;
|
||||||
|
values.bjId = values.classInfo.bjId;
|
||||||
|
values.njmc = values.classInfo.njmc;
|
||||||
|
values.bjmc = values.classInfo.bjmc;
|
||||||
|
values.njmcId = values.classInfo.njmcId; // 添加 njmcId 字段
|
||||||
|
|
||||||
|
// 删除 classInfo 字段,避免提交到后端
|
||||||
|
delete values.classInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理 studentInfo 数据,提取 xsId 和 studentName
|
||||||
|
if (values.studentInfo) {
|
||||||
|
values.xsId = values.studentInfo.xsId || values.studentInfo.id; // 兼容两种字段名
|
||||||
|
values.studentName = values.studentInfo.studentName || values.studentInfo.xsxm || values.studentInfo.xm; // 优先使用 studentName
|
||||||
|
|
||||||
|
// 删除 studentInfo 字段,避免提交到后端
|
||||||
|
delete values.studentInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
await readyToGoSaveApi({...values});
|
await readyToGoSaveApi({...values});
|
||||||
showToast({title: "操作成功"});
|
showToast({title: "操作成功"});
|
||||||
navigateBack({delta: 1});
|
navigateBack({delta: 1});
|
||||||
|
|||||||
@ -46,8 +46,8 @@ const [register, lhkh] = useLayout({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
console.log(111, getData.value)
|
console.log(111, getData.value)
|
||||||
if (getData.value.gradeClassId) {
|
if (getData.value.bjId) {
|
||||||
lhkh.setParam({gradeClassId: getData.value.gradeClassId})
|
lhkh.setParam({bjId: getData.value.bjId})
|
||||||
lhkh.reload()
|
lhkh.reload()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,29 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="w-full h-full main_bg flex-col">
|
<view class="w-full h-full flex-col">
|
||||||
<view class="p-15">
|
|
||||||
<view class="flex-row items-center justify-between">
|
|
||||||
<view class="flex-row items-center">
|
|
||||||
<view class="wi-120 he-120 r-50">
|
|
||||||
<image class="wh-full"
|
|
||||||
src="/static/base/logo.jpg"></image>
|
|
||||||
</view>
|
|
||||||
<view class="white-color ml-15">
|
|
||||||
<view class="font-18">你好,{{ getUser.name }}!</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<view @click="tuichu">
|
|
||||||
<svg t="1726666280409" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
|
||||||
p-id="4480" width="25" height="25">
|
|
||||||
<path
|
|
||||||
d="M512.2 953.1c-99.6 0-199.7-33.3-282.2-101.7C42 695.6 15.9 416 171.7 228s435.5-214.1 623.4-58.3c53.8 44.6 95.2 99.5 123 163.1 7.5 17.2-0.3 37.3-17.6 44.9-17.2 7.5-37.3-0.3-44.9-17.6-23.5-53.8-58.5-100.2-104.1-137.9C592.6 90.3 356 112.5 224.1 271.5 92.3 430.5 114.4 667.1 273.5 799c159 131.8 395.6 109.7 527.5-49.3 19.7-23.8 36.4-49.8 49.6-77.4 8.1-17 28.5-24.1 45.4-16 17 8.1 24.1 28.5 16 45.4-15.6 32.6-35.3 63.4-58.6 91.4-87.4 105.5-213.9 160-341.2 160z"
|
|
||||||
fill="#ffffff" p-id="4481"></path>
|
|
||||||
<path
|
|
||||||
d="M910.9 544.6H440.8c-18.8 0-34.1-15.3-34.1-34.1s15.3-34.1 34.1-34.1h470.1c18.8 0 34.1 15.3 34.1 34.1 0 18.9-15.3 34.1-34.1 34.1z"
|
|
||||||
fill="#ffffff" p-id="4482"></path>
|
|
||||||
</svg>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<view class="flex-1 back-F6F6F6 flex-col">
|
<view class="flex-1 back-F6F6F6 flex-col">
|
||||||
<view class="px-15 pt-15">
|
<view class="px-15 pt-15">
|
||||||
<BasicTitle title="我的应用" :isBorder="false" line/>
|
<BasicTitle title="我的应用" :isBorder="false" line/>
|
||||||
@ -186,7 +162,7 @@ function videoplay(url: string) {
|
|||||||
|
|
||||||
function onlhkh(data: any) {
|
function onlhkh(data: any) {
|
||||||
setData(data)
|
setData(data)
|
||||||
navigateTo('/pages/view/quantitativeAssessment/index/details?gradeClassId=' + data.gradeClassId)
|
navigateTo('/pages/view/quantitativeAssessment/index/details?bjId=' + data.bjId)
|
||||||
}
|
}
|
||||||
|
|
||||||
function totzgg(data: any) {
|
function totzgg(data: any) {
|
||||||
@ -203,16 +179,26 @@ function tuichu() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const thirdId: any = ref('');
|
const thirdId: any = ref('');
|
||||||
|
|
||||||
|
|
||||||
onShow(async () => {
|
onShow(async () => {
|
||||||
|
|
||||||
|
|
||||||
let res = await inspectItemFindAllsApi();
|
let res = await inspectItemFindAllsApi();
|
||||||
inspectItems.value = res.result;
|
inspectItems.value = res.result;
|
||||||
await lhkh.reload()
|
await lhkh.reload()
|
||||||
|
|
||||||
|
|
||||||
let userres = await getUserViewApi();
|
let userres = await getUserViewApi();
|
||||||
|
|
||||||
|
|
||||||
if (userres && userres.result) {
|
if (userres && userres.result) {
|
||||||
thirdId.value = userres.result.thirdId ? userres.result.thirdId : "";
|
thirdId.value = userres.result.thirdId ? userres.result.thirdId : "";
|
||||||
} else {
|
} else {
|
||||||
thirdId.value = "";
|
thirdId.value = "";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@ -240,14 +226,21 @@ const [sspregister, ssp] = useLayout({
|
|||||||
})
|
})
|
||||||
|
|
||||||
async function tokhpj() {
|
async function tokhpj() {
|
||||||
|
|
||||||
|
|
||||||
if (thirdId.value) {
|
if (thirdId.value) {
|
||||||
|
|
||||||
let res = await getSchedulingPlanByUserIdApi();
|
let res = await getSchedulingPlanByUserIdApi();
|
||||||
|
|
||||||
if (res.result && res.result.length > 0) {
|
if (res.result && res.result.length > 0) {
|
||||||
|
|
||||||
navigateTo('/pages/view/quantitativeAssessment/assessment/assessment')
|
navigateTo('/pages/view/quantitativeAssessment/assessment/assessment')
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
showToast({title: "今日未分配!无法填报!"})
|
showToast({title: "今日未分配!无法填报!"})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
showToast({title: "未配置权限!"})
|
showToast({title: "未配置权限!"})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -290,9 +283,4 @@ function tabsChange(index: any) {
|
|||||||
|
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.main_bg {
|
|
||||||
//background: #4651fa;
|
|
||||||
background: url("@/static/base/top.png") no-repeat;
|
|
||||||
background-size: 100% 100%;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
BIN
src/static/base/home/stxc.png
Normal file
BIN
src/static/base/home/stxc.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.3 KiB |
BIN
src/static/base/home/yrcg.png
Normal file
BIN
src/static/base/home/yrcg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
Loading…
x
Reference in New Issue
Block a user