选课调整
This commit is contained in:
parent
1598d8e886
commit
e37bd3e02e
@ -15,6 +15,10 @@ export const xqxjFindAllApi = async () => {
|
||||
export const findAllNjBjTreeApi = async () => {
|
||||
return await get("/api/nj/findAllNjBjTree");
|
||||
};
|
||||
export const findAllNjBjKzTreeApi = async () => {
|
||||
return await get("/api/nj/findAllNjBjKzTree");
|
||||
};
|
||||
|
||||
export const kmFindAllApi = async () => {
|
||||
return await get("/api/km/findAll");
|
||||
};
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
<FormBasicDateTimes v-bind="attrs" v-if="isShow('BasicDateTimes')" 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"/>
|
||||
<FormBasicNjBjSelect v-bind="attrs" v-if="isShow('BasicNjBjSelect')" v-model="newValue"/>
|
||||
<FormBasicXsPicker v-bind="attrs" v-if="isShow('BasicXsPicker')" v-model="newValue"/>
|
||||
</view>
|
||||
</template>
|
||||
@ -24,6 +25,7 @@
|
||||
<script setup lang="ts">
|
||||
import {useAttrs} from "vue";
|
||||
import FormBasicNjBjPicker from "@/components/BasicNjBjPicker/index.vue";
|
||||
import FormBasicNjBjSelect from "@/components/BasicNjBjSelect/index.vue";
|
||||
import FormBasicXsPicker from "@/components/BasicXsPicker/index.vue";
|
||||
|
||||
const attrs = useAttrs()
|
||||
|
||||
1
src/components/BasicForm/type/useForm.d.ts
vendored
1
src/components/BasicForm/type/useForm.d.ts
vendored
@ -29,6 +29,7 @@ type Component =
|
||||
| 'BasicDateTimes'
|
||||
| 'BasicTree'
|
||||
| 'BasicNjBjPicker'
|
||||
| 'BasicNjBjSelect'
|
||||
| 'BasicXsPicker'
|
||||
|
||||
interface FormsSchema {
|
||||
|
||||
264
src/components/BasicNjBjSelect/README.md
Normal file
264
src/components/BasicNjBjSelect/README.md
Normal file
@ -0,0 +1,264 @@
|
||||
# BasicNjBjSelect 年级班级选择组件
|
||||
|
||||
一个通用的年级班级树形选择组件,支持多选模式,提供友好的用户界面。
|
||||
|
||||
## 功能特性
|
||||
|
||||
- 🌳 **树形结构**:支持年级和班级的层级结构选择
|
||||
- ✅ **多选模式**:支持选择多个年级和班级
|
||||
- 🔄 **双向绑定**:支持 v-model 双向数据绑定
|
||||
- 🎨 **自定义样式**:支持自定义触发按钮样式
|
||||
- 🚫 **权限控制**:支持选择是否使用权限控制接口
|
||||
- 📱 **响应式设计**:适配移动端界面
|
||||
- ⚡ **异步加载**:支持异步加载年级班级数据
|
||||
- 🔍 **展开折叠**:支持年级节点的展开和折叠
|
||||
|
||||
## 基本用法
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<view class="demo">
|
||||
<BasicNjBjSelect
|
||||
v-model="selectedClass"
|
||||
placeholder="请选择年级班级"
|
||||
@change="onClassChange"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import BasicNjBjSelect from '@/components/BasicNjBjSelect/index.vue'
|
||||
|
||||
const selectedClass = ref(null)
|
||||
|
||||
const onClassChange = (result) => {
|
||||
console.log('选择的班级:', result)
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
## Props
|
||||
|
||||
| 参数 | 类型 | 默认值 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| modelValue | TreeSelectResult \| null | null | 双向绑定的值 |
|
||||
| defaultValue | TreeSelectResult \| null | null | 默认值 |
|
||||
| customStyle | any | {} | 自定义样式 |
|
||||
| iconArrow | string | 'bottom' | 箭头图标类型 |
|
||||
| iconColor | string | '#666' | 箭头颜色 |
|
||||
| placeholder | string | '选择年级班级' | 占位符文本 |
|
||||
| disabled | boolean | false | 是否禁用 |
|
||||
| title | string | '选择班级' | 弹窗标题 |
|
||||
| useKzTree | boolean | false | 是否使用不受权限控制的接口(true: 调用 findAllNjBjKzTreeApi,false: 调用 findAllNjBjTreeApi) |
|
||||
| multiple | boolean | false | 是否支持多选(true: 多选模式,false: 单选模式) |
|
||||
|
||||
## Events
|
||||
|
||||
| 事件名 | 参数 | 说明 |
|
||||
|--------|------|------|
|
||||
| change | (result: TreeSelectResult \| null) | 选择变化时触发 |
|
||||
| update:modelValue | (result: TreeSelectResult \| null) | v-model 更新时触发 |
|
||||
|
||||
## TreeSelectResult 数据结构
|
||||
|
||||
```typescript
|
||||
interface TreeSelectResult {
|
||||
selectedGrades: NjItem[] // 选中的年级列表
|
||||
selectedClasses: BjItem[] // 选中的班级列表
|
||||
allSelected: (NjItem | BjItem)[] // 所有选中的项目
|
||||
}
|
||||
|
||||
interface NjItem {
|
||||
key: string // 年级ID
|
||||
title: string // 年级名称
|
||||
njmcId: string // 年级名称ID
|
||||
children: BjItem[] // 班级列表
|
||||
expanded?: boolean // 是否展开
|
||||
}
|
||||
|
||||
interface BjItem {
|
||||
key: string // 班级ID
|
||||
title: string // 班级名称
|
||||
}
|
||||
```
|
||||
|
||||
## 权限配置
|
||||
|
||||
组件支持两种权限模式:
|
||||
|
||||
### 权限控制模式(默认)
|
||||
- `useKzTree: false`(默认值)
|
||||
- 调用 `findAllNjBjTreeApi` 接口
|
||||
- 根据用户权限返回可访问的年级班级数据
|
||||
- 适用于需要权限控制的场景
|
||||
|
||||
### 无权限控制模式
|
||||
- `useKzTree: true`
|
||||
- 调用 `findAllNjBjKzTreeApi` 接口
|
||||
- 返回所有年级班级数据,不受权限限制
|
||||
- 适用于管理员或需要查看全部数据的场景
|
||||
|
||||
## 选择模式
|
||||
|
||||
组件支持两种选择模式:
|
||||
|
||||
### 单选模式(默认)
|
||||
- `multiple: false`(默认值)
|
||||
- 使用 radio 按钮进行选择
|
||||
- 只能选择一个年级或一个班级
|
||||
- 选择班级时会自动选择对应的年级
|
||||
- 适用于需要精确选择单个班级的场景
|
||||
|
||||
### 多选模式
|
||||
- `multiple: true`
|
||||
- 使用 checkbox 按钮进行选择
|
||||
- 可以选择多个年级和多个班级
|
||||
- 年级和班级可以独立选择
|
||||
- 适用于需要批量选择的场景
|
||||
|
||||
## 方法
|
||||
|
||||
通过 ref 可以调用以下方法:
|
||||
|
||||
| 方法名 | 参数 | 说明 |
|
||||
|--------|------|------|
|
||||
| reset | - | 重置选择 |
|
||||
| setValue | (grades: NjItem[], classes: BjItem[]) | 设置值 |
|
||||
| loadData | - | 重新加载数据 |
|
||||
| openSelector | - | 打开选择器 |
|
||||
| closeSelector | - | 关闭选择器 |
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 单选模式(默认)
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<BasicNjBjSelect
|
||||
v-model="selectedData"
|
||||
:multiple="false"
|
||||
:use-kz-tree="false"
|
||||
placeholder="请选择年级班级"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import BasicNjBjSelect from '@/components/BasicNjBjSelect/index.vue'
|
||||
|
||||
const selectedData = ref(null)
|
||||
</script>
|
||||
```
|
||||
|
||||
### 多选模式
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<BasicNjBjSelect
|
||||
v-model="selectedData"
|
||||
:multiple="true"
|
||||
:use-kz-tree="false"
|
||||
placeholder="请选择年级班级"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import BasicNjBjSelect from '@/components/BasicNjBjSelect/index.vue'
|
||||
|
||||
const selectedData = ref(null)
|
||||
</script>
|
||||
```
|
||||
|
||||
### 无权限控制(单选)
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<BasicNjBjSelect
|
||||
v-model="selectedData"
|
||||
:multiple="false"
|
||||
:use-kz-tree="true"
|
||||
title="选择年级班级(全部)"
|
||||
placeholder="选择全部年级班级"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import BasicNjBjSelect from '@/components/BasicNjBjSelect/index.vue'
|
||||
|
||||
const selectedData = ref(null)
|
||||
</script>
|
||||
```
|
||||
|
||||
### 自定义样式
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<BasicNjBjSelect
|
||||
v-model="selectedData"
|
||||
:custom-style="{
|
||||
backgroundColor: '#f0f9ff',
|
||||
borderColor: '#0ea5e9'
|
||||
}"
|
||||
placeholder="请选择年级班级"
|
||||
/>
|
||||
</template>
|
||||
```
|
||||
|
||||
### 使用不受权限控制的接口
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<BasicNjBjSelect
|
||||
v-model="selectedData"
|
||||
:use-kz-tree="true"
|
||||
title="选择年级班级(全部)"
|
||||
/>
|
||||
</template>
|
||||
```
|
||||
|
||||
### 禁用状态
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<BasicNjBjSelect
|
||||
v-model="selectedData"
|
||||
:disabled="true"
|
||||
/>
|
||||
</template>
|
||||
```
|
||||
|
||||
### 监听选择变化
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<BasicNjBjSelect
|
||||
v-model="selectedData"
|
||||
@change="onSelectionChange"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const onSelectionChange = (result) => {
|
||||
if (result) {
|
||||
console.log('选中的年级数量:', result.selectedGrades.length)
|
||||
console.log('选中的班级数量:', result.selectedClasses.length)
|
||||
console.log('选中的年级:', result.selectedGrades)
|
||||
console.log('选中的班级:', result.selectedClasses)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 组件会自动加载年级班级数据,无需手动调用
|
||||
2. 如果使用 `useKzTree` 为 true,将调用不受权限控制的接口
|
||||
3. 组件支持 v-model 双向绑定
|
||||
4. 选择结果包含完整的年级班级信息,便于后续处理
|
||||
5. 年级节点支持展开/折叠,点击年级名称可以切换展开状态
|
||||
6. 选择年级时,会自动选择该年级下的所有班级
|
||||
7. 取消选择年级时,会同时取消选择该年级下的所有班级
|
||||
8. 班级可以独立选择,不受年级选择状态影响
|
||||
149
src/components/BasicNjBjSelect/demo.vue
Normal file
149
src/components/BasicNjBjSelect/demo.vue
Normal file
@ -0,0 +1,149 @@
|
||||
<template>
|
||||
<view class="demo-page">
|
||||
<view class="demo-section">
|
||||
<text class="section-title">基础用法</text>
|
||||
<BasicNjBjSelect
|
||||
v-model="selectedClass1"
|
||||
placeholder="请选择年级班级"
|
||||
@change="onClassChange1"
|
||||
/>
|
||||
<view v-if="selectedClass1" class="result-display">
|
||||
<text>已选择:{{ selectedClass1.njmc }} {{ selectedClass1.bjmc }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="demo-section">
|
||||
<text class="section-title">自定义样式</text>
|
||||
<BasicNjBjSelect
|
||||
v-model="selectedClass2"
|
||||
:custom-style="{
|
||||
backgroundColor: '#f0f9ff',
|
||||
borderColor: '#0ea5e9',
|
||||
borderRadius: '12px'
|
||||
}"
|
||||
placeholder="自定义样式选择器"
|
||||
icon-color="#0ea5e9"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view class="demo-section">
|
||||
<text class="section-title">不受权限控制</text>
|
||||
<BasicNjBjSelect
|
||||
v-model="selectedClass3"
|
||||
:use-kz-tree="true"
|
||||
title="选择年级班级(全部)"
|
||||
placeholder="选择全部年级班级"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view class="demo-section">
|
||||
<text class="section-title">禁用状态</text>
|
||||
<BasicNjBjSelect
|
||||
v-model="selectedClass4"
|
||||
:disabled="true"
|
||||
placeholder="禁用状态"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<view class="demo-section">
|
||||
<text class="section-title">操作按钮</text>
|
||||
<view class="button-group">
|
||||
<button @click="resetAll" class="demo-btn">重置所有</button>
|
||||
<button @click="setDefaultValue" class="demo-btn">设置默认值</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import BasicNjBjSelect from './index.vue'
|
||||
|
||||
// 响应式数据
|
||||
const selectedClass1 = ref(null)
|
||||
const selectedClass2 = ref(null)
|
||||
const selectedClass3 = ref(null)
|
||||
const selectedClass4 = ref(null)
|
||||
|
||||
// 事件处理
|
||||
const onClassChange1 = (result) => {
|
||||
console.log('选择变化:', result)
|
||||
}
|
||||
|
||||
// 重置所有选择
|
||||
const resetAll = () => {
|
||||
selectedClass1.value = null
|
||||
selectedClass2.value = null
|
||||
selectedClass3.value = null
|
||||
selectedClass4.value = null
|
||||
}
|
||||
|
||||
// 设置默认值
|
||||
const setDefaultValue = () => {
|
||||
// 这里可以设置一个默认的年级班级
|
||||
// 注意:需要确保数据存在
|
||||
console.log('设置默认值功能需要根据实际数据调整')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.demo-page {
|
||||
padding: 20px;
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.demo-section {
|
||||
margin-bottom: 30px;
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
|
||||
.section-title {
|
||||
display: block;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.result-display {
|
||||
margin-top: 10px;
|
||||
padding: 10px;
|
||||
background-color: #f0f9ff;
|
||||
border-radius: 6px;
|
||||
border-left: 3px solid #0ea5e9;
|
||||
|
||||
text {
|
||||
font-size: 14px;
|
||||
color: #0369a1;
|
||||
}
|
||||
}
|
||||
|
||||
.button-group {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-top: 15px;
|
||||
|
||||
.demo-btn {
|
||||
flex: 1;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
background-color: #1890ff;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
|
||||
&:active {
|
||||
background-color: #1677ff;
|
||||
}
|
||||
|
||||
&::after {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
639
src/components/BasicNjBjSelect/index.vue
Normal file
639
src/components/BasicNjBjSelect/index.vue
Normal file
@ -0,0 +1,639 @@
|
||||
<template>
|
||||
<view class="basic-nj-bj-select">
|
||||
<!-- 选择器触发按钮 -->
|
||||
<view
|
||||
class="select-trigger"
|
||||
:class="{ disabled: disabled }"
|
||||
:style="customStyle"
|
||||
@click="openSelector"
|
||||
>
|
||||
<text class="select-text">{{ displayText || placeholder }}</text>
|
||||
<uni-icons :type="iconArrow" size="14" :color="iconColor"></uni-icons>
|
||||
</view>
|
||||
|
||||
<!-- 选择器弹窗 -->
|
||||
<uni-popup ref="popup" type="bottom" :safe-area="false">
|
||||
<view class="selector-popup">
|
||||
<!-- 头部 -->
|
||||
<view class="popup-header">
|
||||
<text class="popup-title">{{ title }}</text>
|
||||
<view class="popup-actions">
|
||||
<text class="action-btn cancel-btn" @click="closeSelector">取消</text>
|
||||
<text class="action-btn confirm-btn" @click="confirmSelection">确定</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 树形选择器内容 -->
|
||||
<view class="popup-content">
|
||||
<scroll-view scroll-y class="tree-scroll">
|
||||
<checkbox-group v-if="multiple" @change="onGradeCheckboxChange">
|
||||
<view class="tree-container">
|
||||
<view
|
||||
v-for="(nj, njIndex) in njList"
|
||||
:key="nj.key"
|
||||
class="tree-node"
|
||||
>
|
||||
<!-- 年级节点 -->
|
||||
<view
|
||||
class="tree-item grade-item"
|
||||
:class="{ expanded: nj.expanded }"
|
||||
@click="toggleGrade(njIndex)"
|
||||
>
|
||||
<view class="tree-item-content">
|
||||
<view class="expand-icon">
|
||||
<uni-icons
|
||||
:type="nj.expanded ? 'arrowdown' : 'arrowright'"
|
||||
size="12"
|
||||
color="#666"
|
||||
></uni-icons>
|
||||
</view>
|
||||
<text class="grade-text">{{ nj.title }}</text>
|
||||
<checkbox
|
||||
v-if="multiple"
|
||||
:value="nj.key"
|
||||
:checked="isGradeSelected(nj)"
|
||||
class="grade-checkbox"
|
||||
/>
|
||||
<radio
|
||||
v-else
|
||||
:value="nj.key"
|
||||
:checked="isGradeSelected(nj)"
|
||||
class="grade-radio"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 班级节点 -->
|
||||
<view
|
||||
v-if="nj.expanded"
|
||||
class="class-children"
|
||||
>
|
||||
<checkbox-group v-if="multiple" @change="(event: any) => onClassCheckboxChange(nj, event)">
|
||||
<view
|
||||
v-for="(bj, bjIndex) in nj.children"
|
||||
:key="bj.key"
|
||||
class="tree-item class-item"
|
||||
>
|
||||
<view class="tree-item-content">
|
||||
<view class="class-indent"></view>
|
||||
<text class="class-text">{{ bj.title }}</text>
|
||||
<checkbox
|
||||
:value="bj.key"
|
||||
:checked="isClassSelected(bj)"
|
||||
class="class-checkbox"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</checkbox-group>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</checkbox-group>
|
||||
|
||||
<radio-group v-else @change="onGradeRadioChange">
|
||||
<view class="tree-container">
|
||||
<view
|
||||
v-for="(nj, njIndex) in njList"
|
||||
:key="nj.key"
|
||||
class="tree-node"
|
||||
>
|
||||
<!-- 年级节点 -->
|
||||
<view
|
||||
class="tree-item grade-item"
|
||||
:class="{ expanded: nj.expanded }"
|
||||
@click="toggleGrade(njIndex)"
|
||||
>
|
||||
<view class="tree-item-content">
|
||||
<view class="expand-icon">
|
||||
<uni-icons
|
||||
:type="nj.expanded ? 'arrowdown' : 'arrowright'"
|
||||
size="12"
|
||||
color="#666"
|
||||
></uni-icons>
|
||||
</view>
|
||||
<text class="grade-text">{{ nj.title }}</text>
|
||||
<radio
|
||||
:value="nj.key"
|
||||
:checked="isGradeSelected(nj)"
|
||||
class="grade-radio"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 班级节点 -->
|
||||
<view
|
||||
v-if="nj.expanded"
|
||||
class="class-children"
|
||||
>
|
||||
<radio-group @change="onClassRadioChange">
|
||||
<view
|
||||
v-for="(bj, bjIndex) in nj.children"
|
||||
:key="bj.key"
|
||||
class="tree-item class-item"
|
||||
>
|
||||
<view class="tree-item-content">
|
||||
<view class="class-indent"></view>
|
||||
<text class="class-text">{{ bj.title }}</text>
|
||||
<radio
|
||||
:value="bj.key"
|
||||
:checked="isClassSelected(bj)"
|
||||
class="class-radio"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</radio-group>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</radio-group>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, watch, onMounted } from 'vue'
|
||||
import { findAllNjBjTreeApi, findAllNjBjKzTreeApi } from '@/api/base/server'
|
||||
|
||||
// 接口定义
|
||||
interface NjItem {
|
||||
key: string
|
||||
title: string
|
||||
njmcId: string
|
||||
children: BjItem[]
|
||||
expanded?: boolean
|
||||
}
|
||||
|
||||
interface BjItem {
|
||||
key: string
|
||||
title: string
|
||||
}
|
||||
|
||||
interface SelectResult {
|
||||
nj: NjItem
|
||||
bj: BjItem
|
||||
njId: string
|
||||
bjId: string
|
||||
njmc: string
|
||||
bjmc: string
|
||||
njmcId: string
|
||||
}
|
||||
|
||||
interface TreeSelectResult {
|
||||
selectedGrades: NjItem[]
|
||||
selectedClasses: BjItem[]
|
||||
allSelected: (NjItem | BjItem)[]
|
||||
}
|
||||
|
||||
// Props 定义
|
||||
const props = withDefaults(defineProps<{
|
||||
modelValue?: TreeSelectResult | null
|
||||
defaultValue?: TreeSelectResult | null
|
||||
customStyle?: any
|
||||
iconArrow?: string
|
||||
iconColor?: string
|
||||
placeholder?: string
|
||||
disabled?: boolean
|
||||
title?: string
|
||||
useKzTree?: boolean // 是否使用不受权限控制的接口
|
||||
multiple?: boolean // 是否支持多选
|
||||
}>(), {
|
||||
modelValue: null,
|
||||
defaultValue: null,
|
||||
customStyle: {},
|
||||
iconArrow: 'bottom',
|
||||
iconColor: '#666',
|
||||
placeholder: '选择年级班级',
|
||||
disabled: false,
|
||||
title: '选择班级',
|
||||
useKzTree: false,
|
||||
multiple: false
|
||||
})
|
||||
|
||||
// Emits 定义
|
||||
const emit = defineEmits(['change', 'update:modelValue'])
|
||||
|
||||
// 响应式数据
|
||||
const popup = ref()
|
||||
const njList = ref<NjItem[]>([])
|
||||
const selectedGrades = ref<NjItem[]>([])
|
||||
const selectedClasses = ref<BjItem[]>([])
|
||||
const isLoading = ref(false)
|
||||
|
||||
// 计算属性
|
||||
const displayText = computed(() => {
|
||||
if (!props.modelValue) return ''
|
||||
|
||||
const gradeCount = props.modelValue.selectedGrades?.length || 0
|
||||
const classCount = props.modelValue.selectedClasses?.length || 0
|
||||
|
||||
if (props.multiple) {
|
||||
// 多选模式:显示选择数量
|
||||
if (gradeCount > 0 && classCount > 0) {
|
||||
return `已选择 ${gradeCount} 个年级,${classCount} 个班级`
|
||||
} else if (classCount > 0) {
|
||||
return `已选择 ${classCount} 个班级`
|
||||
} else if (gradeCount > 0) {
|
||||
return `已选择 ${gradeCount} 个年级`
|
||||
}
|
||||
} else {
|
||||
// 单选模式:显示具体选择的年级班级
|
||||
if (classCount > 0) {
|
||||
const selectedClass = props.modelValue.selectedClasses[0]
|
||||
const selectedGrade = props.modelValue.selectedGrades[0]
|
||||
return `${selectedGrade?.title || ''} ${selectedClass.title}`
|
||||
} else if (gradeCount > 0) {
|
||||
return props.modelValue.selectedGrades[0].title
|
||||
}
|
||||
}
|
||||
|
||||
return ''
|
||||
})
|
||||
|
||||
// 监听 modelValue 变化
|
||||
watch(() => props.modelValue, (newVal) => {
|
||||
if (newVal) {
|
||||
selectedGrades.value = newVal.selectedGrades || []
|
||||
selectedClasses.value = newVal.selectedClasses || []
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
// 切换年级展开/折叠
|
||||
const toggleGrade = (njIndex: number) => {
|
||||
if (props.disabled) return
|
||||
njList.value[njIndex].expanded = !njList.value[njIndex].expanded
|
||||
}
|
||||
|
||||
// 检查年级是否被选中
|
||||
const isGradeSelected = (nj: NjItem) => {
|
||||
return selectedGrades.value.some(grade => grade.key === nj.key)
|
||||
}
|
||||
|
||||
// 检查班级是否被选中
|
||||
const isClassSelected = (bj: BjItem) => {
|
||||
return selectedClasses.value.some(cls => cls.key === bj.key)
|
||||
}
|
||||
|
||||
// 年级选择变化(多选模式)
|
||||
const onGradeCheckboxChange = (event: any) => {
|
||||
if (props.disabled) return
|
||||
|
||||
const selectedValues = event.detail.value || []
|
||||
|
||||
// 更新选中的年级
|
||||
selectedGrades.value = njList.value.filter(nj =>
|
||||
selectedValues.includes(nj.key)
|
||||
)
|
||||
|
||||
// 更新选中的班级(只保留选中年级下的班级)
|
||||
selectedClasses.value = selectedClasses.value.filter(cls =>
|
||||
selectedGrades.value.some(grade =>
|
||||
grade.children.some(child => child.key === cls.key)
|
||||
)
|
||||
)
|
||||
|
||||
updateModelValue()
|
||||
}
|
||||
|
||||
// 年级单选变化
|
||||
const onGradeRadioChange = (event: any) => {
|
||||
if (props.disabled) return
|
||||
|
||||
const selectedValue = event.detail.value
|
||||
const selectedGrade = njList.value.find(nj => nj.key === selectedValue)
|
||||
|
||||
if (selectedGrade) {
|
||||
selectedGrades.value = [selectedGrade]
|
||||
selectedClasses.value = []
|
||||
} else {
|
||||
selectedGrades.value = []
|
||||
selectedClasses.value = []
|
||||
}
|
||||
|
||||
updateModelValue()
|
||||
}
|
||||
|
||||
// 班级选择变化(多选模式)
|
||||
const onClassCheckboxChange = (nj: NjItem, event: any) => {
|
||||
if (props.disabled) return
|
||||
|
||||
const selectedValues = event.detail.value || []
|
||||
|
||||
// 更新该年级下的班级选择
|
||||
const currentClassKeys = nj.children.map(bj => bj.key)
|
||||
const selectedClassKeys = selectedValues.filter((key: string) => currentClassKeys.includes(key))
|
||||
|
||||
// 移除该年级下之前选中的班级
|
||||
selectedClasses.value = selectedClasses.value.filter(cls =>
|
||||
!currentClassKeys.includes(cls.key)
|
||||
)
|
||||
|
||||
// 添加新选中的班级
|
||||
selectedClassKeys.forEach((key: string) => {
|
||||
const bj = nj.children.find(bj => bj.key === key)
|
||||
if (bj) {
|
||||
selectedClasses.value.push(bj)
|
||||
}
|
||||
})
|
||||
|
||||
updateModelValue()
|
||||
}
|
||||
|
||||
// 班级单选变化
|
||||
const onClassRadioChange = (event: any) => {
|
||||
if (props.disabled) return
|
||||
|
||||
const selectedValue = event.detail.value
|
||||
|
||||
// 查找选中的班级及其所属年级
|
||||
for (const nj of njList.value) {
|
||||
const selectedClass = nj.children.find(bj => bj.key === selectedValue)
|
||||
if (selectedClass) {
|
||||
selectedClasses.value = [selectedClass]
|
||||
selectedGrades.value = [nj]
|
||||
updateModelValue()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有找到,清空选择
|
||||
selectedClasses.value = []
|
||||
selectedGrades.value = []
|
||||
updateModelValue()
|
||||
}
|
||||
|
||||
// 更新模型值
|
||||
const updateModelValue = () => {
|
||||
const result: TreeSelectResult = {
|
||||
selectedGrades: [...selectedGrades.value],
|
||||
selectedClasses: [...selectedClasses.value],
|
||||
allSelected: [...selectedGrades.value, ...selectedClasses.value]
|
||||
}
|
||||
|
||||
console.log('BasicNjBjSelect updateModelValue:', result)
|
||||
emit('change', result)
|
||||
emit('update:modelValue', result)
|
||||
}
|
||||
|
||||
// 打开选择器
|
||||
const openSelector = () => {
|
||||
if (props.disabled) return
|
||||
popup.value?.open()
|
||||
}
|
||||
|
||||
// 关闭选择器
|
||||
const closeSelector = () => {
|
||||
popup.value?.close()
|
||||
}
|
||||
|
||||
// 确认选择
|
||||
const confirmSelection = () => {
|
||||
updateModelValue()
|
||||
closeSelector()
|
||||
}
|
||||
|
||||
// 加载数据
|
||||
const loadData = async () => {
|
||||
if (isLoading.value) return
|
||||
|
||||
isLoading.value = true
|
||||
try {
|
||||
const api = props.useKzTree ? findAllNjBjKzTreeApi : findAllNjBjTreeApi
|
||||
const res = await api()
|
||||
const data = res.result || []
|
||||
|
||||
// 为每个年级添加展开状态
|
||||
njList.value = data.map((nj: NjItem) => ({
|
||||
...nj,
|
||||
expanded: false
|
||||
}))
|
||||
|
||||
// 如果有默认值,设置默认选择
|
||||
if (props.defaultValue) {
|
||||
selectedGrades.value = props.defaultValue.selectedGrades || []
|
||||
selectedClasses.value = props.defaultValue.selectedClasses || []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载年级班级数据失败:', error)
|
||||
uni.showToast({
|
||||
title: '加载数据失败',
|
||||
icon: 'none'
|
||||
})
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 重置
|
||||
const reset = () => {
|
||||
selectedGrades.value = []
|
||||
selectedClasses.value = []
|
||||
emit('change', null)
|
||||
emit('update:modelValue', null)
|
||||
}
|
||||
|
||||
// 设置值
|
||||
const setValue = (grades: NjItem[], classes: BjItem[]) => {
|
||||
selectedGrades.value = [...grades]
|
||||
selectedClasses.value = [...classes]
|
||||
updateModelValue()
|
||||
}
|
||||
|
||||
// 暴露方法
|
||||
defineExpose({
|
||||
reset,
|
||||
setValue,
|
||||
loadData,
|
||||
openSelector,
|
||||
closeSelector
|
||||
})
|
||||
|
||||
// 组件挂载时加载数据
|
||||
onMounted(() => {
|
||||
loadData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.basic-nj-bj-select {
|
||||
.select-trigger {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 12px 15px;
|
||||
background-color: #f7f7f7;
|
||||
border: 1px solid #eee;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
transition: all 0.3s ease;
|
||||
min-height: 44px;
|
||||
|
||||
&:active:not(.disabled) {
|
||||
background-color: #e6f7ff;
|
||||
border-color: #1890ff;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
opacity: 0.6;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.select-text {
|
||||
flex: 1;
|
||||
margin-right: 8px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.selector-popup {
|
||||
background-color: #fff;
|
||||
border-radius: 16px 16px 0 0;
|
||||
max-height: 60vh;
|
||||
|
||||
.popup-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 15px 20px;
|
||||
border-bottom: 1px solid #eee;
|
||||
|
||||
.popup-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.popup-actions {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
|
||||
.action-btn {
|
||||
font-size: 14px;
|
||||
padding: 4px 8px;
|
||||
|
||||
&.cancel-btn {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
&.confirm-btn {
|
||||
color: #1890ff;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.popup-content {
|
||||
.tree-scroll {
|
||||
height: 600px;
|
||||
background-color: #fff;
|
||||
|
||||
.tree-container {
|
||||
padding: 0;
|
||||
|
||||
.tree-node {
|
||||
.tree-item {
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&.grade-item {
|
||||
background-color: #f8f9fa;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
|
||||
.tree-item-content {
|
||||
padding: 5px 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: 30px;
|
||||
|
||||
.expand-icon {
|
||||
margin-right: 5px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.grade-text {
|
||||
flex: 1;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.grade-checkbox {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.class-item {
|
||||
background-color: #fff;
|
||||
|
||||
.tree-item-content {
|
||||
padding: 3px 12px 3px 26px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: 26px;
|
||||
|
||||
.class-indent {
|
||||
width: 10px;
|
||||
height: 1px;
|
||||
background-color: #e0e0e0;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.class-text {
|
||||
flex: 1;
|
||||
font-size: 13px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.class-checkbox {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
}
|
||||
|
||||
.class-children {
|
||||
background-color: #fff;
|
||||
|
||||
.tree-item {
|
||||
border-bottom: none;
|
||||
margin-bottom: 0;
|
||||
|
||||
&:not(:last-child) {
|
||||
border-bottom: 1px solid #f5f5f5;
|
||||
}
|
||||
|
||||
.tree-item-content {
|
||||
padding: 2px 12px 2px 26px;
|
||||
min-height: 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -105,7 +105,7 @@ import {onShow} from "@dcloudio/uni-app";
|
||||
import {deptFindByPidApi, dicApi} from "@/api/system/dic";
|
||||
import {useForm} from "@/components/BasicForm/hooks/useForm";
|
||||
import {getUserViewApi} from "@/api/system/login";
|
||||
import {BasicNjBjPicker} from "@/components/BasicNjBjPicker";
|
||||
import BasicNjBjSelect from "@/components/BasicNjBjSelect/index.vue";
|
||||
|
||||
function setItemValue() {
|
||||
console.log(444,value.value)
|
||||
@ -125,14 +125,18 @@ const [register, {getValue, setSchema, setValue}] = useForm({
|
||||
{
|
||||
field: "classInfo",
|
||||
label: "年级班级",
|
||||
component: "BasicNjBjPicker",
|
||||
component: "BasicNjBjSelect",
|
||||
required: true,
|
||||
defaultValue: null,
|
||||
componentProps: {
|
||||
placeholder: "请选择年级班级",
|
||||
useKzTree: true, // 使用权限控制的接口
|
||||
multiple: false, // 单选模式
|
||||
customStyle: {
|
||||
backgroundColor: '#fff',
|
||||
borderRadius: '8px',
|
||||
padding: '12px 15px'
|
||||
padding: '8px 12px',
|
||||
minHeight: '36px'
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -167,14 +171,14 @@ function radioChange() {
|
||||
console.log(222, value.value)
|
||||
}
|
||||
|
||||
function changeRuleItem(e) {
|
||||
function changeRuleItem(e: any) {
|
||||
value.value = e.detail.value;
|
||||
}
|
||||
|
||||
async function getInspectItemData() {
|
||||
let res = await inspectItemFindAllsApi();
|
||||
if (res && res.result && res.result.length > 0) {
|
||||
inspectItemData.value = res.result.filter(item => authItemIds.value.includes(item.id));
|
||||
inspectItemData.value = res.result.filter((item: any) => authItemIds.value.includes(item.id));
|
||||
avtion.value = 0;
|
||||
getFractionRuleData();
|
||||
} else {
|
||||
@ -182,7 +186,7 @@ async function getInspectItemData() {
|
||||
}
|
||||
}
|
||||
|
||||
function onavtion(item, index) {
|
||||
function onavtion(item: any, index: number) {
|
||||
avtion.value = index;
|
||||
console.log(111, fractionRuleData.value)
|
||||
getFractionRuleData();
|
||||
@ -223,7 +227,7 @@ async function onavtionList(item: any, index: any) {
|
||||
showpopup.value = true;
|
||||
}
|
||||
|
||||
inspectItemData.value[avtion.value]._rulenum = fractionRuleData.value[avtion.value].filter(item => item.isSelect).length;
|
||||
inspectItemData.value[avtion.value]._rulenum = fractionRuleData.value[avtion.value].filter((item: any) => item.isSelect).length;
|
||||
|
||||
}
|
||||
|
||||
@ -254,16 +258,21 @@ async function submit() {
|
||||
return;
|
||||
}
|
||||
let values = await getValue();
|
||||
console.log('assessment.vue getValue:', values);
|
||||
|
||||
// 处理 classInfo 数据,提取 bjId
|
||||
if (values.classInfo && values.classInfo.bjId) {
|
||||
if (values.classInfo && values.classInfo.selectedClasses && values.classInfo.selectedClasses.length > 0) {
|
||||
// 单选模式:取第一个选中的班级
|
||||
const selectedClass = values.classInfo.selectedClasses[0];
|
||||
console.log('selectedClass:', selectedClass);
|
||||
await evaluationSaveApi({
|
||||
itemList: itemList,
|
||||
bjId: values.classInfo.bjId
|
||||
bjId: selectedClass.key
|
||||
});
|
||||
showToast({title: "操作成功"});
|
||||
navigateBack({delta: 1})
|
||||
} else {
|
||||
console.log('classInfo:', values.classInfo);
|
||||
showToast({title: "请选择班级"});
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
<view class="font-w-500 flex-row">
|
||||
<view class="mr-5">{{ data.fullName }}</view>
|
||||
<view class="mr-5">{{ data.score }}分</view>
|
||||
本周得分{{ data.score ? (100 + parseFloat(data.score)) : 100 }}
|
||||
本周得分{{ data.score ? (300 + parseFloat(data.score)) : 300 }}
|
||||
</view>
|
||||
<view class="grid gridCols-4 mt-15 gapY-15">
|
||||
<template v-for="(item,key) in inspectItems">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user