Compare commits
No commits in common. "d28b68ef4613db4ce25b86aa69db5f1b4d8bc793" and "7e327e2131366367c097744744190a44736a4de8" have entirely different histories.
d28b68ef46
...
7e327e2131
253
backend/build.sh
253
backend/build.sh
|
|
@ -1,253 +0,0 @@
|
|||
#!/bin/bash
|
||||
# ============================================================
|
||||
# 脚本名称:build.sh
|
||||
# 功能说明:快速编译打包后端项目,支持多线程、增量、模块化编译
|
||||
# 作 者:湖北新华业务中台研发团队
|
||||
# 创建时间:2026-02-02
|
||||
# ============================================================
|
||||
#
|
||||
# 【优化策略】
|
||||
# - 多线程编译:-T 1C 使用全部CPU核心并行编译
|
||||
# - 增量编译:不执行 clean,复用已编译的 class 文件
|
||||
# - 模块化编译:-pl :module -am 仅编译指定模块及其依赖
|
||||
# - 静默模式:-q 减少日志输出,加快速度
|
||||
# - JVM优化:-XX:TieredStopAtLevel=1 编译器快速模式
|
||||
#
|
||||
# 【使用方法】
|
||||
# ./build.sh [选项]
|
||||
#
|
||||
# 【选项说明】
|
||||
# -q, --quick 快速编译(仅 compile,检查语法错误,最快)
|
||||
# -p, --package 增量打包(package,不 clean,默认选项)
|
||||
# -f, --full 全量编译(clean + package)
|
||||
# -m, --module 指定模块编译(如: -m pangu-business)
|
||||
# -r, --restart 编译后重启后端服务
|
||||
# -c, --clean 仅清理 target 目录
|
||||
# -h, --help 显示帮助信息
|
||||
#
|
||||
# 【常用示例】
|
||||
# ./build.sh -q # 快速编译,仅检查语法(约4秒)
|
||||
# ./build.sh -p # 增量打包(约4秒)
|
||||
# ./build.sh -f # 全量编译(约30秒)
|
||||
# ./build.sh -m pangu-business # 仅编译业务模块
|
||||
# ./build.sh -m pangu-business -r # 编译业务模块并重启(约27秒)
|
||||
# ./build.sh -r # 增量打包并重启
|
||||
#
|
||||
# 【开发建议】
|
||||
# 1. 日常开发:修改代码后用 -q 快速检查语法错误
|
||||
# 2. 本地测试:用 -r 编译并重启服务
|
||||
# 3. 仅改某模块:用 -m 指定模块,加快编译速度
|
||||
# 4. 部署前:用 -f 全量编译确保干净
|
||||
#
|
||||
# ============================================================
|
||||
set -e
|
||||
|
||||
# 颜色定义
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 项目根目录
|
||||
PROJECT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
# Maven 通用参数
|
||||
MAVEN_OPTS="-Xmx1024m -XX:+TieredCompilation -XX:TieredStopAtLevel=1"
|
||||
# 多线程编译(CPU核心数)
|
||||
THREADS="1C"
|
||||
# 通用参数:跳过测试、静默模式
|
||||
COMMON_ARGS="-DskipTests -q"
|
||||
|
||||
# 打印带颜色的消息
|
||||
print_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||
print_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
|
||||
print_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||
print_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||
|
||||
# 显示帮助信息
|
||||
show_help() {
|
||||
echo "用法: ./build.sh [选项]"
|
||||
echo ""
|
||||
echo "选项:"
|
||||
echo " -f, --full 全量编译(clean + package)"
|
||||
echo " -q, --quick 快速编译(仅 compile,最快)"
|
||||
echo " -p, --package 打包(package,不 clean)"
|
||||
echo " -m, --module 指定模块编译(如: -m pangu-business)"
|
||||
echo " -r, --restart 编译并重启后端服务"
|
||||
echo " -c, --clean 仅清理"
|
||||
echo " -h, --help 显示帮助信息"
|
||||
echo ""
|
||||
echo "示例:"
|
||||
echo " ./build.sh -q # 快速编译检查语法"
|
||||
echo " ./build.sh -p # 增量打包"
|
||||
echo " ./build.sh -f # 全量编译"
|
||||
echo " ./build.sh -m pangu-business -r # 编译业务模块并重启"
|
||||
echo " ./build.sh -r # 全量编译并重启"
|
||||
}
|
||||
|
||||
# 快速编译(仅 compile)
|
||||
quick_compile() {
|
||||
local module=$1
|
||||
print_info "快速编译中(仅检查语法)..."
|
||||
local start_time=$(date +%s)
|
||||
|
||||
if [ -n "$module" ]; then
|
||||
print_info "目标模块: $module"
|
||||
mvn compile -T $THREADS $COMMON_ARGS -pl :$module -am
|
||||
else
|
||||
mvn compile -T $THREADS $COMMON_ARGS
|
||||
fi
|
||||
|
||||
local end_time=$(date +%s)
|
||||
print_success "编译完成,耗时: $((end_time - start_time)) 秒"
|
||||
}
|
||||
|
||||
# 增量打包(不 clean)
|
||||
incremental_package() {
|
||||
local module=$1
|
||||
print_info "增量打包中..."
|
||||
local start_time=$(date +%s)
|
||||
|
||||
if [ -n "$module" ]; then
|
||||
print_info "目标模块: $module"
|
||||
mvn package -T $THREADS $COMMON_ARGS -pl :$module -am
|
||||
else
|
||||
mvn package -T $THREADS $COMMON_ARGS
|
||||
fi
|
||||
|
||||
local end_time=$(date +%s)
|
||||
print_success "打包完成,耗时: $((end_time - start_time)) 秒"
|
||||
}
|
||||
|
||||
# 全量编译
|
||||
full_build() {
|
||||
print_info "全量编译中(clean + package)..."
|
||||
local start_time=$(date +%s)
|
||||
|
||||
mvn clean package -T $THREADS $COMMON_ARGS
|
||||
|
||||
local end_time=$(date +%s)
|
||||
print_success "全量编译完成,耗时: $((end_time - start_time)) 秒"
|
||||
}
|
||||
|
||||
# 仅清理
|
||||
clean_only() {
|
||||
print_info "清理中..."
|
||||
mvn clean -q
|
||||
print_success "清理完成"
|
||||
}
|
||||
|
||||
# 重启后端服务
|
||||
restart_backend() {
|
||||
print_info "重启后端服务..."
|
||||
|
||||
# 停止当前运行的服务
|
||||
pkill -f "ruoyi-admin.jar" 2>/dev/null || true
|
||||
sleep 2
|
||||
|
||||
# 检查 jar 包是否存在
|
||||
local jar_file="$PROJECT_DIR/ruoyi-admin/target/ruoyi-admin.jar"
|
||||
if [ ! -f "$jar_file" ]; then
|
||||
print_error "找不到 jar 包: $jar_file"
|
||||
print_warn "请先运行 ./build.sh -p 或 ./build.sh -f 进行打包"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 启动服务
|
||||
nohup java -jar "$jar_file" > /tmp/ruoyi-admin.log 2>&1 &
|
||||
print_info "后端启动中,等待服务就绪..."
|
||||
|
||||
# 等待服务启动
|
||||
local retry=0
|
||||
local max_retry=30
|
||||
while [ $retry -lt $max_retry ]; do
|
||||
if lsof -i :8080 >/dev/null 2>&1; then
|
||||
print_success "后端服务启动成功!端口: 8080"
|
||||
return 0
|
||||
fi
|
||||
sleep 1
|
||||
retry=$((retry + 1))
|
||||
done
|
||||
|
||||
print_error "后端服务启动超时,请检查日志: /tmp/ruoyi-admin.log"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 主逻辑
|
||||
main() {
|
||||
local action=""
|
||||
local module=""
|
||||
local need_restart=false
|
||||
|
||||
# 解析参数
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-f|--full)
|
||||
action="full"
|
||||
shift
|
||||
;;
|
||||
-q|--quick)
|
||||
action="quick"
|
||||
shift
|
||||
;;
|
||||
-p|--package)
|
||||
action="package"
|
||||
shift
|
||||
;;
|
||||
-m|--module)
|
||||
module="$2"
|
||||
shift 2
|
||||
;;
|
||||
-r|--restart)
|
||||
need_restart=true
|
||||
shift
|
||||
;;
|
||||
-c|--clean)
|
||||
action="clean"
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
show_help
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
print_error "未知选项: $1"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# 默认操作:增量打包
|
||||
if [ -z "$action" ] && [ "$need_restart" = true ]; then
|
||||
action="package"
|
||||
elif [ -z "$action" ]; then
|
||||
action="package"
|
||||
fi
|
||||
|
||||
# 执行操作
|
||||
case $action in
|
||||
full)
|
||||
full_build
|
||||
;;
|
||||
quick)
|
||||
quick_compile "$module"
|
||||
;;
|
||||
package)
|
||||
incremental_package "$module"
|
||||
;;
|
||||
clean)
|
||||
clean_only
|
||||
;;
|
||||
esac
|
||||
|
||||
# 重启服务
|
||||
if [ "$need_restart" = true ]; then
|
||||
restart_backend
|
||||
fi
|
||||
}
|
||||
|
||||
# 执行主函数
|
||||
main "$@"
|
||||
|
|
@ -1,113 +0,0 @@
|
|||
-- ============================================================
|
||||
-- 会员测试数据(31条)- 覆盖各种测试场景
|
||||
-- 作者:湖北新华业务中台研发团队
|
||||
-- 创建时间:2026-02-02
|
||||
-- ============================================================
|
||||
|
||||
USE `pguser-db`;
|
||||
|
||||
-- 清空现有测试数据(可选,谨慎使用)
|
||||
-- DELETE FROM pg_member WHERE member_id >= 1000;
|
||||
|
||||
-- 密码统一为 123456 的 BCrypt 加密结果
|
||||
-- $2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2
|
||||
|
||||
-- ============================================================
|
||||
-- 家长会员(20条)- 测试各种情况
|
||||
-- ============================================================
|
||||
|
||||
-- 1-5: 正常状态家长,不同性别,小程序注册
|
||||
INSERT INTO pg_member (member_id, member_code, phone, password, nickname, gender, birthday, identity_type, register_source, register_time, status, tenant_id, create_dept, create_by, create_time, del_flag) VALUES
|
||||
(1001, 'JS1738400001001', '13800000001', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '张三爸爸', '1', '1985-03-15', '1', '1', '2026-01-15 10:30:00', '0', '000000', 103, 1, NOW(), '0'),
|
||||
(1002, 'JS1738400001002', '13800000002', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '李四妈妈', '2', '1987-06-20', '1', '1', '2026-01-16 14:20:00', '0', '000000', 103, 1, NOW(), '0'),
|
||||
(1003, 'JS1738400001003', '13800000003', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '王五家长', '0', NULL, '1', '1', '2026-01-17 09:00:00', '0', '000000', 103, 1, NOW(), '0'),
|
||||
(1004, 'JS1738400001004', '13800000004', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '赵六爷爷', '1', '1958-12-01', '1', '1', '2026-01-18 16:45:00', '0', '000000', 103, 1, NOW(), '0'),
|
||||
(1005, 'JS1738400001005', '13800000005', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '钱七奶奶', '2', '1960-08-08', '1', '1', '2026-01-19 11:30:00', '0', '000000', 103, 1, NOW(), '0');
|
||||
|
||||
-- 6-10: 正常状态家长,H5注册,有微信openId
|
||||
INSERT INTO pg_member (member_id, member_code, phone, password, nickname, gender, birthday, identity_type, open_id, register_source, register_time, status, tenant_id, create_dept, create_by, create_time, del_flag) VALUES
|
||||
(1006, 'JS1738400001006', '13800000006', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '孙八爸爸', '1', '1982-04-10', '1', 'oXYZ123456789abc001', '2', '2026-01-20 08:15:00', '0', '000000', 103, 1, NOW(), '0'),
|
||||
(1007, 'JS1738400001007', '13800000007', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '周九妈妈', '2', '1984-09-25', '1', 'oXYZ123456789abc002', '2', '2026-01-21 13:40:00', '0', '000000', 103, 1, NOW(), '0'),
|
||||
(1008, 'JS1738400001008', '13800000008', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '吴十家长', '1', '1979-01-30', '1', 'oXYZ123456789abc003', '2', '2026-01-22 17:00:00', '0', '000000', 103, 1, NOW(), '0'),
|
||||
(1009, 'JS1738400001009', '13800000009', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '郑十一妈妈', '2', NULL, '1', 'oXYZ123456789abc004', '2', '2026-01-23 10:20:00', '0', '000000', 103, 1, NOW(), '0'),
|
||||
(1010, 'JS1738400001010', '13800000010', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '冯十二爸爸', '1', '1975-07-14', '1', 'oXYZ123456789abc005', '2', '2026-01-24 15:55:00', '0', '000000', 103, 1, NOW(), '0');
|
||||
|
||||
-- 11-15: 后台新增的家长
|
||||
INSERT INTO pg_member (member_id, member_code, phone, password, nickname, gender, birthday, identity_type, register_source, register_time, status, tenant_id, create_dept, create_by, create_time, del_flag) VALUES
|
||||
(1011, 'JS1738400001011', '13800000011', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '陈十三家长', '1', '1980-11-11', '1', '3', '2026-01-25 09:30:00', '0', '000000', 103, 1, NOW(), '0'),
|
||||
(1012, 'JS1738400001012', '13800000012', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '楚十四妈妈', '2', '1983-02-28', '1', '3', '2026-01-26 11:15:00', '0', '000000', 103, 1, NOW(), '0'),
|
||||
(1013, 'JS1738400001013', '13800000013', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '魏十五爸爸', '1', NULL, '1', '3', '2026-01-27 14:40:00', '0', '000000', 103, 1, NOW(), '0'),
|
||||
(1014, 'JS1738400001014', '13800000014', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '蒋十六家长', '0', NULL, '1', '3', '2026-01-28 16:20:00', '0', '000000', 103, 1, NOW(), '0'),
|
||||
(1015, 'JS1738400001015', '13800000015', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '沈十七妈妈', '2', '1986-05-05', '1', '3', '2026-01-29 08:00:00', '0', '000000', 103, 1, NOW(), '0');
|
||||
|
||||
-- 16-18: 批量导入的家长
|
||||
INSERT INTO pg_member (member_id, member_code, phone, password, nickname, gender, birthday, identity_type, register_source, register_time, status, tenant_id, create_dept, create_by, create_time, del_flag) VALUES
|
||||
(1016, 'JS1738400001016', '13800000016', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '韩十八爸爸', '1', '1978-08-18', '1', '4', '2026-01-30 10:00:00', '0', '000000', 103, 1, NOW(), '0'),
|
||||
(1017, 'JS1738400001017', '13800000017', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '杨十九妈妈', '2', '1981-10-10', '1', '4', '2026-01-30 10:00:00', '0', '000000', 103, 1, NOW(), '0'),
|
||||
(1018, 'JS1738400001018', '13800000018', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '朱二十家长', '1', NULL, '1', '4', '2026-01-30 10:00:00', '0', '000000', 103, 1, NOW(), '0');
|
||||
|
||||
-- 19-20: 停用状态的家长
|
||||
INSERT INTO pg_member (member_id, member_code, phone, password, nickname, gender, birthday, identity_type, register_source, register_time, status, tenant_id, create_dept, create_by, create_time, del_flag) VALUES
|
||||
(1019, 'JS1738400001019', '13800000019', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '秦廿一爸爸', '1', '1976-03-03', '1', '1', '2025-12-01 09:00:00', '1', '000000', 103, 1, NOW(), '0'),
|
||||
(1020, 'JS1738400001020', '13800000020', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '尤廿二妈妈', '2', '1980-06-15', '1', '2', '2025-11-15 14:30:00', '1', '000000', 103, 1, NOW(), '0');
|
||||
|
||||
-- ============================================================
|
||||
-- 教师会员(11条)- 需要学校信息
|
||||
-- 假设已有学校数据:school_id=1 为武汉第一中学
|
||||
-- ============================================================
|
||||
|
||||
-- 21-25: 正常状态教师,小程序注册
|
||||
INSERT INTO pg_member (member_id, member_code, phone, password, nickname, gender, birthday, identity_type, region_id, school_id, school_grade_id, school_class_id, register_source, register_time, status, tenant_id, create_dept, create_by, create_time, del_flag) VALUES
|
||||
(1021, 'JS1738400001021', '13900000021', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '许老师', '1', '1988-01-20', '2', 420102, 1, 1, 1, '1', '2026-01-10 08:00:00', '0', '000000', 103, 1, NOW(), '0'),
|
||||
(1022, 'JS1738400001022', '13900000022', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '何老师', '2', '1990-05-15', '2', 420102, 1, 1, 2, '1', '2026-01-11 09:30:00', '0', '000000', 103, 1, NOW(), '0'),
|
||||
(1023, 'JS1738400001023', '13900000023', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '吕老师', '1', '1985-09-08', '2', 420102, 1, 2, 1, '1', '2026-01-12 10:15:00', '0', '000000', 103, 1, NOW(), '0'),
|
||||
(1024, 'JS1738400001024', '13900000024', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '施老师', '2', '1992-03-22', '2', 420102, 1, 2, 2, '1', '2026-01-13 11:45:00', '0', '000000', 103, 1, NOW(), '0'),
|
||||
(1025, 'JS1738400001025', '13900000025', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '张老师', '1', '1983-07-30', '2', 420102, 1, 3, 1, '1', '2026-01-14 13:20:00', '0', '000000', 103, 1, NOW(), '0');
|
||||
|
||||
-- 26-28: H5注册的教师,有微信openId
|
||||
INSERT INTO pg_member (member_id, member_code, phone, password, nickname, gender, birthday, identity_type, open_id, region_id, school_id, school_grade_id, school_class_id, register_source, register_time, status, tenant_id, create_dept, create_by, create_time, del_flag) VALUES
|
||||
(1026, 'JS1738400001026', '13900000026', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '孔老师', '2', '1989-11-11', '2', 'oTEACH123456789001', 420103, 1, 3, 2, '2', '2026-01-15 14:00:00', '0', '000000', 103, 1, NOW(), '0'),
|
||||
(1027, 'JS1738400001027', '13900000027', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '曹老师', '1', '1987-04-05', '2', 'oTEACH123456789002', 420103, 1, 4, 1, '2', '2026-01-16 15:30:00', '0', '000000', 103, 1, NOW(), '0'),
|
||||
(1028, 'JS1738400001028', '13900000028', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '严老师', '2', '1991-08-25', '2', 'oTEACH123456789003', 420103, 1, 4, 2, '2', '2026-01-17 16:45:00', '0', '000000', 103, 1, NOW(), '0');
|
||||
|
||||
-- 29-30: 后台新增的教师
|
||||
INSERT INTO pg_member (member_id, member_code, phone, password, nickname, gender, birthday, identity_type, region_id, school_id, school_grade_id, school_class_id, register_source, register_time, status, tenant_id, create_dept, create_by, create_time, del_flag) VALUES
|
||||
(1029, 'JS1738400001029', '13900000029', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '华老师', '1', '1984-12-12', '2', 420104, 1, 5, 1, '3', '2026-01-18 09:00:00', '0', '000000', 103, 1, NOW(), '0'),
|
||||
(1030, 'JS1738400001030', '13900000030', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '金老师', '2', '1993-02-14', '2', 420104, 1, 5, 2, '3', '2026-01-19 10:30:00', '0', '000000', 103, 1, NOW(), '0');
|
||||
|
||||
-- 31: 停用状态的教师
|
||||
INSERT INTO pg_member (member_id, member_code, phone, password, nickname, gender, birthday, identity_type, region_id, school_id, school_grade_id, school_class_id, register_source, register_time, status, tenant_id, create_dept, create_by, create_time, del_flag) VALUES
|
||||
(1031, 'JS1738400001031', '13900000031', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '魏老师(已离职)', '1', '1986-06-06', '2', 420102, 1, 1, 1, '1', '2025-09-01 08:00:00', '1', '000000', 103, 1, NOW(), '0');
|
||||
|
||||
-- ============================================================
|
||||
-- 测试数据统计
|
||||
-- ============================================================
|
||||
-- 家长会员: 20条
|
||||
-- - 正常状态: 18条
|
||||
-- - 停用状态: 2条
|
||||
-- - 小程序注册: 5条
|
||||
-- - H5注册: 5条
|
||||
-- - 后台新增: 5条
|
||||
-- - 批量导入: 3条
|
||||
-- - 有openId: 5条
|
||||
-- - 有生日: 14条
|
||||
-- - 男性: 9条, 女性: 9条, 未知: 2条
|
||||
--
|
||||
-- 教师会员: 11条
|
||||
-- - 正常状态: 10条
|
||||
-- - 停用状态: 1条
|
||||
-- - 小程序注册: 5条
|
||||
-- - H5注册: 3条
|
||||
-- - 后台新增: 2条
|
||||
-- - 有openId: 3条
|
||||
-- - 男性: 6条, 女性: 5条
|
||||
-- ============================================================
|
||||
|
||||
SELECT '会员测试数据导入完成!' AS message;
|
||||
SELECT
|
||||
COUNT(*) AS 总数,
|
||||
SUM(CASE WHEN identity_type = '1' THEN 1 ELSE 0 END) AS 家长数,
|
||||
SUM(CASE WHEN identity_type = '2' THEN 1 ELSE 0 END) AS 教师数,
|
||||
SUM(CASE WHEN status = '0' THEN 1 ELSE 0 END) AS 正常状态,
|
||||
SUM(CASE WHEN status = '1' THEN 1 ELSE 0 END) AS 停用状态
|
||||
FROM pg_member WHERE member_id >= 1001 AND member_id <= 1031;
|
||||
|
|
@ -13,9 +13,6 @@ import org.dromara.pangu.member.service.IPgMemberService;
|
|||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 会员管理
|
||||
*
|
||||
|
|
@ -29,27 +26,18 @@ public class PgMemberController extends BaseController {
|
|||
|
||||
private final IPgMemberService memberService;
|
||||
|
||||
/**
|
||||
* 查询会员列表
|
||||
*/
|
||||
@SaCheckPermission("business:member:list")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<PgMember> list(PgMember member, PageQuery pageQuery) {
|
||||
return memberService.selectPageList(member, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员详情
|
||||
*/
|
||||
@SaCheckPermission("business:member:query")
|
||||
@GetMapping("/{memberId}")
|
||||
public R<PgMember> getInfo(@PathVariable Long memberId) {
|
||||
return R.ok(memberService.selectById(memberId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增会员
|
||||
*/
|
||||
@SaCheckPermission("business:member:add")
|
||||
@Log(title = "会员管理", businessType = BusinessType.INSERT)
|
||||
@PostMapping
|
||||
|
|
@ -57,9 +45,6 @@ public class PgMemberController extends BaseController {
|
|||
return toAjax(memberService.insert(member));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改会员
|
||||
*/
|
||||
@SaCheckPermission("business:member:edit")
|
||||
@Log(title = "会员管理", businessType = BusinessType.UPDATE)
|
||||
@PutMapping
|
||||
|
|
@ -67,44 +52,10 @@ public class PgMemberController extends BaseController {
|
|||
return toAjax(memberService.update(member));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除会员
|
||||
*/
|
||||
@SaCheckPermission("business:member:remove")
|
||||
@Log(title = "会员管理", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/{memberIds}")
|
||||
public R<Void> remove(@PathVariable Long[] memberIds) {
|
||||
return toAjax(memberService.deleteByIds(memberIds));
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置会员密码
|
||||
*/
|
||||
@SaCheckPermission("business:member:resetPwd")
|
||||
@Log(title = "会员管理-重置密码", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/resetPwd/{memberId}")
|
||||
public R<Map<String, String>> resetPwd(@PathVariable Long memberId) {
|
||||
String newPassword = memberService.resetPassword(memberId);
|
||||
Map<String, String> result = new HashMap<>(2);
|
||||
result.put("password", newPassword);
|
||||
return R.ok(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改会员状态
|
||||
*/
|
||||
@SaCheckPermission("business:member:edit")
|
||||
@Log(title = "会员管理-状态变更", businessType = BusinessType.UPDATE)
|
||||
@PutMapping("/changeStatus")
|
||||
public R<Void> changeStatus(@RequestBody PgMember member) {
|
||||
return toAjax(memberService.changeStatus(member.getMemberId(), member.getStatus()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查手机号是否唯一
|
||||
*/
|
||||
@GetMapping("/checkPhoneUnique")
|
||||
public R<Boolean> checkPhoneUnique(@RequestParam String phone, @RequestParam(required = false) Long memberId) {
|
||||
return R.ok(memberService.checkPhoneUnique(phone, memberId));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,68 +12,10 @@ import java.util.List;
|
|||
* @author pangu
|
||||
*/
|
||||
public interface IPgMemberService {
|
||||
|
||||
/**
|
||||
* 分页查询会员列表
|
||||
*/
|
||||
TableDataInfo<PgMember> selectPageList(PgMember member, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 查询会员列表
|
||||
*/
|
||||
List<PgMember> selectList(PgMember member);
|
||||
|
||||
/**
|
||||
* 根据ID获取会员详情
|
||||
*/
|
||||
PgMember selectById(Long memberId);
|
||||
|
||||
/**
|
||||
* 新增会员
|
||||
*/
|
||||
int insert(PgMember member);
|
||||
|
||||
/**
|
||||
* 修改会员
|
||||
*/
|
||||
int update(PgMember member);
|
||||
|
||||
/**
|
||||
* 批量删除会员
|
||||
*/
|
||||
int deleteByIds(Long[] memberIds);
|
||||
|
||||
/**
|
||||
* 删除单个会员
|
||||
*/
|
||||
int deleteById(Long memberId);
|
||||
|
||||
/**
|
||||
* 重置会员密码
|
||||
* @return 新密码
|
||||
*/
|
||||
String resetPassword(Long memberId);
|
||||
|
||||
/**
|
||||
* 修改会员状态
|
||||
*/
|
||||
int changeStatus(Long memberId, String status);
|
||||
|
||||
/**
|
||||
* 检查手机号是否唯一
|
||||
* @param phone 手机号
|
||||
* @param memberId 会员ID(编辑时排除自己)
|
||||
* @return true=唯一,false=不唯一
|
||||
*/
|
||||
boolean checkPhoneUnique(String phone, Long memberId);
|
||||
|
||||
/**
|
||||
* 检查会员是否可删除(是否绑定学生)
|
||||
*/
|
||||
boolean checkCanDelete(Long memberId);
|
||||
|
||||
/**
|
||||
* 根据手机号查询会员
|
||||
*/
|
||||
PgMember selectByPhone(String phone);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,23 +1,17 @@
|
|||
package org.dromara.pangu.member.service.impl;
|
||||
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.pangu.member.domain.PgMember;
|
||||
import org.dromara.pangu.member.mapper.PgMemberMapper;
|
||||
import org.dromara.pangu.member.service.IPgMemberService;
|
||||
import org.dromara.pangu.student.mapper.PgStudentMapper;
|
||||
import cn.hutool.crypto.digest.BCrypt;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
|
@ -30,9 +24,6 @@ import java.util.List;
|
|||
public class PgMemberServiceImpl implements IPgMemberService {
|
||||
|
||||
private final PgMemberMapper baseMapper;
|
||||
private final PgStudentMapper studentMapper;
|
||||
|
||||
private static final String DEFAULT_PASSWORD = "123456";
|
||||
|
||||
@Override
|
||||
public TableDataInfo<PgMember> selectPageList(PgMember member, PageQuery pageQuery) {
|
||||
|
|
@ -52,170 +43,20 @@ public class PgMemberServiceImpl implements IPgMemberService {
|
|||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int insert(PgMember member) {
|
||||
// 校验手机号唯一性
|
||||
if (!checkPhoneUnique(member.getPhone(), null)) {
|
||||
throw new ServiceException("手机号已存在");
|
||||
}
|
||||
|
||||
// 生成会员编号: JS + 时间戳
|
||||
member.setMemberCode("JS" + System.currentTimeMillis());
|
||||
|
||||
// 昵称未填写时自动生成: 用户 + 手机号后4位
|
||||
if (StrUtil.isBlank(member.getNickname()) && StrUtil.isNotBlank(member.getPhone())) {
|
||||
member.setNickname("用户" + member.getPhone().substring(member.getPhone().length() - 4));
|
||||
}
|
||||
|
||||
// 密码加密(默认密码)
|
||||
if (StrUtil.isBlank(member.getPassword())) {
|
||||
member.setPassword(BCrypt.hashpw(DEFAULT_PASSWORD));
|
||||
} else {
|
||||
member.setPassword(BCrypt.hashpw(member.getPassword()));
|
||||
}
|
||||
|
||||
// 设置注册时间和来源
|
||||
member.setRegisterTime(new Date());
|
||||
if (StrUtil.isBlank(member.getRegisterSource())) {
|
||||
member.setRegisterSource("3"); // 后台新增
|
||||
}
|
||||
|
||||
// 默认状态为正常
|
||||
if (StrUtil.isBlank(member.getStatus())) {
|
||||
member.setStatus("0");
|
||||
}
|
||||
|
||||
// 教师身份校验必填字段
|
||||
validateTeacherInfo(member);
|
||||
|
||||
return baseMapper.insert(member);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int update(PgMember member) {
|
||||
// 校验手机号唯一性
|
||||
if (StrUtil.isNotBlank(member.getPhone()) && !checkPhoneUnique(member.getPhone(), member.getMemberId())) {
|
||||
throw new ServiceException("手机号已存在");
|
||||
}
|
||||
|
||||
// 教师身份校验必填字段
|
||||
validateTeacherInfo(member);
|
||||
|
||||
// 家长身份清空学校信息
|
||||
if ("1".equals(member.getIdentityType())) {
|
||||
member.setRegionId(null);
|
||||
member.setSchoolId(null);
|
||||
member.setSchoolGradeId(null);
|
||||
member.setSchoolClassId(null);
|
||||
}
|
||||
|
||||
// 不更新密码(密码通过重置接口更新)
|
||||
member.setPassword(null);
|
||||
|
||||
return baseMapper.updateById(member);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int deleteByIds(Long[] memberIds) {
|
||||
// 检查每个会员是否可删除
|
||||
for (Long memberId : memberIds) {
|
||||
if (!checkCanDelete(memberId)) {
|
||||
PgMember member = baseMapper.selectById(memberId);
|
||||
String nickname = member != null ? member.getNickname() : String.valueOf(memberId);
|
||||
throw new ServiceException("会员【" + nickname + "】已绑定学生,请先解绑学生后再删除");
|
||||
}
|
||||
}
|
||||
return baseMapper.deleteByIds(Arrays.asList(memberIds));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int deleteById(Long memberId) {
|
||||
if (!checkCanDelete(memberId)) {
|
||||
throw new ServiceException("该会员已绑定学生,请先解绑学生后再删除");
|
||||
}
|
||||
return baseMapper.deleteById(memberId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public String resetPassword(Long memberId) {
|
||||
PgMember member = baseMapper.selectById(memberId);
|
||||
if (member == null) {
|
||||
throw new ServiceException("会员不存在");
|
||||
}
|
||||
|
||||
// 生成8位随机密码
|
||||
String newPassword = RandomUtil.randomString(8);
|
||||
|
||||
PgMember updateMember = new PgMember();
|
||||
updateMember.setMemberId(memberId);
|
||||
updateMember.setPassword(BCrypt.hashpw(newPassword));
|
||||
baseMapper.updateById(updateMember);
|
||||
|
||||
return newPassword;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int changeStatus(Long memberId, String status) {
|
||||
PgMember member = new PgMember();
|
||||
member.setMemberId(memberId);
|
||||
member.setStatus(status);
|
||||
return baseMapper.updateById(member);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkPhoneUnique(String phone, Long memberId) {
|
||||
if (StrUtil.isBlank(phone)) {
|
||||
return true;
|
||||
}
|
||||
LambdaQueryWrapper<PgMember> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(PgMember::getPhone, phone);
|
||||
if (memberId != null) {
|
||||
wrapper.ne(PgMember::getMemberId, memberId);
|
||||
}
|
||||
return baseMapper.selectCount(wrapper) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkCanDelete(Long memberId) {
|
||||
// 检查是否有绑定的学生
|
||||
Long count = studentMapper.selectCount(
|
||||
new LambdaQueryWrapper<org.dromara.pangu.student.domain.PgStudent>()
|
||||
.eq(org.dromara.pangu.student.domain.PgStudent::getMemberId, memberId)
|
||||
);
|
||||
return count == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PgMember selectByPhone(String phone) {
|
||||
return baseMapper.selectOne(
|
||||
new LambdaQueryWrapper<PgMember>().eq(PgMember::getPhone, phone)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验教师身份必填字段
|
||||
*/
|
||||
private void validateTeacherInfo(PgMember member) {
|
||||
if ("2".equals(member.getIdentityType())) {
|
||||
if (member.getRegionId() == null) {
|
||||
throw new ServiceException("教师身份必须选择所属区域");
|
||||
}
|
||||
if (member.getSchoolId() == null) {
|
||||
throw new ServiceException("教师身份必须选择所属学校");
|
||||
}
|
||||
if (member.getSchoolGradeId() == null) {
|
||||
throw new ServiceException("教师身份必须选择所属年级");
|
||||
}
|
||||
if (member.getSchoolClassId() == null) {
|
||||
throw new ServiceException("教师身份必须选择所属班级");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<PgMember> buildQueryWrapper(PgMember member) {
|
||||
LambdaQueryWrapper<PgMember> lqw = new LambdaQueryWrapper<>();
|
||||
lqw.like(StrUtil.isNotBlank(member.getNickname()), PgMember::getNickname, member.getNickname());
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import org.dromara.common.web.core.BaseController;
|
|||
import org.dromara.pangu.school.domain.PgSchool;
|
||||
import org.dromara.pangu.school.domain.PgSchoolClass;
|
||||
import org.dromara.pangu.school.domain.PgSchoolGrade;
|
||||
import org.dromara.pangu.school.domain.vo.SchoolTreeNode;
|
||||
import org.dromara.pangu.school.service.IPgSchoolService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
|
@ -43,15 +42,6 @@ public class PgSchoolController extends BaseController {
|
|||
return R.ok(schoolService.selectList(school));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取学校树形结构(包含年级和班级)
|
||||
*/
|
||||
@SaCheckPermission("business:school:list")
|
||||
@GetMapping("/tree")
|
||||
public R<List<SchoolTreeNode>> tree(PgSchool school) {
|
||||
return R.ok(schoolService.selectSchoolTree(school));
|
||||
}
|
||||
|
||||
@SaCheckPermission("business:school:query")
|
||||
@GetMapping("/{schoolId}")
|
||||
public R<PgSchool> getInfo(@PathVariable Long schoolId) {
|
||||
|
|
@ -95,12 +85,8 @@ public class PgSchoolController extends BaseController {
|
|||
@PostMapping("/grade")
|
||||
public R<Void> addSchoolGrade(@RequestBody Map<String, Object> params) {
|
||||
Long schoolId = Long.valueOf(params.get("schoolId").toString());
|
||||
// JSON解析时数值会被解析为Integer,需要逐个转换为Long
|
||||
@SuppressWarnings("unchecked")
|
||||
List<?> rawGradeIds = (List<?>) params.get("gradeIds");
|
||||
List<Long> gradeIds = rawGradeIds.stream()
|
||||
.map(id -> Long.valueOf(id.toString()))
|
||||
.toList();
|
||||
List<Long> gradeIds = (List<Long>) params.get("gradeIds");
|
||||
return toAjax(schoolService.addSchoolGrades(schoolId, gradeIds));
|
||||
}
|
||||
|
||||
|
|
@ -113,32 +99,8 @@ public class PgSchoolController extends BaseController {
|
|||
public R<Void> addGradeClass(@RequestBody Map<String, Object> params) {
|
||||
Long schoolId = Long.valueOf(params.get("schoolId").toString());
|
||||
Long schoolGradeId = Long.valueOf(params.get("schoolGradeId").toString());
|
||||
// JSON解析时数值会被解析为Integer,需要逐个转换为Long
|
||||
@SuppressWarnings("unchecked")
|
||||
List<?> rawClassIds = (List<?>) params.get("classIds");
|
||||
List<Long> classIds = rawClassIds.stream()
|
||||
.map(id -> Long.valueOf(id.toString()))
|
||||
.toList();
|
||||
List<Long> classIds = (List<Long>) params.get("classIds");
|
||||
return toAjax(schoolService.addGradeClasses(schoolId, schoolGradeId, classIds));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除学校下的年级
|
||||
*/
|
||||
@SaCheckPermission("business:school:edit")
|
||||
@Log(title = "学校年级管理", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/grade/{schoolGradeId}")
|
||||
public R<Void> removeSchoolGrade(@PathVariable Long schoolGradeId) {
|
||||
return toAjax(schoolService.removeSchoolGrade(schoolGradeId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除年级下的班级
|
||||
*/
|
||||
@SaCheckPermission("business:school:edit")
|
||||
@Log(title = "学校班级管理", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/class/{schoolClassId}")
|
||||
public R<Void> removeGradeClass(@PathVariable Long schoolClassId) {
|
||||
return toAjax(schoolService.removeGradeClass(schoolClassId));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package org.dromara.pangu.school.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
|
@ -49,10 +48,4 @@ public class PgSchool extends BaseEntity {
|
|||
private String delFlag;
|
||||
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 区域名称(非数据库字段,用于展示)
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private String regionName;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,94 +0,0 @@
|
|||
package org.dromara.pangu.school.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 学校树节点(学校/年级/班级统一结构)
|
||||
*
|
||||
* @author pangu
|
||||
*/
|
||||
@Data
|
||||
public class SchoolTreeNode {
|
||||
|
||||
/**
|
||||
* 节点ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 节点名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 节点类型:school-学校, grade-年级, class-班级
|
||||
*/
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* 学校编码(仅学校节点有值)
|
||||
*/
|
||||
private String schoolCode;
|
||||
|
||||
/**
|
||||
* 学校类型(仅学校节点有值)
|
||||
*/
|
||||
private String schoolType;
|
||||
|
||||
/**
|
||||
* 区域名称(仅学校节点有值)
|
||||
*/
|
||||
private String regionName;
|
||||
|
||||
/**
|
||||
* 区域ID(仅学校节点有值)
|
||||
*/
|
||||
private Long regionId;
|
||||
|
||||
/**
|
||||
* 状态(0正常 1停用)
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private String createTime;
|
||||
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
private String createBy;
|
||||
|
||||
/**
|
||||
* 子节点
|
||||
*/
|
||||
private List<SchoolTreeNode> children;
|
||||
|
||||
/**
|
||||
* 父节点ID(用于前端判断层级)
|
||||
*/
|
||||
private Long parentId;
|
||||
|
||||
/**
|
||||
* 学校ID(年级和班级节点需要)
|
||||
*/
|
||||
private Long schoolId;
|
||||
|
||||
/**
|
||||
* 学校年级关联ID(班级节点需要)
|
||||
*/
|
||||
private Long schoolGradeId;
|
||||
|
||||
/**
|
||||
* 年级ID(仅年级节点有值)
|
||||
*/
|
||||
private Long gradeId;
|
||||
|
||||
/**
|
||||
* 班级ID(仅班级节点有值)
|
||||
*/
|
||||
private Long classId;
|
||||
}
|
||||
|
|
@ -4,7 +4,6 @@ import org.dromara.common.mybatis.core.page.PageQuery;
|
|||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.pangu.school.domain.PgSchool;
|
||||
import org.dromara.pangu.school.domain.PgSchoolGrade;
|
||||
import org.dromara.pangu.school.domain.vo.SchoolTreeNode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
|
@ -35,19 +34,4 @@ public interface IPgSchoolService {
|
|||
* 为年级添加班级(批量挂载)
|
||||
*/
|
||||
int addGradeClasses(Long schoolId, Long schoolGradeId, List<Long> classIds);
|
||||
|
||||
/**
|
||||
* 获取学校树形结构(包含年级和班级)
|
||||
*/
|
||||
List<SchoolTreeNode> selectSchoolTree(PgSchool school);
|
||||
|
||||
/**
|
||||
* 删除学校下的年级(同时删除该年级下的班级)
|
||||
*/
|
||||
int removeSchoolGrade(Long schoolGradeId);
|
||||
|
||||
/**
|
||||
* 删除年级下的班级
|
||||
*/
|
||||
int removeGradeClass(Long schoolClassId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,27 +6,20 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.pangu.base.domain.PgClass;
|
||||
import org.dromara.pangu.base.domain.PgGrade;
|
||||
import org.dromara.pangu.base.domain.PgRegion;
|
||||
import org.dromara.pangu.base.mapper.PgClassMapper;
|
||||
import org.dromara.pangu.base.mapper.PgGradeMapper;
|
||||
import org.dromara.pangu.base.mapper.PgRegionMapper;
|
||||
import org.dromara.pangu.school.domain.PgSchool;
|
||||
import org.dromara.pangu.school.domain.PgSchoolClass;
|
||||
import org.dromara.pangu.school.domain.PgSchoolGrade;
|
||||
import org.dromara.pangu.school.domain.vo.SchoolTreeNode;
|
||||
import org.dromara.pangu.school.mapper.PgSchoolClassMapper;
|
||||
import org.dromara.pangu.school.mapper.PgSchoolGradeMapper;
|
||||
import org.dromara.pangu.school.mapper.PgSchoolMapper;
|
||||
import org.dromara.pangu.school.service.IPgSchoolService;
|
||||
import org.dromara.common.core.service.UserService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 学校 Service 实现
|
||||
|
|
@ -41,51 +34,17 @@ public class PgSchoolServiceImpl implements IPgSchoolService {
|
|||
private final PgSchoolGradeMapper schoolGradeMapper;
|
||||
private final PgSchoolClassMapper schoolClassMapper;
|
||||
private final PgGradeMapper gradeMapper;
|
||||
private final PgClassMapper classMapper;
|
||||
private final PgRegionMapper regionMapper;
|
||||
private final UserService userService;
|
||||
|
||||
@Override
|
||||
public TableDataInfo<PgSchool> selectPageList(PgSchool school, PageQuery pageQuery) {
|
||||
LambdaQueryWrapper<PgSchool> lqw = buildQueryWrapper(school);
|
||||
Page<PgSchool> page = baseMapper.selectPage(pageQuery.build(), lqw);
|
||||
// 填充区域名称
|
||||
fillRegionName(page.getRecords());
|
||||
return TableDataInfo.build(page);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PgSchool> selectList(PgSchool school) {
|
||||
List<PgSchool> list = baseMapper.selectList(buildQueryWrapper(school));
|
||||
fillRegionName(list);
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 填充区域名称
|
||||
*/
|
||||
private void fillRegionName(List<PgSchool> schools) {
|
||||
if (schools == null || schools.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
// 收集所有区域ID
|
||||
Set<Long> regionIds = schools.stream()
|
||||
.map(PgSchool::getRegionId)
|
||||
.filter(id -> id != null)
|
||||
.collect(Collectors.toSet());
|
||||
if (regionIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
// 批量查询区域信息
|
||||
List<PgRegion> regions = regionMapper.selectBatchIds(regionIds);
|
||||
Map<Long, String> regionMap = regions.stream()
|
||||
.collect(Collectors.toMap(PgRegion::getRegionId, PgRegion::getRegionName));
|
||||
// 填充区域名称
|
||||
for (PgSchool s : schools) {
|
||||
if (s.getRegionId() != null) {
|
||||
s.setRegionName(regionMap.get(s.getRegionId()));
|
||||
}
|
||||
}
|
||||
return baseMapper.selectList(buildQueryWrapper(school));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -95,59 +54,16 @@ public class PgSchoolServiceImpl implements IPgSchoolService {
|
|||
|
||||
@Override
|
||||
public int insert(PgSchool school) {
|
||||
// 自动生成学校编码
|
||||
String schoolCode = generateSchoolCode();
|
||||
school.setSchoolCode(schoolCode);
|
||||
return baseMapper.insert(school);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成学校编码,格式:SCH + 年份后两位 + 6位序号
|
||||
*/
|
||||
private String generateSchoolCode() {
|
||||
String yearSuffix = String.valueOf(java.time.Year.now().getValue()).substring(2);
|
||||
String prefix = "SCH" + yearSuffix;
|
||||
// 查询当前最大编码
|
||||
LambdaQueryWrapper<PgSchool> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.likeRight(PgSchool::getSchoolCode, prefix);
|
||||
wrapper.orderByDesc(PgSchool::getSchoolCode);
|
||||
wrapper.last("LIMIT 1");
|
||||
PgSchool lastSchool = baseMapper.selectOne(wrapper);
|
||||
int nextSeq = 1;
|
||||
if (lastSchool != null && lastSchool.getSchoolCode() != null) {
|
||||
String lastCode = lastSchool.getSchoolCode();
|
||||
String seqStr = lastCode.substring(prefix.length());
|
||||
nextSeq = Integer.parseInt(seqStr) + 1;
|
||||
}
|
||||
return prefix + String.format("%06d", nextSeq);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int update(PgSchool school) {
|
||||
return baseMapper.updateById(school);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int deleteByIds(Long[] schoolIds) {
|
||||
for (Long schoolId : schoolIds) {
|
||||
// TODO: 检查是否被学生信息引用(学生管理模块完成后添加),有则不允许删除
|
||||
|
||||
// 级联删除:先删除学校下的所有班级和年级
|
||||
List<PgSchoolGrade> grades = schoolGradeMapper.selectList(
|
||||
new LambdaQueryWrapper<PgSchoolGrade>().eq(PgSchoolGrade::getSchoolId, schoolId)
|
||||
);
|
||||
for (PgSchoolGrade grade : grades) {
|
||||
// 删除年级下的所有班级
|
||||
schoolClassMapper.delete(
|
||||
new LambdaQueryWrapper<PgSchoolClass>().eq(PgSchoolClass::getSchoolGradeId, grade.getId())
|
||||
);
|
||||
}
|
||||
// 删除学校下的所有年级
|
||||
schoolGradeMapper.delete(
|
||||
new LambdaQueryWrapper<PgSchoolGrade>().eq(PgSchoolGrade::getSchoolId, schoolId)
|
||||
);
|
||||
}
|
||||
return baseMapper.deleteByIds(Arrays.asList(schoolIds));
|
||||
}
|
||||
|
||||
|
|
@ -211,25 +127,6 @@ public class PgSchoolServiceImpl implements IPgSchoolService {
|
|||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int removeSchoolGrade(Long schoolGradeId) {
|
||||
// TODO: 检查是否被学生信息引用(学生管理模块完成后添加),有则不允许删除
|
||||
|
||||
// 级联删除:先删除年级下的所有班级
|
||||
schoolClassMapper.delete(
|
||||
new LambdaQueryWrapper<PgSchoolClass>().eq(PgSchoolClass::getSchoolGradeId, schoolGradeId)
|
||||
);
|
||||
// 删除年级
|
||||
return schoolGradeMapper.deleteById(schoolGradeId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int removeGradeClass(Long schoolClassId) {
|
||||
// TODO: 检查是否被学生信息引用(学生管理模块完成后添加)
|
||||
return schoolClassMapper.deleteById(schoolClassId);
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<PgSchool> buildQueryWrapper(PgSchool school) {
|
||||
LambdaQueryWrapper<PgSchool> lqw = new LambdaQueryWrapper<>();
|
||||
lqw.like(StrUtil.isNotBlank(school.getSchoolName()), PgSchool::getSchoolName, school.getSchoolName());
|
||||
|
|
@ -238,124 +135,4 @@ public class PgSchoolServiceImpl implements IPgSchoolService {
|
|||
lqw.eq(StrUtil.isNotBlank(school.getStatus()), PgSchool::getStatus, school.getStatus());
|
||||
return lqw;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SchoolTreeNode> selectSchoolTree(PgSchool school) {
|
||||
// 查询学校列表
|
||||
List<PgSchool> schools = baseMapper.selectList(buildQueryWrapper(school));
|
||||
fillRegionName(schools);
|
||||
|
||||
if (schools.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 收集所有学校ID
|
||||
List<Long> schoolIds = schools.stream().map(PgSchool::getSchoolId).toList();
|
||||
|
||||
// 查询所有学校的年级
|
||||
List<PgSchoolGrade> allSchoolGrades = schoolGradeMapper.selectList(
|
||||
new LambdaQueryWrapper<PgSchoolGrade>().in(PgSchoolGrade::getSchoolId, schoolIds)
|
||||
);
|
||||
|
||||
// 查询年级名称
|
||||
Set<Long> gradeIds = allSchoolGrades.stream().map(PgSchoolGrade::getGradeId).collect(java.util.stream.Collectors.toSet());
|
||||
Map<Long, String> gradeNameMap = new HashMap<>();
|
||||
if (!gradeIds.isEmpty()) {
|
||||
List<PgGrade> grades = gradeMapper.selectBatchIds(gradeIds);
|
||||
gradeNameMap = grades.stream().collect(java.util.stream.Collectors.toMap(PgGrade::getGradeId, PgGrade::getGradeName));
|
||||
}
|
||||
|
||||
// 收集所有学校年级关联ID
|
||||
List<Long> schoolGradeIds = allSchoolGrades.stream().map(PgSchoolGrade::getId).toList();
|
||||
|
||||
// 查询所有班级
|
||||
final List<PgSchoolClass> allSchoolClasses;
|
||||
if (!schoolGradeIds.isEmpty()) {
|
||||
allSchoolClasses = schoolClassMapper.selectList(
|
||||
new LambdaQueryWrapper<PgSchoolClass>().in(PgSchoolClass::getSchoolGradeId, schoolGradeIds)
|
||||
);
|
||||
} else {
|
||||
allSchoolClasses = new ArrayList<>();
|
||||
}
|
||||
|
||||
// 查询班级名称
|
||||
Set<Long> classIds = allSchoolClasses.stream().map(PgSchoolClass::getClassId).collect(java.util.stream.Collectors.toSet());
|
||||
Map<Long, String> classNameMap = new HashMap<>();
|
||||
if (!classIds.isEmpty()) {
|
||||
List<PgClass> classes = classMapper.selectBatchIds(classIds);
|
||||
classNameMap = classes.stream().collect(java.util.stream.Collectors.toMap(PgClass::getClassId, PgClass::getClassName));
|
||||
}
|
||||
|
||||
// 批量查询创建人昵称
|
||||
Set<Long> createByIds = schools.stream()
|
||||
.map(PgSchool::getCreateBy)
|
||||
.filter(id -> id != null)
|
||||
.collect(Collectors.toSet());
|
||||
Map<Long, String> userNicknameMap = new HashMap<>();
|
||||
if (!createByIds.isEmpty()) {
|
||||
userNicknameMap = userService.selectUserNamesByIds(new ArrayList<>(createByIds));
|
||||
}
|
||||
|
||||
// 构建树形结构
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
List<SchoolTreeNode> result = new ArrayList<>();
|
||||
Map<Long, String> finalUserNicknameMap = userNicknameMap;
|
||||
|
||||
for (PgSchool s : schools) {
|
||||
SchoolTreeNode schoolNode = new SchoolTreeNode();
|
||||
schoolNode.setId(s.getSchoolId());
|
||||
schoolNode.setName(s.getSchoolName());
|
||||
schoolNode.setType("school");
|
||||
schoolNode.setSchoolCode(s.getSchoolCode());
|
||||
schoolNode.setSchoolType(s.getSchoolType());
|
||||
schoolNode.setRegionName(s.getRegionName());
|
||||
schoolNode.setRegionId(s.getRegionId());
|
||||
schoolNode.setStatus(s.getStatus());
|
||||
schoolNode.setCreateTime(s.getCreateTime() != null ? sdf.format(s.getCreateTime()) : null);
|
||||
// 使用昵称而不是ID
|
||||
schoolNode.setCreateBy(s.getCreateBy() != null ? finalUserNicknameMap.getOrDefault(s.getCreateBy(), s.getCreateBy().toString()) : null);
|
||||
schoolNode.setSchoolId(s.getSchoolId());
|
||||
|
||||
// 查找该学校的年级
|
||||
Map<Long, String> finalGradeNameMap = gradeNameMap;
|
||||
Map<Long, String> finalClassNameMap = classNameMap;
|
||||
List<SchoolTreeNode> gradeNodes = allSchoolGrades.stream()
|
||||
.filter(sg -> sg.getSchoolId().equals(s.getSchoolId()))
|
||||
.map(sg -> {
|
||||
SchoolTreeNode gradeNode = new SchoolTreeNode();
|
||||
gradeNode.setId(sg.getId()); // 使用学校年级关联表ID
|
||||
gradeNode.setName(finalGradeNameMap.getOrDefault(sg.getGradeId(), ""));
|
||||
gradeNode.setType("grade");
|
||||
gradeNode.setSchoolId(sg.getSchoolId());
|
||||
gradeNode.setParentId(s.getSchoolId());
|
||||
gradeNode.setSchoolGradeId(sg.getId());
|
||||
gradeNode.setGradeId(sg.getGradeId());
|
||||
|
||||
// 查找该年级的班级
|
||||
List<SchoolTreeNode> classNodes = allSchoolClasses.stream()
|
||||
.filter(sc -> sc.getSchoolGradeId().equals(sg.getId()))
|
||||
.map(sc -> {
|
||||
SchoolTreeNode classNode = new SchoolTreeNode();
|
||||
classNode.setId(sc.getId()); // 使用学校班级关联表ID
|
||||
classNode.setName(finalClassNameMap.getOrDefault(sc.getClassId(), ""));
|
||||
classNode.setType("class");
|
||||
classNode.setSchoolId(sc.getSchoolId());
|
||||
classNode.setSchoolGradeId(sc.getSchoolGradeId());
|
||||
classNode.setParentId(sg.getId());
|
||||
classNode.setClassId(sc.getClassId());
|
||||
return classNode;
|
||||
})
|
||||
.toList();
|
||||
|
||||
gradeNode.setChildren(classNodes.isEmpty() ? null : new ArrayList<>(classNodes));
|
||||
return gradeNode;
|
||||
})
|
||||
.toList();
|
||||
|
||||
schoolNode.setChildren(gradeNodes.isEmpty() ? null : new ArrayList<>(gradeNodes));
|
||||
result.add(schoolNode);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,10 +96,7 @@ public class SysMenuController extends BaseController {
|
|||
@SaCheckPermission("system:menu:query")
|
||||
@GetMapping(value = "/roleMenuTreeselect/{roleId}")
|
||||
public R<MenuTreeSelectVo> roleMenuTreeselect(@PathVariable("roleId") Long roleId) {
|
||||
// 只查询可见的菜单(visible='0')
|
||||
SysMenuBo menuQuery = new SysMenuBo();
|
||||
menuQuery.setVisible("0");
|
||||
List<SysMenuVo> menus = menuService.selectMenuList(menuQuery, LoginHelper.getUserId());
|
||||
List<SysMenuVo> menus = menuService.selectMenuList(LoginHelper.getUserId());
|
||||
MenuTreeSelectVo selectVo = new MenuTreeSelectVo(
|
||||
menuService.selectMenuListByRoleId(roleId),
|
||||
menuService.buildMenuTreeSelect(menus));
|
||||
|
|
|
|||
|
|
@ -238,7 +238,7 @@
|
|||
| ---- | ---- |:---:| --------------- |
|
||||
| 学校名称 | 文本 | ✓ | 最大长度100字符 |
|
||||
| 学校编码 | 文本 | ✓ | 系统自动生成,唯一 |
|
||||
| 类型 | 枚举 | ✓ | 节点类型:学校/年级/班级(用于区分树形层级) |
|
||||
| 类型 | 枚举 | ✓ | 学校类型(小学/中学/高中等) |
|
||||
| 所属区域 | 树形选择 | ✓ | 省-市-区三级 |
|
||||
| 状态 | 开关 | ✓ | 启用/禁用 |
|
||||
| 创建时间 | 日期时间 | - | 系统自动记录 |
|
||||
|
|
|
|||
|
|
@ -46,8 +46,8 @@ const generateCaptcha = () => {
|
|||
return { code, img: base64 }
|
||||
}
|
||||
|
||||
// 获取验证码(路径与后端一致:/auth/code)
|
||||
Mock.mock(/\/auth\/code/, 'get', () => {
|
||||
// 获取验证码
|
||||
Mock.mock(/\/api\/captchaImage/, 'get', () => {
|
||||
const uuid = Mock.Random.guid()
|
||||
const captcha = generateCaptcha()
|
||||
|
||||
|
|
|
|||
|
|
@ -5,10 +5,11 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
:title="isEdit ? '编辑会员' : '新增会员'"
|
||||
:title="memberId ? '编辑会员' : '新增会员'"
|
||||
width="700px"
|
||||
:close-on-click-modal="false"
|
||||
destroy-on-close
|
||||
@open="handleOpen"
|
||||
>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
|
|
@ -24,7 +25,7 @@
|
|||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="昵称" prop="nickname">
|
||||
<el-input v-model="form.nickname" placeholder="不填则自动生成" />
|
||||
<el-input v-model="form.nickname" placeholder="请输入昵称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
|
@ -55,7 +56,7 @@
|
|||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="身份类型" prop="identityType">
|
||||
<el-radio-group v-model="form.identityType" @change="handleIdentityChange">
|
||||
<el-radio-group v-model="form.identityType">
|
||||
<el-radio value="1">家长</el-radio>
|
||||
<el-radio value="2">教师</el-radio>
|
||||
</el-radio-group>
|
||||
|
|
@ -76,14 +77,14 @@
|
|||
|
||||
<!-- 教师身份时显示学校信息 -->
|
||||
<template v-if="form.identityType === '2'">
|
||||
<el-divider content-position="left">学校信息(教师必填)</el-divider>
|
||||
<el-divider content-position="left">学校信息</el-divider>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="区域" prop="regionId" :rules="teacherRules.regionId">
|
||||
<el-form-item label="区域" prop="regionId">
|
||||
<el-cascader
|
||||
v-model="form.regionIds"
|
||||
:options="regionTree"
|
||||
:props="{ value: 'regionId', label: 'regionName', checkStrictly: true }"
|
||||
:props="{ value: 'id', label: 'label', checkStrictly: true }"
|
||||
placeholder="请选择区域"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
|
|
@ -92,25 +93,25 @@
|
|||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="学校" prop="schoolId" :rules="teacherRules.schoolId">
|
||||
<el-form-item label="学校" prop="schoolId">
|
||||
<el-select v-model="form.schoolId" placeholder="请选择学校" clearable style="width: 100%" @change="handleSchoolChange">
|
||||
<el-option v-for="item in schoolList" :key="item.schoolId" :label="item.schoolName" :value="item.schoolId" />
|
||||
<el-option v-for="item in schoolList" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="年级" prop="schoolGradeId" :rules="teacherRules.schoolGradeId">
|
||||
<el-select v-model="form.schoolGradeId" placeholder="请选择年级" clearable style="width: 100%" @change="handleGradeChange">
|
||||
<el-option v-for="item in gradeList" :key="item.id" :label="item.gradeName" :value="item.id" />
|
||||
<el-form-item label="年级" prop="gradeId">
|
||||
<el-select v-model="form.gradeId" placeholder="请选择年级" clearable style="width: 100%" @change="handleGradeChange">
|
||||
<el-option v-for="item in gradeList" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="班级" prop="schoolClassId" :rules="teacherRules.schoolClassId">
|
||||
<el-select v-model="form.schoolClassId" placeholder="请选择班级" clearable style="width: 100%">
|
||||
<el-option v-for="item in classList" :key="item.id" :label="item.className" :value="item.id" />
|
||||
<el-form-item label="班级" prop="classId">
|
||||
<el-select v-model="form.classId" placeholder="请选择班级" clearable style="width: 100%">
|
||||
<el-option v-for="item in classList" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
|
@ -119,14 +120,6 @@
|
|||
|
||||
<!-- 绑定学生 -->
|
||||
<el-divider content-position="left">绑定学生</el-divider>
|
||||
<el-alert
|
||||
v-if="form.identityType === '2'"
|
||||
title="教师只能绑定本校学生"
|
||||
type="info"
|
||||
:closable="false"
|
||||
show-icon
|
||||
style="margin-bottom: 12px"
|
||||
/>
|
||||
<el-row style="margin-bottom: 12px;">
|
||||
<el-button type="primary" size="small" :icon="Plus" @click="handleAddStudent">添加学生</el-button>
|
||||
</el-row>
|
||||
|
|
@ -134,18 +127,14 @@
|
|||
<template #empty>
|
||||
<el-empty description="暂无绑定学生" :image-size="60" />
|
||||
</template>
|
||||
<el-table-column prop="studentName" label="姓名" min-width="80" />
|
||||
<el-table-column prop="name" label="姓名" min-width="80" />
|
||||
<el-table-column prop="studentNo" label="学号" width="120" />
|
||||
<el-table-column prop="schoolName" label="学校" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="gradeName" label="年级" width="80" />
|
||||
<el-table-column prop="className" label="班级" width="60" />
|
||||
<el-table-column label="操作" width="80" align="center">
|
||||
<template #default="{ $index }">
|
||||
<el-popconfirm title="确定解绑该学生?" @confirm="handleRemoveStudent($index)">
|
||||
<template #reference>
|
||||
<el-button link type="danger" size="small">解绑</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
<el-button link type="danger" size="small" @click="handleRemoveStudent($index)">移除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
|
@ -158,21 +147,34 @@
|
|||
</template>
|
||||
|
||||
<script setup>
|
||||
import { addMember, getClassList, getGradeList, getMember, getRegionTree, getSchoolList, updateMember } from '@/api/pangu/member'
|
||||
import { Plus } from '@element-plus/icons-vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { reactive, ref } from 'vue'
|
||||
import request from '@/utils/request'
|
||||
import { computed, reactive, ref, watch } from 'vue'
|
||||
|
||||
const emit = defineEmits(['success'])
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
memberId: {
|
||||
type: [Number, null],
|
||||
default: null
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue', 'success'])
|
||||
|
||||
const visible = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (val) => emit('update:modelValue', val)
|
||||
})
|
||||
|
||||
const visible = ref(false)
|
||||
const isEdit = ref(false)
|
||||
const formRef = ref(null)
|
||||
const submitLoading = ref(false)
|
||||
|
||||
// 表单数据
|
||||
const form = reactive({
|
||||
memberId: null,
|
||||
phone: '',
|
||||
nickname: '',
|
||||
gender: '0',
|
||||
|
|
@ -182,8 +184,8 @@ const form = reactive({
|
|||
regionIds: [],
|
||||
regionId: null,
|
||||
schoolId: null,
|
||||
schoolGradeId: null,
|
||||
schoolClassId: null,
|
||||
gradeId: null,
|
||||
classId: null,
|
||||
students: []
|
||||
})
|
||||
|
||||
|
|
@ -192,20 +194,9 @@ const rules = {
|
|||
phone: [
|
||||
{ required: true, message: '请输入手机号', trigger: 'blur' },
|
||||
{ pattern: /^1[3-9]\d{9}$/, message: '手机号格式不正确', trigger: 'blur' }
|
||||
],
|
||||
identityType: [
|
||||
{ required: true, message: '请选择身份类型', trigger: 'change' }
|
||||
]
|
||||
}
|
||||
|
||||
// 教师身份额外校验规则
|
||||
const teacherRules = {
|
||||
regionId: [{ required: true, message: '请选择所属区域', trigger: 'change' }],
|
||||
schoolId: [{ required: true, message: '请选择所属学校', trigger: 'change' }],
|
||||
schoolGradeId: [{ required: true, message: '请选择所属年级', trigger: 'change' }],
|
||||
schoolClassId: [{ required: true, message: '请选择所属班级', trigger: 'change' }]
|
||||
}
|
||||
|
||||
// 下拉选项数据
|
||||
const regionTree = ref([])
|
||||
const schoolList = ref([])
|
||||
|
|
@ -213,38 +204,39 @@ const gradeList = ref([])
|
|||
const classList = ref([])
|
||||
|
||||
/**
|
||||
* 打开弹窗
|
||||
* 弹窗打开时加载数据
|
||||
*/
|
||||
const open = async (row) => {
|
||||
resetForm()
|
||||
isEdit.value = !!row
|
||||
visible.value = true
|
||||
|
||||
const handleOpen = async () => {
|
||||
// 加载区域树
|
||||
await loadRegionTree()
|
||||
try {
|
||||
const res = await getRegionTree()
|
||||
regionTree.value = res.data || []
|
||||
} catch (e) {
|
||||
// 忽略错误
|
||||
}
|
||||
|
||||
// 编辑模式加载会员数据
|
||||
if (row && row.memberId) {
|
||||
if (props.memberId) {
|
||||
try {
|
||||
const res = await request.get(`/business/member/${row.memberId}`)
|
||||
if (res.code === 200 && res.data) {
|
||||
const res = await getMember(props.memberId)
|
||||
if (res.data) {
|
||||
Object.assign(form, res.data)
|
||||
// 处理区域级联
|
||||
// 加载关联数据
|
||||
if (form.regionId) {
|
||||
// 构建regionIds数组用于级联选择器回显
|
||||
form.regionIds = [form.regionId]
|
||||
await loadSchoolList(form.regionId)
|
||||
}
|
||||
if (form.schoolId) {
|
||||
await loadGradeList(form.schoolId)
|
||||
}
|
||||
if (form.schoolGradeId) {
|
||||
await loadClassList(form.schoolGradeId)
|
||||
if (form.gradeId) {
|
||||
await loadClassList(form.gradeId)
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('加载会员数据失败', e)
|
||||
// 忽略错误
|
||||
}
|
||||
} else {
|
||||
resetForm()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -252,7 +244,6 @@ const open = async (row) => {
|
|||
* 重置表单
|
||||
*/
|
||||
const resetForm = () => {
|
||||
form.memberId = null
|
||||
form.phone = ''
|
||||
form.nickname = ''
|
||||
form.gender = '0'
|
||||
|
|
@ -262,24 +253,9 @@ const resetForm = () => {
|
|||
form.regionIds = []
|
||||
form.regionId = null
|
||||
form.schoolId = null
|
||||
form.schoolGradeId = null
|
||||
form.schoolClassId = null
|
||||
form.gradeId = null
|
||||
form.classId = null
|
||||
form.students = []
|
||||
schoolList.value = []
|
||||
gradeList.value = []
|
||||
classList.value = []
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载区域树
|
||||
*/
|
||||
const loadRegionTree = async () => {
|
||||
try {
|
||||
const res = await request.get('/business/region/tree')
|
||||
regionTree.value = res.data || []
|
||||
} catch (e) {
|
||||
regionTree.value = []
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -287,8 +263,8 @@ const loadRegionTree = async () => {
|
|||
*/
|
||||
const loadSchoolList = async (regionId) => {
|
||||
try {
|
||||
const res = await request.get('/business/school/list', { params: { regionId } })
|
||||
schoolList.value = res.rows || []
|
||||
const res = await getSchoolList(regionId)
|
||||
schoolList.value = res.data || []
|
||||
} catch (e) {
|
||||
schoolList.value = []
|
||||
}
|
||||
|
|
@ -299,7 +275,7 @@ const loadSchoolList = async (regionId) => {
|
|||
*/
|
||||
const loadGradeList = async (schoolId) => {
|
||||
try {
|
||||
const res = await request.get('/business/school/grades', { params: { schoolId } })
|
||||
const res = await getGradeList(schoolId)
|
||||
gradeList.value = res.data || []
|
||||
} catch (e) {
|
||||
gradeList.value = []
|
||||
|
|
@ -309,40 +285,23 @@ const loadGradeList = async (schoolId) => {
|
|||
/**
|
||||
* 加载班级列表
|
||||
*/
|
||||
const loadClassList = async (schoolGradeId) => {
|
||||
const loadClassList = async (gradeId) => {
|
||||
try {
|
||||
const res = await request.get('/business/school/classes', { params: { schoolGradeId } })
|
||||
const res = await getClassList(gradeId)
|
||||
classList.value = res.data || []
|
||||
} catch (e) {
|
||||
classList.value = []
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 身份类型变更
|
||||
*/
|
||||
const handleIdentityChange = () => {
|
||||
// 切换为家长时清空学校信息
|
||||
if (form.identityType === '1') {
|
||||
form.regionIds = []
|
||||
form.regionId = null
|
||||
form.schoolId = null
|
||||
form.schoolGradeId = null
|
||||
form.schoolClassId = null
|
||||
schoolList.value = []
|
||||
gradeList.value = []
|
||||
classList.value = []
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 区域变更
|
||||
*/
|
||||
const handleRegionChange = (val) => {
|
||||
form.regionId = val && val.length ? val[val.length - 1] : null
|
||||
form.schoolId = null
|
||||
form.schoolGradeId = null
|
||||
form.schoolClassId = null
|
||||
form.gradeId = null
|
||||
form.classId = null
|
||||
schoolList.value = []
|
||||
gradeList.value = []
|
||||
classList.value = []
|
||||
|
|
@ -355,8 +314,8 @@ const handleRegionChange = (val) => {
|
|||
* 学校变更
|
||||
*/
|
||||
const handleSchoolChange = () => {
|
||||
form.schoolGradeId = null
|
||||
form.schoolClassId = null
|
||||
form.gradeId = null
|
||||
form.classId = null
|
||||
gradeList.value = []
|
||||
classList.value = []
|
||||
if (form.schoolId) {
|
||||
|
|
@ -368,18 +327,18 @@ const handleSchoolChange = () => {
|
|||
* 年级变更
|
||||
*/
|
||||
const handleGradeChange = () => {
|
||||
form.schoolClassId = null
|
||||
form.classId = null
|
||||
classList.value = []
|
||||
if (form.schoolGradeId) {
|
||||
loadClassList(form.schoolGradeId)
|
||||
if (form.gradeId) {
|
||||
loadClassList(form.gradeId)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加学生
|
||||
* 添加学生(模拟,实际应该弹出学生选择器)
|
||||
*/
|
||||
const handleAddStudent = () => {
|
||||
ElMessage.info('学生选择功能待开发')
|
||||
ElMessage.info('请选择要绑定的学生')
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -401,30 +360,24 @@ const handleSubmit = async () => {
|
|||
|
||||
submitLoading.value = true
|
||||
try {
|
||||
const data = { ...form }
|
||||
// 移除不需要提交的字段
|
||||
delete data.regionIds
|
||||
delete data.students
|
||||
|
||||
if (isEdit.value) {
|
||||
const res = await request.put('/business/member', data)
|
||||
if (res.code === 200) {
|
||||
if (props.memberId) {
|
||||
await updateMember({ ...form, id: props.memberId })
|
||||
ElMessage.success('修改成功')
|
||||
visible.value = false
|
||||
emit('success')
|
||||
}
|
||||
} else {
|
||||
const res = await request.post('/business/member', data)
|
||||
if (res.code === 200) {
|
||||
await addMember(form)
|
||||
ElMessage.success('新增成功')
|
||||
}
|
||||
visible.value = false
|
||||
emit('success')
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
submitLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
// 监听弹窗关闭,重置表单
|
||||
watch(visible, (val) => {
|
||||
if (!val) {
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,101 +0,0 @@
|
|||
<!--
|
||||
重置密码成功弹窗
|
||||
@author pangu
|
||||
-->
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
title="密码重置成功"
|
||||
width="420px"
|
||||
:close-on-click-modal="false"
|
||||
destroy-on-close
|
||||
>
|
||||
<div class="reset-pwd-content">
|
||||
<el-icon class="success-icon" color="#67C23A" :size="48">
|
||||
<CircleCheckFilled />
|
||||
</el-icon>
|
||||
<p class="tip-text">密码已重置,请复制新密码发送给用户</p>
|
||||
<el-input
|
||||
v-model="password"
|
||||
readonly
|
||||
class="pwd-input"
|
||||
size="large"
|
||||
>
|
||||
<template #append>
|
||||
<el-button :icon="DocumentCopy" @click="handleCopy">复制</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button type="primary" @click="visible = false">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { CircleCheckFilled, DocumentCopy } from '@element-plus/icons-vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { ref } from 'vue'
|
||||
|
||||
const visible = ref(false)
|
||||
const password = ref('')
|
||||
|
||||
/**
|
||||
* 打开弹窗
|
||||
*/
|
||||
const open = (newPassword) => {
|
||||
password.value = newPassword || ''
|
||||
visible.value = true
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制密码到剪贴板
|
||||
*/
|
||||
const handleCopy = async () => {
|
||||
if (!password.value) {
|
||||
ElMessage.warning('没有可复制的密码')
|
||||
return
|
||||
}
|
||||
try {
|
||||
await navigator.clipboard.writeText(password.value)
|
||||
ElMessage.success('复制成功')
|
||||
} catch (e) {
|
||||
// 降级方案:使用 execCommand
|
||||
const textarea = document.createElement('textarea')
|
||||
textarea.value = password.value
|
||||
textarea.style.position = 'fixed'
|
||||
textarea.style.opacity = '0'
|
||||
document.body.appendChild(textarea)
|
||||
textarea.select()
|
||||
try {
|
||||
document.execCommand('copy')
|
||||
ElMessage.success('复制成功')
|
||||
} catch (err) {
|
||||
ElMessage.error('复制失败,请手动复制')
|
||||
}
|
||||
document.body.removeChild(textarea)
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.reset-pwd-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 20px 0;
|
||||
}
|
||||
.success-icon {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.tip-text {
|
||||
color: #606266;
|
||||
margin-bottom: 20px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.pwd-input {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -48,8 +48,8 @@
|
|||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="tableData" border stripe :header-cell-style="{ background: '#f5f7fa', color: '#606266' }" style="width: 100%">
|
||||
<el-table-column prop="memberCode" label="会员编号" width="160" />
|
||||
<el-table-column prop="phone" label="手机号" width="130">
|
||||
<el-table-column prop="memberNo" label="会员编号" width="140" />
|
||||
<el-table-column prop="phone" label="手机号" width="120">
|
||||
<template #default="{ row }">
|
||||
{{ maskPhone(row.phone) }}
|
||||
</template>
|
||||
|
|
@ -60,45 +60,28 @@
|
|||
{{ row.gender === '1' ? '男' : row.gender === '2' ? '女' : '未知' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="birthday" label="出生日期" width="110" />
|
||||
<el-table-column prop="identityType" label="身份类型" width="85" align="center">
|
||||
<el-table-column prop="birthday" label="出生日期" width="100" />
|
||||
<el-table-column prop="identityType" label="身份类型" width="80" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.identityType === '1' ? 'success' : 'warning'">
|
||||
{{ row.identityType === '1' ? '家长' : '教师' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="registerTime" label="注册时间" width="165" />
|
||||
<el-table-column prop="registerSource" label="注册来源" width="85" align="center">
|
||||
<template #default="{ row }">
|
||||
{{ formatRegisterSource(row.registerSource) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="openId" label="openId" width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="registerTime" label="注册时间" width="160" />
|
||||
<el-table-column prop="registerSource" label="注册来源" width="80" align="center" />
|
||||
<el-table-column prop="status" label="状态" width="70" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-switch
|
||||
v-model="row.status"
|
||||
active-value="0"
|
||||
inactive-value="1"
|
||||
@change="handleStatusChange(row)"
|
||||
/>
|
||||
<el-tag :type="row.status === '0' ? 'success' : 'danger'">
|
||||
{{ row.status === '0' ? '正常' : '停用' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="200" fixed="right" align="center">
|
||||
<el-table-column label="操作" width="180" fixed="right" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link :icon="Edit" @click="handleEdit(row)">编辑</el-button>
|
||||
<el-button type="warning" link :icon="Key" @click="handleResetPwd(row)">重置密码</el-button>
|
||||
<el-popconfirm
|
||||
title="确定要删除该会员吗?"
|
||||
confirm-button-text="确定"
|
||||
cancel-button-text="取消"
|
||||
@confirm="handleDelete(row)"
|
||||
>
|
||||
<template #reference>
|
||||
<el-button type="danger" link :icon="Delete">删除</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
<el-button type="danger" link :icon="Delete" @click="handleDelete(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
|
@ -111,13 +94,13 @@
|
|||
:total="total"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
style="margin-top: 16px; justify-content: flex-end"
|
||||
@size-change="getList"
|
||||
@current-change="getList"
|
||||
@size-change="handleQuery"
|
||||
@current-change="handleQuery"
|
||||
/>
|
||||
</el-card>
|
||||
|
||||
<!-- 新增/编辑弹窗 -->
|
||||
<MemberDialog ref="memberDialogRef" @success="getList" />
|
||||
<MemberDialog ref="memberDialogRef" @success="handleQuery" />
|
||||
|
||||
<!-- 重置密码弹窗 -->
|
||||
<ResetPwdDialog ref="resetPwdDialogRef" />
|
||||
|
|
@ -147,7 +130,9 @@ const queryParams = ref({
|
|||
phone: '',
|
||||
nickname: '',
|
||||
identityType: '',
|
||||
status: ''
|
||||
status: '',
|
||||
beginTime: '',
|
||||
endTime: ''
|
||||
})
|
||||
|
||||
const memberDialogRef = ref()
|
||||
|
|
@ -159,28 +144,22 @@ const maskPhone = (phone) => {
|
|||
return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2')
|
||||
}
|
||||
|
||||
// 格式化注册来源
|
||||
const formatRegisterSource = (source) => {
|
||||
const map = { '1': '小程序', '2': 'H5', '3': '后台新增', '4': '批量导入' }
|
||||
return map[source] || source
|
||||
}
|
||||
|
||||
// 获取会员列表
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const params = { ...queryParams.value }
|
||||
// 处理日期范围
|
||||
if (dateRange.value && dateRange.value.length === 2) {
|
||||
params.params = {
|
||||
beginRegisterTime: dateRange.value[0],
|
||||
endRegisterTime: dateRange.value[1]
|
||||
queryParams.value.beginTime = dateRange.value[0]
|
||||
queryParams.value.endTime = dateRange.value[1]
|
||||
} else {
|
||||
queryParams.value.beginTime = ''
|
||||
queryParams.value.endTime = ''
|
||||
}
|
||||
}
|
||||
const res = await request.get('/business/member/list', { params })
|
||||
try {
|
||||
const res = await request.get('/business/member/list', { params: queryParams.value })
|
||||
if (res.code === 200) {
|
||||
tableData.value = res.rows || []
|
||||
total.value = res.total || 0
|
||||
tableData.value = res.rows
|
||||
total.value = res.total
|
||||
}
|
||||
} finally {
|
||||
loading.value = false
|
||||
|
|
@ -201,7 +180,9 @@ const resetQuery = () => {
|
|||
phone: '',
|
||||
nickname: '',
|
||||
identityType: '',
|
||||
status: ''
|
||||
status: '',
|
||||
beginTime: '',
|
||||
endTime: ''
|
||||
}
|
||||
dateRange.value = []
|
||||
getList()
|
||||
|
|
@ -219,49 +200,22 @@ const handleEdit = (row) => {
|
|||
|
||||
// 重置密码
|
||||
const handleResetPwd = (row) => {
|
||||
ElMessageBox.confirm(`确定要重置会员"${row.nickname || row.phone}"的密码吗?`, '提示', {
|
||||
resetPwdDialogRef.value?.open(row)
|
||||
}
|
||||
|
||||
// 删除
|
||||
const handleDelete = (row) => {
|
||||
ElMessageBox.confirm(`确定要删除会员"${row.nickname}"吗?`, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(async () => {
|
||||
const res = await request.put(`/business/member/resetPwd/${row.memberId}`)
|
||||
if (res.code === 200) {
|
||||
resetPwdDialogRef.value?.open(res.data.password)
|
||||
}
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
// 删除
|
||||
const handleDelete = async (row) => {
|
||||
try {
|
||||
const res = await request.delete(`/business/member/${row.memberId}`)
|
||||
const res = await request.delete(`/business/member/${row.id}`)
|
||||
if (res.code === 200) {
|
||||
ElMessage.success('删除成功')
|
||||
getList()
|
||||
}
|
||||
} catch (e) {
|
||||
// 错误已在request中处理
|
||||
}
|
||||
}
|
||||
|
||||
// 状态变更
|
||||
const handleStatusChange = async (row) => {
|
||||
const text = row.status === '0' ? '启用' : '停用'
|
||||
try {
|
||||
const res = await request.put('/business/member/changeStatus', {
|
||||
memberId: row.memberId,
|
||||
status: row.status
|
||||
})
|
||||
if (res.code === 200) {
|
||||
ElMessage.success(`${text}成功`)
|
||||
} else {
|
||||
// 恢复原状态
|
||||
row.status = row.status === '0' ? '1' : '0'
|
||||
}
|
||||
} catch (e) {
|
||||
// 恢复原状态
|
||||
row.status = row.status === '0' ? '1' : '0'
|
||||
}
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
|
|
|||
|
|
@ -1,23 +1,34 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
title="新增-班级"
|
||||
title="新增班级"
|
||||
width="500px"
|
||||
:close-on-click-modal="false"
|
||||
destroy-on-close
|
||||
>
|
||||
<div style="margin-bottom: 16px;">
|
||||
<div><span style="font-weight: bold;">学校:</span><span>{{ currentSchool?.schoolName }}</span></div>
|
||||
<div><span style="font-weight: bold;">年级:</span><span>{{ currentSchool?.gradeName }}</span></div>
|
||||
<span style="font-weight: bold;">学校:</span>
|
||||
<span>{{ currentSchool?.schoolName }}</span>
|
||||
</div>
|
||||
|
||||
<el-form ref="formRef" :model="form" label-width="80px">
|
||||
<el-form-item label="选择年级" prop="schoolGradeId">
|
||||
<el-select v-model="form.schoolGradeId" placeholder="请先选择年级" style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in schoolGradeOptions"
|
||||
:key="item.id"
|
||||
:label="item.gradeName"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="选择班级" prop="classIds">
|
||||
<el-checkbox-group v-model="form.classIds">
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="8" v-for="item in classOptions" :key="item.value">
|
||||
<el-checkbox :label="item.value" :disabled="item.disabled" style="margin-bottom: 8px;">
|
||||
{{ item.label }}{{ item.disabled ? '(已添加)' : '' }}
|
||||
<el-checkbox :label="item.value" style="margin-bottom: 8px;">
|
||||
{{ item.label }}
|
||||
</el-checkbox>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
|
@ -34,12 +45,13 @@
|
|||
|
||||
<script setup>
|
||||
/**
|
||||
* 新增班级弹窗(为年级挂载班级)
|
||||
* 新增班级弹窗
|
||||
* @author pangu
|
||||
*/
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { addGradeClass, getClassOptions } from '@/api/pangu/school'
|
||||
import request from '@/utils/request'
|
||||
import { addGradeClass, getClassOptions, getSchoolGradeTree } from '@/api/pangu/school'
|
||||
|
||||
const emit = defineEmits(['success'])
|
||||
|
||||
|
|
@ -47,6 +59,7 @@ const dialogVisible = ref(false)
|
|||
const submitLoading = ref(false)
|
||||
const formRef = ref(null)
|
||||
const currentSchool = ref(null)
|
||||
const schoolGradeOptions = ref([])
|
||||
const classOptions = ref([])
|
||||
|
||||
// 表单数据
|
||||
|
|
@ -56,19 +69,27 @@ const form = ref({
|
|||
classIds: []
|
||||
})
|
||||
|
||||
// 已有班级ID列表
|
||||
const existingClassIds = ref([])
|
||||
// 获取学校已挂载的年级
|
||||
const fetchSchoolGrades = async (schoolId) => {
|
||||
try {
|
||||
const res = await getSchoolGradeTree(schoolId)
|
||||
if (res.code === 200) {
|
||||
schoolGradeOptions.value = res.data || []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取学校年级失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取班级选项
|
||||
const fetchClassOptions = async () => {
|
||||
try {
|
||||
const res = await getClassOptions()
|
||||
if (res.code === 200) {
|
||||
// 转换为选项格式 { value: classId, label: className }
|
||||
// 转换为选项格式
|
||||
classOptions.value = (res.data || []).map(item => ({
|
||||
value: item.classId,
|
||||
label: item.className,
|
||||
disabled: existingClassIds.value.includes(item.classId)
|
||||
label: item.className
|
||||
}))
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
@ -77,23 +98,24 @@ const fetchClassOptions = async () => {
|
|||
}
|
||||
|
||||
// 打开弹窗
|
||||
// schoolData: { schoolId, schoolName, schoolGradeId, gradeName, existingClassIds }
|
||||
const open = (schoolData) => {
|
||||
const open = (school) => {
|
||||
dialogVisible.value = true
|
||||
currentSchool.value = schoolData
|
||||
// 保存已有班级ID列表
|
||||
existingClassIds.value = schoolData.existingClassIds || []
|
||||
currentSchool.value = school
|
||||
form.value = {
|
||||
schoolId: schoolData.schoolId,
|
||||
schoolGradeId: schoolData.schoolGradeId,
|
||||
schoolId: school.schoolId,
|
||||
schoolGradeId: null,
|
||||
classIds: []
|
||||
}
|
||||
// 重新获取班级选项(更新禁用状态)
|
||||
fetchClassOptions()
|
||||
// 获取该学校已挂载的年级
|
||||
fetchSchoolGrades(school.schoolId)
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
const handleSubmit = async () => {
|
||||
if (!form.value.schoolGradeId) {
|
||||
ElMessage.warning('请选择年级')
|
||||
return
|
||||
}
|
||||
if (form.value.classIds.length === 0) {
|
||||
ElMessage.warning('请至少选择一个班级')
|
||||
return
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
title="新增-年级"
|
||||
title="新增年级"
|
||||
width="500px"
|
||||
:close-on-click-modal="false"
|
||||
destroy-on-close
|
||||
|
|
@ -16,8 +16,8 @@
|
|||
<el-checkbox-group v-model="form.gradeIds">
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="8" v-for="item in gradeOptions" :key="item.value">
|
||||
<el-checkbox :label="item.value" :disabled="item.disabled" style="margin-bottom: 8px;">
|
||||
{{ item.label }}{{ item.disabled ? '(已添加)' : '' }}
|
||||
<el-checkbox :label="item.value" style="margin-bottom: 8px;">
|
||||
{{ item.label }}
|
||||
</el-checkbox>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
|
@ -55,19 +55,15 @@ const form = ref({
|
|||
gradeIds: []
|
||||
})
|
||||
|
||||
// 已有年级ID列表
|
||||
const existingGradeIds = ref([])
|
||||
|
||||
// 获取年级选项
|
||||
const fetchGradeOptions = async () => {
|
||||
try {
|
||||
const res = await getGradeOptions()
|
||||
if (res.code === 200) {
|
||||
// 转换为选项格式 { value: gradeId, label: gradeName }
|
||||
// 转换为选项格式
|
||||
gradeOptions.value = (res.data || []).map(item => ({
|
||||
value: item.gradeId,
|
||||
label: item.gradeName,
|
||||
disabled: existingGradeIds.value.includes(item.gradeId)
|
||||
label: item.gradeName
|
||||
}))
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
@ -79,14 +75,10 @@ const fetchGradeOptions = async () => {
|
|||
const open = (school) => {
|
||||
dialogVisible.value = true
|
||||
currentSchool.value = school
|
||||
// 保存已有年级ID列表
|
||||
existingGradeIds.value = school.existingGradeIds || []
|
||||
form.value = {
|
||||
schoolId: school.schoolId,
|
||||
gradeIds: []
|
||||
}
|
||||
// 重新获取年级选项(更新禁用状态)
|
||||
fetchGradeOptions()
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
|
|
|
|||
|
|
@ -12,19 +12,16 @@
|
|||
:rules="rules"
|
||||
label-width="100px"
|
||||
>
|
||||
<el-form-item v-if="isEdit" label="学校编码">
|
||||
<el-input v-model="form.schoolCode" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="学校名称" prop="schoolName">
|
||||
<el-input v-model="form.schoolName" placeholder="请输入学校名称" maxlength="100" show-word-limit />
|
||||
<el-input v-model="form.schoolName" placeholder="请输入学校名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="学校类型" prop="schoolType">
|
||||
<el-select v-model="form.schoolType" placeholder="请选择学校类型" style="width: 100%">
|
||||
<el-option label="小学" value="1" />
|
||||
<el-option label="初中" value="2" />
|
||||
<el-option label="高中" value="3" />
|
||||
<el-option label="九年一贯制" value="4" />
|
||||
<el-option label="完全中学" value="5" />
|
||||
<el-option label="小学" value="小学" />
|
||||
<el-option label="初中" value="初中" />
|
||||
<el-option label="高中" value="高中" />
|
||||
<el-option label="九年一贯制" value="九年一贯制" />
|
||||
<el-option label="完全中学" value="完全中学" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="所属区域" prop="regionId">
|
||||
|
|
@ -87,7 +84,6 @@ const isEdit = ref(false)
|
|||
// 表单数据
|
||||
const form = ref({
|
||||
schoolId: null,
|
||||
schoolCode: '',
|
||||
schoolName: '',
|
||||
schoolType: '',
|
||||
regionId: null,
|
||||
|
|
@ -96,8 +92,8 @@ const form = ref({
|
|||
status: '0'
|
||||
})
|
||||
|
||||
// 弹窗标题 - 根据需求文档:编辑-学校
|
||||
const dialogTitle = computed(() => isEdit.value ? '编辑-学校' : '新增学校')
|
||||
// 弹窗标题
|
||||
const dialogTitle = computed(() => isEdit.value ? '编辑学校' : '新增学校')
|
||||
|
||||
// 表单验证规则
|
||||
const rules = {
|
||||
|
|
@ -141,9 +137,7 @@ const handleRegionChange = (value) => {
|
|||
}
|
||||
|
||||
// 打开弹窗
|
||||
// row: 编辑时传入学校数据,新增时为null
|
||||
// defaultRegionId: 新增时默认选中的区域ID(从列表页带入)
|
||||
const open = (row, defaultRegionId = null) => {
|
||||
const open = (row) => {
|
||||
dialogVisible.value = true
|
||||
isEdit.value = !!row
|
||||
|
||||
|
|
@ -151,7 +145,6 @@ const open = (row, defaultRegionId = null) => {
|
|||
// 编辑模式:回显数据
|
||||
form.value = {
|
||||
schoolId: row.schoolId,
|
||||
schoolCode: row.schoolCode || '',
|
||||
schoolName: row.schoolName,
|
||||
schoolType: row.schoolType,
|
||||
regionId: row.regionId,
|
||||
|
|
@ -160,16 +153,14 @@ const open = (row, defaultRegionId = null) => {
|
|||
status: row.status
|
||||
}
|
||||
} else {
|
||||
// 新增模式:重置表单,如果有默认区域则带入
|
||||
const regionIds = defaultRegionId ? getRegionIdPath(defaultRegionId) : []
|
||||
// 新增模式:重置表单
|
||||
form.value = {
|
||||
schoolId: null,
|
||||
schoolCode: '',
|
||||
schoolName: '',
|
||||
schoolType: '',
|
||||
regionId: defaultRegionId,
|
||||
regionIds: regionIds,
|
||||
regionName: defaultRegionId ? getRegionPath(regionIds, props.regionTree) : '',
|
||||
regionId: null,
|
||||
regionIds: [],
|
||||
regionName: '',
|
||||
status: '0'
|
||||
}
|
||||
}
|
||||
|
|
@ -179,24 +170,23 @@ const open = (row, defaultRegionId = null) => {
|
|||
const getRegionIdPath = (regionId) => {
|
||||
if (!regionId) return []
|
||||
|
||||
// 递归遍历树查找目标节点,返回从根到目标的完整路径
|
||||
const findPath = (nodes, targetId, path = []) => {
|
||||
for (const node of nodes) {
|
||||
const currentPath = [...path, node.regionId]
|
||||
if (node.regionId === targetId) {
|
||||
return currentPath
|
||||
const id = regionId.toString()
|
||||
const path = []
|
||||
|
||||
// 省级ID(1位)
|
||||
if (id.length >= 1) {
|
||||
path.push(parseInt(id.charAt(0)))
|
||||
}
|
||||
if (node.children && node.children.length > 0) {
|
||||
const result = findPath(node.children, targetId, currentPath)
|
||||
if (result.length > 0) {
|
||||
return result
|
||||
// 市级ID(2位)
|
||||
if (id.length >= 2) {
|
||||
path.push(parseInt(id.substring(0, 2)))
|
||||
}
|
||||
}
|
||||
}
|
||||
return []
|
||||
// 区级ID(3位)
|
||||
if (id.length >= 3) {
|
||||
path.push(parseInt(id))
|
||||
}
|
||||
|
||||
return findPath(props.regionTree, regionId)
|
||||
return path
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@
|
|||
:props="{ label: 'regionName', children: 'children' }"
|
||||
node-key="regionId"
|
||||
highlight-current
|
||||
default-expand-all
|
||||
:filter-node-method="filterNode"
|
||||
@node-click="handleNodeClick"
|
||||
/>
|
||||
|
|
@ -52,63 +51,45 @@
|
|||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 树形表格展示学校/年级/班级 -->
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="treeData"
|
||||
:row-key="row => `${row.type}_${row.id}`"
|
||||
border
|
||||
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
|
||||
:header-cell-style="{ background: '#f5f7fa', color: '#606266' }"
|
||||
style="width: 100%"
|
||||
@expand-change="handleExpandChange"
|
||||
>
|
||||
<el-table-column prop="name" label="学校名称" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column prop="schoolCode" label="学校编码" width="130" />
|
||||
<el-table-column prop="type" label="类型" width="80" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag v-if="row.type === 'school'" type="primary" size="small">学校</el-tag>
|
||||
<el-tag v-else-if="row.type === 'grade'" type="success" size="small">年级</el-tag>
|
||||
<el-tag v-else type="info" size="small">班级</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="regionName" label="所属区域" min-width="120" show-overflow-tooltip />
|
||||
<el-table v-loading="loading" :data="tableData" border stripe :header-cell-style="{ background: '#f5f7fa', color: '#606266' }" style="width: 100%">
|
||||
<el-table-column prop="schoolName" label="学校名称" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column prop="schoolType" label="学校类型" width="100" align="center" />
|
||||
<el-table-column prop="regionName" label="所属区域" min-width="180" show-overflow-tooltip />
|
||||
<el-table-column prop="status" label="状态" width="80" align="center">
|
||||
<template #default="{ row }">
|
||||
<template v-if="row.type === 'school'">
|
||||
<el-tag :type="row.status === '0' ? 'success' : 'danger'" size="small">
|
||||
<el-tag :type="row.status === '0' ? 'success' : 'danger'">
|
||||
{{ row.status === '0' ? '正常' : '停用' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="创建时间" width="160" />
|
||||
<el-table-column prop="createBy" label="创建人" width="100" />
|
||||
<el-table-column label="操作" width="220" fixed="right" align="center">
|
||||
<el-table-column label="操作" width="280" fixed="right" align="center">
|
||||
<template #default="{ row }">
|
||||
<!-- 学校操作:编辑、新增年级、删除 -->
|
||||
<template v-if="row.type === 'school'">
|
||||
<el-button type="primary" link size="small" @click="handleEdit(row)">编辑</el-button>
|
||||
<el-button type="primary" link size="small" @click="handleAddGrade(row)">新增年级</el-button>
|
||||
<el-button type="danger" link size="small" @click="handleDelete(row, 'school')">删除</el-button>
|
||||
</template>
|
||||
<!-- 年级操作:新增班级、删除 -->
|
||||
<template v-else-if="row.type === 'grade'">
|
||||
<el-button type="primary" link size="small" @click="handleAddClass(row)">新增班级</el-button>
|
||||
<el-button type="danger" link size="small" @click="handleDelete(row, 'grade')">删除</el-button>
|
||||
</template>
|
||||
<!-- 班级操作:删除 -->
|
||||
<template v-else>
|
||||
<el-button type="danger" link size="small" @click="handleDelete(row, 'class')">删除</el-button>
|
||||
</template>
|
||||
<el-button type="primary" link :icon="Edit" @click="handleEdit(row)">编辑</el-button>
|
||||
<el-button type="primary" link :icon="Collection" @click="handleAddGrade(row)">新增年级</el-button>
|
||||
<el-button type="primary" link :icon="Files" @click="handleAddClass(row)">新增班级</el-button>
|
||||
<el-button type="danger" link :icon="Delete" @click="handleDelete(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页 -->
|
||||
<el-pagination
|
||||
v-model:current-page="queryParams.pageNum"
|
||||
v-model:page-size="queryParams.pageSize"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:total="total"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
style="margin-top: 16px; justify-content: flex-end"
|
||||
@size-change="handleQuery"
|
||||
@current-change="handleQuery"
|
||||
/>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 编辑学校弹窗 -->
|
||||
<!-- 新增/编辑弹窗 -->
|
||||
<SchoolDialog ref="schoolDialogRef" :region-tree="regionTree" @success="handleQuery" />
|
||||
|
||||
<!-- 新增年级弹窗 -->
|
||||
|
|
@ -122,10 +103,9 @@
|
|||
<script setup>
|
||||
/**
|
||||
* 学校管理页面
|
||||
* 按区域树形展示学校、年级、班级结构
|
||||
* @author pangu
|
||||
*/
|
||||
import { Plus, Refresh, Search } from '@element-plus/icons-vue'
|
||||
import { Collection, Delete, Edit, Files, Plus, Refresh, Search } from '@element-plus/icons-vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { onMounted, ref, watch } from 'vue'
|
||||
import request from '@/utils/request'
|
||||
|
|
@ -141,10 +121,13 @@ const selectedRegionId = ref(null)
|
|||
|
||||
// 表格相关
|
||||
const loading = ref(false)
|
||||
const treeData = ref([])
|
||||
const tableData = ref([])
|
||||
const total = ref(0)
|
||||
|
||||
// 查询参数
|
||||
const queryParams = ref({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
schoolName: '',
|
||||
status: '',
|
||||
regionId: ''
|
||||
|
|
@ -174,184 +157,86 @@ const getRegionTree = async () => {
|
|||
}
|
||||
}
|
||||
|
||||
// 获取学校树形数据(包含年级和班级)
|
||||
const getSchoolTree = async () => {
|
||||
// 获取学校列表
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await request.get('/business/school/tree', { params: queryParams.value })
|
||||
const res = await request.get('/business/school/list', { params: queryParams.value })
|
||||
if (res.code === 200) {
|
||||
treeData.value = res.data
|
||||
tableData.value = res.rows
|
||||
total.value = res.total
|
||||
}
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 区域树节点点击 - 基于选择的区域筛选学校
|
||||
// 树节点点击
|
||||
const handleNodeClick = (data) => {
|
||||
selectedRegionId.value = data.regionId
|
||||
queryParams.value.regionId = data.regionId
|
||||
getSchoolTree()
|
||||
}
|
||||
|
||||
// 表格行展开/折叠时,自动选中左侧对应区域
|
||||
const handleExpandChange = (row, expanded) => {
|
||||
if (!expanded) return // 折叠时不处理
|
||||
|
||||
// 获取区域ID:学校直接取 regionId,年级/班级需要从父级学校获取
|
||||
let regionId = null
|
||||
if (row.type === 'school') {
|
||||
regionId = row.regionId
|
||||
} else if (row.type === 'grade' || row.type === 'class') {
|
||||
// 从 treeData 中查找所属学校的 regionId
|
||||
const school = treeData.value.find(s => s.id === row.schoolId)
|
||||
regionId = school?.regionId
|
||||
}
|
||||
|
||||
if (regionId) {
|
||||
// 设置左侧区域树的选中状态(高亮)
|
||||
treeRef.value?.setCurrentKey(regionId)
|
||||
selectedRegionId.value = regionId
|
||||
}
|
||||
queryParams.value.pageNum = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
// 搜索
|
||||
const handleQuery = () => {
|
||||
getSchoolTree()
|
||||
queryParams.value.pageNum = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
// 重置
|
||||
const resetQuery = () => {
|
||||
queryParams.value = {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
schoolName: '',
|
||||
status: '',
|
||||
regionId: ''
|
||||
}
|
||||
selectedRegionId.value = null
|
||||
treeRef.value?.setCurrentKey(null)
|
||||
getSchoolTree()
|
||||
getList()
|
||||
}
|
||||
|
||||
// 新增学校 - 基于选择的区域新增学校信息
|
||||
// 新增
|
||||
const handleAdd = () => {
|
||||
schoolDialogRef.value?.open(null, selectedRegionId.value)
|
||||
schoolDialogRef.value?.open()
|
||||
}
|
||||
|
||||
// 编辑学校
|
||||
// 编辑
|
||||
const handleEdit = (row) => {
|
||||
const schoolData = {
|
||||
schoolId: row.id,
|
||||
schoolCode: row.schoolCode,
|
||||
schoolName: row.name,
|
||||
schoolType: row.schoolType,
|
||||
regionId: row.regionId,
|
||||
regionName: row.regionName,
|
||||
status: row.status
|
||||
}
|
||||
schoolDialogRef.value?.open(schoolData)
|
||||
schoolDialogRef.value?.open(row)
|
||||
}
|
||||
|
||||
// 新增年级 - 学校下新增年级,选择对应的年级挂载
|
||||
const handleAddGrade = (row) => {
|
||||
// 获取学校已有的年级ID列表
|
||||
const existingGradeIds = (row.children || [])
|
||||
.filter(child => child.type === 'grade')
|
||||
.map(child => child.gradeId)
|
||||
|
||||
const schoolData = {
|
||||
schoolId: row.id,
|
||||
schoolName: row.name,
|
||||
existingGradeIds: existingGradeIds
|
||||
}
|
||||
gradeDialogRef.value?.open(schoolData)
|
||||
}
|
||||
|
||||
// 新增班级 - 年级下新增班级,选择对应的班级挂载
|
||||
const handleAddClass = (row) => {
|
||||
// 从 treeData 中查找学校名称
|
||||
const school = treeData.value.find(s => s.id === row.schoolId)
|
||||
// 获取年级已有的班级ID列表
|
||||
const existingClassIds = (row.children || [])
|
||||
.filter(child => child.type === 'class')
|
||||
.map(child => child.classId)
|
||||
|
||||
const schoolData = {
|
||||
schoolId: row.schoolId,
|
||||
schoolName: school ? school.name : '',
|
||||
schoolGradeId: row.id,
|
||||
gradeName: row.name,
|
||||
existingClassIds: existingClassIds
|
||||
}
|
||||
classDialogRef.value?.open(schoolData)
|
||||
}
|
||||
|
||||
// 统计子级数量
|
||||
const countChildren = (node) => {
|
||||
let gradeCount = 0
|
||||
let classCount = 0
|
||||
if (node.children && node.children.length > 0) {
|
||||
for (const child of node.children) {
|
||||
if (child.type === 'grade') {
|
||||
gradeCount++
|
||||
// 统计年级下的班级
|
||||
if (child.children && child.children.length > 0) {
|
||||
classCount += child.children.filter(c => c.type === 'class').length
|
||||
}
|
||||
} else if (child.type === 'class') {
|
||||
classCount++
|
||||
}
|
||||
}
|
||||
}
|
||||
return { gradeCount, classCount }
|
||||
}
|
||||
|
||||
// 删除操作 - 删除时提示是否有子级(有子级给出提示,确认后级联删除)
|
||||
const handleDelete = (row, type) => {
|
||||
let message = ''
|
||||
let url = ''
|
||||
|
||||
if (type === 'school') {
|
||||
const { gradeCount, classCount } = countChildren(row)
|
||||
if (gradeCount > 0 || classCount > 0) {
|
||||
// 有子级时,提示具体数量,让用户确认后级联删除
|
||||
let childInfo = []
|
||||
if (gradeCount > 0) childInfo.push(`${gradeCount}个年级`)
|
||||
if (classCount > 0) childInfo.push(`${classCount}个班级`)
|
||||
message = `学校"${row.name}"下存在${childInfo.join('、')},删除后其下的年级和班级也将被删除,确定要删除吗?`
|
||||
} else {
|
||||
message = `确定要删除学校"${row.name}"吗?`
|
||||
}
|
||||
url = `/business/school/${row.id}`
|
||||
} else if (type === 'grade') {
|
||||
const classCount = (row.children || []).filter(c => c.type === 'class').length
|
||||
if (classCount > 0) {
|
||||
// 有班级时,提示具体数量,让用户确认后级联删除
|
||||
message = `年级"${row.name}"下存在${classCount}个班级,删除后其下的班级也将被删除,确定要删除吗?`
|
||||
} else {
|
||||
message = `确定要删除年级"${row.name}"吗?`
|
||||
}
|
||||
url = `/business/school/grade/${row.id}`
|
||||
} else {
|
||||
message = `确定要删除班级"${row.name}"吗?`
|
||||
url = `/business/school/class/${row.id}`
|
||||
}
|
||||
|
||||
ElMessageBox.confirm(message, '提示', {
|
||||
// 删除
|
||||
const handleDelete = (row) => {
|
||||
ElMessageBox.confirm(`确定要删除学校"${row.schoolName}"吗?`, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(async () => {
|
||||
const res = await request.delete(url)
|
||||
const res = await request.delete(`/business/school/${row.schoolId}`)
|
||||
if (res.code === 200) {
|
||||
ElMessage.success('删除成功')
|
||||
getSchoolTree()
|
||||
getList()
|
||||
}
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
// 新增年级
|
||||
const handleAddGrade = (row) => {
|
||||
gradeDialogRef.value?.open(row)
|
||||
}
|
||||
|
||||
// 新增班级
|
||||
const handleAddClass = (row) => {
|
||||
classDialogRef.value?.open(row)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getRegionTree()
|
||||
getSchoolTree()
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -378,7 +378,7 @@ function handleAuthUser(row) {
|
|||
/** 查询菜单树结构 */
|
||||
function getMenuTreeselect() {
|
||||
menuTreeselect().then(response => {
|
||||
menuOptions.value = response.data || []
|
||||
menuOptions.value = response.data
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -450,16 +450,16 @@ function handleUpdate(row) {
|
|||
/** 根据角色ID查询菜单树结构 */
|
||||
function getRoleMenuTreeselect(roleId) {
|
||||
return roleMenuTreeselect(roleId).then(response => {
|
||||
menuOptions.value = response.data.menus
|
||||
return response.data
|
||||
menuOptions.value = response.menus
|
||||
return response
|
||||
})
|
||||
}
|
||||
|
||||
/** 根据角色ID查询部门树结构 */
|
||||
function getDeptTree(roleId) {
|
||||
return deptTreeSelect(roleId).then(response => {
|
||||
deptOptions.value = response.data.depts
|
||||
return response.data
|
||||
deptOptions.value = response.depts
|
||||
return response
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue