Bladeren bron

学校管理修复问题

jinxia.mo 1 dag geleden
bovenliggende
commit
dec07ee754

+ 5 - 5
back-ui/src/views/dz/agent/index.vue

@@ -3,7 +3,7 @@
       <el-row :gutter="20">
           <splitpanes :horizontal="appStore.device === 'mobile'" class="default-theme">
               <!--机构数据-->
-              <pane size="16">
+              <!-- <pane size="16">
                   <el-col>
                       <div class="head-container">
                           <el-input v-model="deptName" placeholder="请输入机构名称" clearable prefix-icon="Search" style="margin-bottom: 20px" />
@@ -12,7 +12,7 @@
                           <el-tree :data="deptOptions" :props="{ label: 'label', children: 'children' }" :expand-on-click-node="false" :filter-node-method="filterNode" ref="deptTreeRef" node-key="id" highlight-current default-expand-all @node-click="handleNodeClick" />
                       </div>
                   </el-col>
-              </pane>
+              </pane> -->
               <pane size="84">
                   <el-col>
                       <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
@@ -24,14 +24,14 @@
                                       @keyup.enter="handleQuery"
                               />
                           </el-form-item>
-                          <el-form-item label="联系电话" prop="phonenumber">
+                          <!-- <el-form-item label="联系电话" prop="phonenumber">
                               <el-input
                                       v-model="queryParams.phonenumber"
                                       placeholder="请输入联系电话"
                                       clearable
                                       @keyup.enter="handleQuery"
                               />
-                          </el-form-item>
+                          </el-form-item> -->
                           <el-form-item>
                               <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
                               <el-button icon="Refresh" @click="resetQuery">重置</el-button>
@@ -75,7 +75,7 @@
                           <el-table-column label="账号/邀请码" align="center" prop="agentId" />
 <!--                          <el-table-column label="机构" align="center" prop="deptId" />-->
                           <el-table-column label="归属机构" align="center" key="deptName" prop="dept.deptName" :show-overflow-tooltip="true" />
-                          <el-table-column label="联系电话" align="center" prop="phonenumber" />
+                          <!-- <el-table-column label="联系电话" align="center" prop="phonenumber" /> -->
                           <!--      <el-table-column label="上级代理商ID" align="center" prop="parentId" />-->
 <!--                          <el-table-column label="学校/校区" align="center" prop="schools" />-->
 <!--                          <el-table-column label="学校/校区" align="center" prop="schoolName" />-->

+ 142 - 15
back-ui/src/views/dz/school/index.vue

@@ -46,6 +46,16 @@
                   />
                 </el-select>
               </el-form-item>
+              <el-form-item label="学校类型" prop="examTypes">
+                <el-select v-model="queryParams.examTypes" placeholder="请选择学校类型" clearable style="width: 170px">
+                  <el-option
+                          v-for="dict in exam_type"
+                          :key="dict.value"
+                          :label="dict.label"
+                          :value="dict.value"
+                  />
+                </el-select>
+              </el-form-item>
               <el-form-item>
                 <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
                 <el-button icon="Refresh" @click="resetQuery">重置</el-button>
@@ -96,7 +106,7 @@
 
             <el-table v-loading="loading" :data="schoolList" @selection-change="handleSelectionChange">
               <el-table-column type="selection" width="55" align="center" />
-              <el-table-column label="ID" align="center" prop="id" />
+              <el-table-column label="ID" align="center" prop="id" width="80" />
               <el-table-column label="名称" align="center" prop="name" />
 <!--              <el-table-column label="归属机构" align="center" key="deptName" prop="dept.deptName" :show-overflow-tooltip="true" />-->
 <!--              <el-table-column label="省份" align="center" prop="location" />-->
@@ -106,6 +116,11 @@
                   <dict-tag :options="bool_values" :value="scope.row.status"/>
                 </template>
               </el-table-column>
+              <el-table-column label="学校类型" align="center" prop="examTypes">
+                <template #default="scope">
+                  <span>{{ formatExamTypes(scope.row.examTypes) }}</span>
+                </template>
+              </el-table-column>
               <el-table-column label="备注" align="center" prop="remark" />
               <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
                 <template #default="scope">
@@ -136,11 +151,22 @@
 <!--        <el-form-item label="归属机构" prop="deptId">-->
 <!--          <el-tree-select v-model="form.deptId" :data="enabledDeptOptions" :props="{ value: 'id', label: 'label', children: 'children' }" value-key="id" placeholder="请选择归属机构" clearable check-strictly />-->
 <!--        </el-form-item>-->
-        <el-form-item label="省份" prop="location">
+        <!-- <el-form-item label="省份" prop="location">
           <el-input v-model="form.location" placeholder="请输入省份" />
+        </el-form-item> -->
+        <el-form-item label="省市区" prop="areaIds">
+          <AddressSelect v-model="areaIds" style="width: 100%" />
         </el-form-item>
-        <el-form-item label="省市区" prop="areaIds" style="width: 220px;">
-          <AddressSelect class="w-[198px]" v-model="areaIds" />
+        
+        <el-form-item label="学校类型" prop="examTypes">
+          <el-select v-model="form.examTypes" placeholder="请选择学校类型" multiple>
+            <el-option
+                    v-for="dict in exam_type"
+                    :key="dict.value"
+                    :label="dict.label"
+                    :value="dict.value"
+            ></el-option>
+          </el-select>
         </el-form-item>
         <el-form-item label="状态" prop="status">
           <el-select v-model="form.status" placeholder="请选择状态">
@@ -167,16 +193,17 @@
 </template>
 
 <script setup name="School">
-  import { listSchool, getSchool, delSchool, addSchool, updateSchool } from "@/api/dz/school"
+  import { listSchool, getSchool, delSchool, addSchool, updateSchool, checkSchoolName } from "@/api/dz/school"
   import { deptTreeSelect } from "@/api/system/user"
   import useAppStore from '@/store/modules/app'
   import { Splitpanes, Pane } from "splitpanes"
   import "splitpanes/dist/splitpanes.css"
   import AddressSelect from '@/components/AddressSelect';
+  import { debounce } from '@/utils/index';
 
   const { proxy } = getCurrentInstance()
   const appStore = useAppStore()
-  const { bool_values } = proxy.useDict('bool_values')
+  const { bool_values, exam_type } = proxy.useDict('bool_values', 'exam_type')
   const deptName = ref("")
   const deptOptions = ref(undefined)
   const enabledDeptOptions = ref(undefined)
@@ -195,6 +222,9 @@
   const areaIds = ref([]);
   let selectedIds = [];
 
+  const schoolRef = ref(null);
+
+  // 先创建 data 对象,但先不定义 rules
   const data = reactive({
     form: {},
     queryParams: {
@@ -207,19 +237,67 @@
       city: null,
       area: null,
       status: null,
+      examTypes: null,
     },
-    rules: {
-      // status: [
-      //   { required: true, message: "状态不能为空", trigger: "change" },
-      //   { required: true, message: "机构不能为空", trigger: "change" }
-      // ],
-    }
+    rules: {}
   })
 
+  // 名称校验函数(在 data 定义之后,可以访问 data.form)
+  const validateSchoolName = (rule, value, callback) => {
+    if (!value) {
+      callback()
+      return
+    }
+    const schoolId = data.form.id || null
+    checkSchoolName(value, schoolId).then(response => {
+      if (response.data) {
+        callback()
+      } else {
+        callback(new Error('学校名称已存在,请使用其他名称'))
+      }
+    }).catch(() => {
+      callback()
+    })
+  }
+
+  // 设置 rules(在 validateSchoolName 定义之后)
+  data.rules = {
+    name: [
+      { required: true, message: "名称不能为空", trigger: "blur" },
+      { validator: validateSchoolName, trigger: "blur" }
+    ]
+    // status: [
+    //   { required: true, message: "状态不能为空", trigger: "change" },
+    //   { required: true, message: "机构不能为空", trigger: "change" }
+    // ],
+  }
+
   const { queryParams, form, rules } = toRefs(data)
   const queryRef = ref(null);
   const dateRange = ref([]);
 
+  // 防抖校验函数
+  const debouncedValidateName = debounce((name) => {
+    if (!name || !schoolRef.value) return
+    const schoolId = form.value.id || null
+    checkSchoolName(name, schoolId).then(response => {
+      if (!response.data) {
+        // 名称不唯一,触发校验错误
+        schoolRef.value.validateField('name', () => {})
+      } else {
+        // 名称唯一,清除错误
+        schoolRef.value.clearValidate('name')
+      }
+    })
+  }, 500)
+
+  // 监听名称变化(仅在对话框打开时校验)
+  watch(() => form.value.name, (newName) => {
+    if (open.value && newName && newName.trim()) {
+      debouncedValidateName(newName.trim())
+    }
+  })
+
   /** 查询机构校区列表 */
   function getList() {
     loading.value = true
@@ -299,10 +377,16 @@
       city: null,
       area: null,
       status: null,
+      examTypes: [],
       createTime: null,
       updateTime: null
     }
-    proxy.resetForm("schoolRef")
+    // 清空省市区选择
+    areaIds.value = []
+    if (schoolRef.value) {
+      schoolRef.value.resetFields()
+      schoolRef.value.clearValidate()
+    }
   }
 
   /** 搜索按钮操作 */
@@ -346,6 +430,25 @@
     const _id = row.id || ids.value
     getSchool(_id).then(response => {
       form.value = response.data
+      // 将 examTypes 字符串转换为数组(如果是逗号分隔的字符串)
+      if (form.value.examTypes && typeof form.value.examTypes === 'string') {
+        form.value.examTypes = form.value.examTypes.split(',').filter(item => item.trim())
+      } else if (!form.value.examTypes) {
+        form.value.examTypes = []
+      }
+      // 将省市区数据转换为 areaIds 数组格式,用于 AddressSelect 组件回显
+      // 按照顺序构建数组:[省, 市, 区],只包含有值的部分
+      const areaArray = []
+      if (form.value.pro) {
+        areaArray.push(form.value.pro)
+        if (form.value.city) {
+          areaArray.push(form.value.city)
+          if (form.value.area) {
+            areaArray.push(form.value.area)
+          }
+        }
+      }
+      areaIds.value = areaArray
       open.value = true
       title.value = "修改机构校区"
     })
@@ -353,7 +456,7 @@
 
   /** 提交按钮 */
   function submitForm() {
-    proxy.$refs["schoolRef"].validate(valid => {
+    schoolRef.value.validate(valid => {
       if (valid) {
         // 构建省市区对象
         let areaObj = {
@@ -362,10 +465,19 @@
           area: areaIds.value?.[2] || ''
         };
 
+        // 处理 examTypes:如果是数组,转换为逗号分隔的字符串
+        let examTypesValue = form.value.examTypes
+        if (Array.isArray(examTypesValue)) {
+          examTypesValue = examTypesValue.length > 0 ? examTypesValue.join(',') : null
+        } else if (!examTypesValue) {
+          examTypesValue = null
+        }
+
         // 合并表单数据和省市区数据
         const submitData = {
           ...form.value,
-          ...areaObj
+          ...areaObj,
+          examTypes: examTypesValue
         };
 
         if (form.value.id != null) {
@@ -405,6 +517,21 @@
     }, `school_${new Date().getTime()}.xlsx`)
   }
 
+  /** 格式化学校类型显示 */
+  function formatExamTypes(examTypes) {
+    if (!examTypes) return ''
+    // 将字符串按逗号分割
+    const types = String(examTypes).split(',').map(item => item.trim()).filter(item => item)
+    if (types.length === 0) return ''
+    // 根据字典查找对应的标签
+    const labels = types.map(type => {
+      const dictItem = exam_type.value.find(item => item.value === type)
+      return dictItem ? dictItem.label : type
+    })
+    // 用英文逗号连接
+    return labels.join(',')
+  }
+
   onMounted(() => {
     // getDeptTree()
     getList()

+ 11 - 0
ie-admin/src/main/java/com/ruoyi/web/controller/dz/DzSchoolController.java

@@ -29,6 +29,7 @@ import org.springframework.web.bind.annotation.DeleteMapping;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 import com.ruoyi.common.annotation.Log;
 import com.ruoyi.common.core.controller.BaseController;
@@ -227,4 +228,14 @@ public class DzSchoolController extends BaseController
     {
         return toAjax(dzSchoolService.deleteDzSchoolByIds(ids));
     }
+
+    /**
+     * 校验学校名称是否唯一
+     */
+    @GetMapping("/checkSchoolName")
+    public AjaxResult checkSchoolName(@RequestParam("name") String name, @RequestParam(value = "id", required = false) Long id)
+    {
+        boolean isUnique = dzSchoolService.checkSchoolName(name, id);
+        return AjaxResult.success(isUnique);
+    }
 }

+ 9 - 0
ie-system/src/main/java/com/ruoyi/dz/mapper/DzSchoolMapper.java

@@ -60,4 +60,13 @@ public interface DzSchoolMapper
      * @return 结果
      */
     public int deleteDzSchoolByIds(Long[] ids);
+
+    /**
+     * 校验学校名称是否唯一
+     *
+     * @param name 学校名称
+     * @param id 学校ID(修改时传入,新增时传null)
+     * @return 学校数量
+     */
+    public int checkSchoolName(@Param("name") String name, @Param("id") Long id);
 }

+ 9 - 0
ie-system/src/main/java/com/ruoyi/dz/service/IDzSchoolService.java

@@ -58,4 +58,13 @@ public interface IDzSchoolService
      * @return 结果
      */
     public int deleteDzSchoolById(Long id);
+
+    /**
+     * 校验学校名称是否唯一
+     *
+     * @param name 学校名称
+     * @param id 学校ID(修改时传入,新增时传null)
+     * @return true-唯一,false-不唯一
+     */
+    public boolean checkSchoolName(String name, Long id);
 }

+ 14 - 0
ie-system/src/main/java/com/ruoyi/dz/service/impl/DzSchoolServiceImpl.java

@@ -111,4 +111,18 @@ public class DzSchoolServiceImpl implements IDzSchoolService
     {
         return dzSchoolMapper.deleteDzSchoolById(id);
     }
+
+    /**
+     * 校验学校名称是否唯一
+     *
+     * @param name 学校名称
+     * @param id 学校ID(修改时传入,新增时传null)
+     * @return true-唯一,false-不唯一
+     */
+    @Override
+    public boolean checkSchoolName(String name, Long id)
+    {
+        int count = dzSchoolMapper.checkSchoolName(name, id);
+        return count == 0;
+    }
 }

+ 9 - 2
ie-system/src/main/resources/mapper/dz/DzSchoolMapper.xml

@@ -31,7 +31,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     </resultMap>
 
     <sql id="selectDzSchoolVo">
-        select t1.id, t1.name, t1.dept_id, t1.location, t1.remark, t1.pro, t1.city, t1.area, t1.status, t1.create_time, t1.update_time,
+        select t1.id, t1.name, t1.dept_id, t1.location, t1.remark, t1.pro, t1.city, t1.area, t1.status, t1.create_time, t1.update_time,t1.exam_types,
         d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.status as dept_status
          from dz_school t1
         left join sys_dept d on t1.dept_id = d.dept_id
@@ -55,7 +55,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="campus != null and campus"> and t1.dept_id &gt; 0</if>
             <if test="campus != null and not campus"> and (t1.dept_id = 0 or t1.dept_id is null)</if>
         </where>
-        order by t1.dept_id &gt; 0, t1.name
+        order by t1.id desc
     </select>
 
     <select id="selectDzSchoolListByIds" resultMap="DzSchoolResult">
@@ -131,4 +131,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             #{id}
         </foreach>
     </delete>
+
+    <select id="checkSchoolName" resultType="int">
+        select count(1) from dz_school where name = #{name}
+        <if test="id != null">
+            and id != #{id}
+        </if>
+    </select>
 </mapper>