zhangguohua 1 рік тому
батько
коміт
44a19a8432

+ 1 - 1
.env.production

@@ -1 +1 @@
-VITE_API_BASE_URL = "https://dev.k12100.net/admin/adminApi"
+VITE_API_BASE_URL = "https://dev3.k12100.net/admin/adminApi"

+ 33 - 0
src/api/examination.ts

@@ -0,0 +1,33 @@
+import request from '../utils/request.ts'
+// 联考学校列表
+export function getJoinExamList(data: any) {
+  return request({
+    url: `/adminApi/v1/listSchoolICh`,
+    method: 'get',
+    data
+  })
+}
+// 单考学校列表
+export function getSingleExamList(data: any) {
+  return request({
+    url: `/adminApi/v1/schoolOneAll`,
+    method: 'get',
+    data
+  })
+}
+// 新增学校
+export function addSingleSchool(data: any) {
+  return request({
+    url: `/adminApi/v1/admin-child-schools`,
+    method: 'post',
+    data
+  })
+}
+// 删除学校
+export function deleteSchool(data: any) {
+  return request({
+    url: `/adminApi/v1/admin-child-schools`,
+    method: 'delete',
+    data
+  })
+}

+ 2 - 7
src/api/login.ts

@@ -1,14 +1,9 @@
 // src/api/login.ts
 import request from '../utils/request.ts'
-
-
-
-
-
 // 登录接口
 export function login(data:any) {
   return request({
-    url: '/api/v1/teach_login/sign_up',
+    url: '/adminApi/v1/auth/login',
     method: 'post',
     data
   })
@@ -17,7 +12,7 @@ export function login(data:any) {
 // 获取用户信息
 export function getUserInfo() {
   return request({
-    url: '/api/v1/pc_personal/find_user_info',
+    url: '/adminApi/v1/sys/users/info',
     method: 'get'
   })
 }

+ 114 - 0
src/api/school.ts

@@ -0,0 +1,114 @@
+import request from '../utils/request.ts'
+
+// 获取学校数量
+export function getSchoolNumber() {
+  return request({
+    url: '/adminApi/v1/adminSchoolNumber',
+    method: 'get'
+  })
+}
+// 获取学校管理列表
+export function getSchoolList(data: any) {
+  return request({
+    url: '/adminApi/v1/adminSchoolInfosAll',
+    method: 'post',
+    data
+  })
+}
+// 获取学校管理筛选列表
+export function getSearchList() {
+  return request({
+    url: '/adminApi/v1/provinceCityTree',
+    method: 'get'
+  })
+}
+// 学校管理--新增用户
+export function addUser(data: any) {
+  return request({
+    url: '/adminApi/v1/adminSchoolInfos',
+    method: 'post',
+    data
+  })
+}
+// 学校管理--运维人员列表
+export function getUserList() {
+  return request({
+    url: '/adminApi/v1/sys/userList',
+    method: 'get'
+  })
+}
+// 学校管理--学校logo上传
+export function uploadFile(data: any) {
+  return request({
+    url: '/adminApi/v1/oss/oss/upload_filesSele',
+    method: 'post',
+    data
+  })
+}
+// 学校管理--省市区树列表
+export function provinceTree() {
+  return request({
+    url: '/adminApi/v1/provinceTree',
+    method: 'get'
+  })
+}
+// 学校管理--查看详情
+export function getSchoolInfo(id: any) {
+  return request({
+    url: `/adminApi/v1/admin-school-infos/${id}`,
+    method: 'get'
+  })
+}
+// 学校管理--编辑
+export function editSchoolInfo(data: any) {
+  return request({
+    url: `/adminApi/v1/adminSchoolInfos`,
+    method: 'put',
+    data
+  })
+}
+// 学校管理--改变状态(冻结/解冻)
+export function changeStatus(data: any) {
+  return request({
+    url: `/adminApi/v1/adminSchoolInfos/status`,
+    method: 'put',
+    data
+  })
+}
+
+
+// 学校管理--查询所有年级
+export function getAllGrade(data: any) {
+  return request({
+    url: `/adminApi/v1/admin/school/grade/pageList`,
+    method: 'get',
+    data
+  })
+}
+
+
+
+// 学校管理--查询所有科目
+export function getAllSubject(data: any) {
+  return request({
+    url: `/adminApi/v1/admin/school/course/getAllList`,
+    method: 'get',
+    data
+  })
+}
+// 学校管理--新增科目
+export function addSubject(data: any) {
+  return request({
+    url: `/adminApi/v1/admin/school/course/courses`,
+    method: 'post',
+    data
+  })
+}
+// 学校管理--删除科目
+export function deleteSubject(data: any) {
+  return request({
+    url: `/adminApi/v1/admin/school/course/deleteByIds`,
+    method: 'delete',
+    data
+  })
+}

BIN
src/assets/bg/table_no_data.png


+ 4 - 0
src/assets/icon/add.svg

@@ -0,0 +1,4 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect x="7" y="1" width="1.8" height="14" rx="0.9" fill="#2E64FA"/>
+<rect x="15" y="7" width="1.8" height="14" rx="0.9" transform="rotate(90 15 7)" fill="#2E64FA"/>
+</svg>

+ 11 - 0
src/assets/icon/cancel.svg

@@ -0,0 +1,11 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_7_8568)">
+<path d="M3.21973 3.01758C3.49642 2.83507 3.87407 2.86534 4.11719 3.1084L12.8936 11.8838L12.9844 11.9951C13.1656 12.2708 13.1345 12.6487 12.8916 12.8916C12.6485 13.1347 12.2709 13.1649 11.9941 12.9824L11.8828 12.8916L3.1084 4.11719C2.83074 3.83947 2.8307 3.38609 3.1084 3.1084L3.21973 3.01758Z" fill="#BBBBBB" stroke="#BBBBBB" stroke-width="0.2"/>
+<path d="M11.9951 3.0166C12.2708 2.83514 12.6486 2.86552 12.8916 3.1084L12.9824 3.21973C13.139 3.457 13.1388 3.76851 12.9824 4.00586L12.8916 4.11719L4.11719 12.8916C3.87418 13.1346 3.49641 13.1657 3.21973 12.9834L3.1084 12.8916C2.83078 12.6139 2.83072 12.1605 3.1084 11.8828L11.8838 3.10645L11.9951 3.0166Z" fill="#BBBBBB" stroke="#BBBBBB" stroke-width="0.2"/>
+</g>
+<defs>
+<clipPath id="clip0_7_8568">
+<rect width="16" height="16" fill="white"/>
+</clipPath>
+</defs>
+</svg>

+ 3 - 0
src/assets/icon/confirm.svg

@@ -0,0 +1,3 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M13.0542 3.67993C13.352 3.43496 13.7932 3.45214 14.0718 3.73071L14.1235 3.78833C14.3503 4.06646 14.3505 4.46732 14.1235 4.74536L14.0718 4.80298L6.90674 11.968C6.50596 12.3688 5.87223 12.3961 5.43994 12.0481L5.35596 11.9739L1.73486 8.40259C1.43554 8.10732 1.43364 7.62471 1.73096 7.32739L1.78857 7.27563C2.08607 7.03294 2.52482 7.05023 2.80225 7.32739L5.99072 10.5159C6.06906 10.5942 6.1969 10.5945 6.2749 10.5159L12.9966 3.73267L13.0542 3.67993Z" fill="#2E64FA" stroke="#2E64FA" stroke-width="0.2"/>
+</svg>

+ 11 - 0
src/assets/icon/delete.svg

@@ -0,0 +1,11 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_7_8551)">
+<path d="M3.21973 3.01758C3.49642 2.83507 3.87407 2.86534 4.11719 3.1084L12.8936 11.8838L12.9844 11.9951C13.1656 12.2708 13.1345 12.6487 12.8916 12.8916C12.6485 13.1347 12.2709 13.1649 11.9941 12.9824L11.8828 12.8916L3.1084 4.11719C2.83074 3.83947 2.8307 3.38609 3.1084 3.1084L3.21973 3.01758Z" fill="#2E64FA" stroke="#2E64FA" stroke-width="0.2"/>
+<path d="M11.9951 3.0166C12.2708 2.83514 12.6486 2.86552 12.8916 3.1084L12.9824 3.21973C13.139 3.457 13.1388 3.76851 12.9824 4.00586L12.8916 4.11719L4.11719 12.8916C3.87418 13.1346 3.49641 13.1657 3.21973 12.9834L3.1084 12.8916C2.83078 12.6139 2.83072 12.1605 3.1084 11.8828L11.8838 3.10645L11.9951 3.0166Z" fill="#2E64FA" stroke="#2E64FA" stroke-width="0.2"/>
+</g>
+<defs>
+<clipPath id="clip0_7_8551">
+<rect width="16" height="16" fill="white"/>
+</clipPath>
+</defs>
+</svg>

+ 9 - 0
src/assets/school/default_logo.svg

@@ -0,0 +1,9 @@
+<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
+<circle cx="24" cy="24" r="22" fill="#EFEFEF"/>
+<path d="M13 18.1142C13 16.8463 13.797 15.7154 14.991 15.2889L24.3273 11.9545C25.6298 11.4894 27 12.4549 27 13.838V35H13V18.1142Z" fill="#D2D2D2"/>
+<path d="M27 18H32C33.6569 18 35 19.3431 35 21V35H27V18Z" fill="#DDDDDD"/>
+<rect x="17" y="20" width="6" height="1.5" rx="0.75" fill="#EFEFEF"/>
+<rect x="10" y="34" width="28" height="2" rx="1" fill="#D2D2D2"/>
+<rect x="17" y="23.5" width="6" height="1.5" rx="0.75" fill="#EFEFEF"/>
+<rect x="17" y="27" width="6" height="1.5" rx="0.75" fill="#EFEFEF"/>
+</svg>

+ 0 - 127
src/components/SearchMain.vue

@@ -1,127 +0,0 @@
-<template>
-  <div class="module_content">
-    <div class="tag_group" v-for="(itemData, index) in searchData" :key="index">
-      <template v-if="itemData.list.length > 0">
-        <div class="tag_group_title">{{ itemData.label }}:</div>
-        <div class="tag_group_content">
-          <div class="tag_item" v-for="item in itemData.list" :key="item.value" @click="selectItem(index, item.value, itemData)" :class="item.value == itemData.value ? 'active' : ''">
-            {{ item.label }}
-          </div>
-        </div>
-      </template>
-    </div>
-  </div>
-</template>
-
-<script>
-import { ref } from 'vue'
-export default {
-  name: 'SearchMain',
-  setup(props, context) {
-    const searchData = ref([
-      {
-        label: '省份',
-        value: '0',
-        type: 'province',
-        list: [
-          { label: '全部', value: '0' },
-          { label: '北京', value: '1' },
-          { label: '上海', value: '2' },
-          { label: '天津', value: '3' },
-          { label: '河北', value: '4' },
-          { label: '山西', value: '5' },
-        ]
-      },
-      {
-        label: '城市',
-        value: '0',
-        type: 'city',
-        list: [
-          { label: '全部', value: '0' },
-          { label: '东城', value: '1' },
-          { label: '西城', value: '2' },
-          { label: '南城', value: '3' },
-          { label: '北城', value: '4' },
-          { label: '中城', value: '5' },
-        ]
-      },
-      {
-        label: '运维',
-        value: '0',
-        type: 'admin',
-        list: [
-          { label: '全部', value: '0' },
-          { label: '运维1号', value: '1' },
-          { label: '运维2号', value: '2' },
-          { label: '运维3号', value: '3' },
-          { label: '运维4号', value: '4' },
-          { label: '运维5号', value: '5' },
-        ]
-      },
-      {
-        label: '状态',
-        value: '0',
-        type: 'status',
-        list: [
-          { label: '全部', value: '0' },
-          { label: '正式用户', value: '1' },
-          { label: '体验用户', value: '2' },
-          { label: '冻结用户', value: '3' },
-        ]
-      }
-    ])
-    const selectItem = (index, val, item) => {
-      if(searchData.value[index].value == val) return
-      let d = { index, value: val}
-      context.emit('selectItem', d, item)
-    }
-    return {
-      searchData, selectItem
-    }
-  }
-}
-</script>
-
-<style scoped>
-.filters_group {
-  background: #fff;
-}
-.tag_group{
-  padding:8px 0;
-  display: flex;
-  justify-content: flex-start;
-}
-.tag_group_title {
-  width: auto;
-  height: auto;
-  margin-right: 8px;
-  color: #303133;
-  font-size: 14px;
-  text-align: left;
-  line-height: 30px;
-  font-weight: 500;
-  letter-spacing: 0em;
-}
-.tag_group_content {
-  width: calc(100% - 80px);
-  display: flex;
-  flex-wrap: wrap;
-  gap: 8px;
-}
-.tag_item {
-  padding: 0 8px;
-  line-height: 30px;
-  border-radius: 4px;
-  font-size: 14px;
-  background: #fff;
-  color: #999999;
-  cursor: pointer;
-}
-.tag_item.active {
-  background: rgba(71,113,203,0.1);
-  color: #2E64FA;
-}
-.tag_item:hover {
-  color: #2E64FA;
-}
-</style>

+ 126 - 84
src/styles/common.scss

@@ -42,6 +42,53 @@ body {
     Hiragino Sans GB, noto sans, Helvetica Neue, Helvetica,
     Arial, sans-serif;
 }
+// .module_content {
+//   padding: 16px;
+//   margin-bottom: 20px;
+//   border-radius: 10px;
+//   background: #fff;
+//   .module_title {
+//     display: flex;
+//     justify-content: space-between;
+//     margin-bottom: 16px;
+//     .title_left, .title_right {
+//       display: flex;
+//       gap: 10px;
+//     }
+//   }
+// }
+
+.el-select .el-select__wrapper,
+.el-input .el-input__wrapper,
+.el-date-editor.el-input,
+.el-button {
+  height: 36px;
+}
+.el-button:focus  {
+  outline: none;
+}
+.el-button+.el-button {
+  margin: 0;
+}
+.el-input-group__append {
+  width: 36px;
+}
+.el-button.is-text {
+  padding: 0 5px;
+  font-weight: 400;
+}
+.el-button.is-text:not(.is-disabled):active,
+.el-button.is-text:not(.is-disabled):hover 
+{
+  background: none;
+}
+
+
+
+
+
+
+
 
 // 全局滚动条样式修改
 /* 滚动条轨道区域样式 */
@@ -179,11 +226,9 @@ body {
   th.el-table__cell {
     border-right: 1px solid #ebeef5;
   }
-
   th.el-table__cell:nth-last-child(2) {
     border-right: none; // 去掉最后一个单元格的右侧边框  最右边还有个滚动条的 所以要去掉倒数第二个
   }
-
   .el-table {
     border-radius: 8px;
     border: 1px solid #f0f2f5;
@@ -194,7 +239,6 @@ body {
           td {
             border-right: 1px solid #ebeef5;
           }
-
           td:last-child,
           th:last-child {
             border-right: 0px solid green;
@@ -203,25 +247,21 @@ body {
       }
     }
     .el-table__empty-block {
-      // background-color: red;
       min-height: 120px;
       background-image: url("../assets/bg/table_no_data.png");
       background-size: 64px 72px;
       background-position: 50% 50%;
       background-repeat: no-repeat;
-
       .el-table__empty-text {
         display: none;
       }
     }
   }
   //表格加载动画字体
-  .el-loading-spinner .el-loading-text
-  {
+  .el-loading-spinner .el-loading-text {
     font-size: 14px;
   }
-  .el-loading-spinner i
-  {
+  .el-loading-spinner i {
     font-size: 20px;
     color:#2E64FA;
   }
@@ -230,49 +270,37 @@ body {
     height: 36px !important;
     line-height: 36px !important;
   }
-
   // 下拉选择框 中间箭头上下居中
   .el-select .el-input .el-select__caret {
     height: 36px;
   }
-
   // 下拉选择框 中间箭头上下居中
   .el-input__icon {
     line-height: 36px;
   }
-
   //去掉滚动条后的表格整体宽度
   .el-table__body {
     width: 100% !important;
   }
-
   .el-table {
     border-radius: 8px;
   }
-
   //标题栏背景色
   .el-table tr th.el-table__cell {
-    // background-color: #F0F2F5;
     background-color: #f4f6f8 !important;
     font-size: 16px;
     color: #333;
     font-weight: 600;
-    // padding-left: 8px;
   }
-
   .el-table .cell {
     padding: 0 5px !important;
-   
-    // color:#666;
   }
-
   .el-table thead {
     color: #333 !important;
     font-weight: 500;
     font-size: 14px;
     height: 52px;
-    .is-left
-    {
+    .is-left {
       padding-left: 10px;  //靠左的标题加10边距
     }
   }
@@ -1234,9 +1262,32 @@ body {
   background: #fff;
   border-radius: 10px;
   padding: 20px;
-  box-sizing:border-box;
-  
-
+  // box-sizing: border-box;
+}
+.page_tag {
+  width: calc(100% - 20px);
+  height: 100%;
+  border-radius: 0px 4px 4px 0px;
+  .el-radio-button__inner {
+    padding: 0 20px;
+    height: 36px;
+    line-height: 36px;
+    color: #666666;
+  }
+  .el-radio-button__orig-radio:checked + .el-radio-button__inner {
+    color: #fff !important;
+  }
+  .tab_item {
+    font-weight: 400;
+    font-size: 14px;
+    color:#999999;
+  }
+  .tag_item {
+    max-width: 100px;
+    white-space: nowrap;     /* 禁止换行 */
+    overflow: hidden;        /* 隐藏溢出内容 */
+    text-overflow: ellipsis; /* 溢出部分显示省略号 */
+  }
 }
 
 
@@ -1336,16 +1387,20 @@ body {
       margin-left: 20px;
       font-size: 14px !important;
     }
-
-    .content_left 
-    {
+    .el-select {
+      width: 120px;
+    }
+    .el-input {
+      width: 230px;
+    }
+    .content_left {
+      display: flex;
+      gap: 10px;
       width: auto;
       font-weight: 400;
       font-size: 14px;
       color: #666666;
-
-      .tab_button
-      {
+      .tab_button {
         width: auto;
         height: 36px !important;
         line-height: 36px;
@@ -1353,35 +1408,20 @@ body {
         // margin-right: 20px;
         font-size: 14px !important;
       }
-      .select_cur
-      {
+      .select_cur {
         background:#2E64FA;
         color:#fff;
         border:1px solid #2E64FA;
       }
-      .left_title
-      {
+      .left_title {
         width: 42px;
         font-weight: 600;
         font-size: 14px;
         color: #333333;
         margin-right: 8px;
       }
-
-      .el-input__inner
-      {
-        height: 36px;
-        line-height: 36px;
-        
-      }
-
-      .el-input__icon
-      {
-        line-height: 36px;
-      }
-
-      .grade_title
-      {
+      
+      .grade_title {
         font-weight: 600;
         font-size: 16px;
         color: #333333;
@@ -1394,10 +1434,10 @@ body {
         margin-right: 10px;
       }
       //通用input
-      .input_width
-      {
-        width: 230px;
-      }
+      // .input_width
+      // {
+      //   width: 230px;
+      // }
       
       //学年
       .select_year
@@ -1569,8 +1609,10 @@ body {
 //弹窗公共样式
 .page_dialog {
   border-radius: 8px;
-
   //饿了么样式覆盖
+  .el-dialog {
+    padding: 0;
+  }
   .el-dialog__header {
     width: 100%;
     height: 48px;
@@ -1582,28 +1624,28 @@ body {
     border-top-left-radius: 8px;
     padding: 0 !important;
     text-align: left;
-
     .el-dialog__title {
       font-weight: 600;
       font-size: 16px;
       color: #303133;
     }
-
+    button:focus, button:focus-visible {
+      outline: none;
+    }
     .el-dialog__headerbtn {
       // display: none;
-      width: 100px;
-      height: 24px;
-      margin-top: -8px;
-      background-image: url("../assets/icon/close-line.png");
-      background-position: 100% 50%;
-      background-repeat: no-repeat;
-      background-size: 24px 24px;
-      // background-color: red;
-      cursor: pointer;
-
-      i {
-        display: none;
-      }
+      // width: 100px;
+      // height: 24px;
+      // margin-top: -8px;
+      // background-image: url("../assets/icon/close-line.png");
+      // background-position: 100% 50%;
+      // background-repeat: no-repeat;
+      // background-size: 24px 24px;
+      // // background-color: red;
+      // cursor: pointer;
+      // i {
+      //   display: none;
+      // }
     }
   }
 
@@ -1612,13 +1654,16 @@ body {
     font-weight: 400;
     font-size: 14px;
     color: #666666;
-    padding: 0 !important;
+    padding: 20px;
     width: 100%;
     margin: auto;
     // min-height: 99px;
     // display: flex;
     // justify-content: flex-start;
     // align-items: center;
+    .el-date-editor.el-input, .el-date-editor.el-input__wrapper {
+      width: 140px;
+    }
   }
 
   .max_height_540 {
@@ -2536,26 +2581,22 @@ body {
 
   //饿了么底部按钮样式覆盖
   .el-dialog__footer {
-    padding: 0 !important;
+    padding: 0 20px;
     text-align: right;
     border-top: 1px solid #e6e6e6;
     height: 66px;
     line-height: 66px;
     border-radius: 0 0 8px 8px;
-
     .dialog_footer {
       padding: 0 !important;
-      width: calc(100% - 40px);
       margin: auto;
       height: 64px;
       display: flex;
       justify-content: space-between;
-
       .footer_left {
         width: auto;
         height: 64px;
       }
-
       .footer_right {
         width: auto;
         height: 64px;
@@ -2564,13 +2605,9 @@ body {
         align-items: center;
       }
     }
-
-    .dialog-footer {
-      width: calc(100% - 40px);
-      margin: auto;
-    }
-
     .el-button {
+      width: 68px;
+      height: 36px;
       padding: 0 !important;
       font-size: 14px;
     }
@@ -3084,6 +3121,11 @@ body {
   }
 }
 
+.el-message-box__btns {
+  .el-button + .el-button {
+    margin-left: 10px!important;
+  }
+}
 //列表行操作
 .table_row_option {
   width: 100%;
@@ -3164,7 +3206,7 @@ body {
     height: 36px !important;
     line-height: 36px;
     font-size: 14px;
-    color: #ff5d5e;
+    color: #F56C6C;
     cursor: pointer;
   }
 
@@ -3183,7 +3225,7 @@ body {
     height: 36px;
     line-height: 36px;
     font-size: 14px;
-    color: #ff5d5e;
+    color: #F56C6C;
     cursor: pointer;
   }
 

+ 138 - 0
src/views/examination/addSchool.vue

@@ -0,0 +1,138 @@
+<template>
+  <el-dialog v-model="schoolData.showDialog" :title="`${schoolData.data.tenantName} 新增学校`" width="680px">
+    <div class="page_search">
+      <div class="search_content">
+        <div class="content_left">
+          <el-select v-model="provinceSelect" placeholder="全部省份" clearable>
+            <el-option v-for="province in provincesList" :key="province.provinceCode" :label="province.provinceName" :value="province.provinceCode"></el-option>
+          </el-select>
+          <el-input v-model="schooltName" placeholder="请输入学校名称或编号" @input="SearchChange">
+            <template #append>
+            <el-button :icon="Search" @click="SearchChange" />
+            </template>
+          </el-input>
+        </div>
+      </div>
+    </div>
+    <div class="page_jg_20"></div>
+    <div class="page_table"> 
+      <el-table :data="tableData" stripe v-loading="tableLoading" element-loading-text="拼命加载中……" height="500px"
+        element-loading-spinner="el-icon-loading" element-loading-background="#ffffff" @selection-change="handleSelectionChange">
+          <el-table-column type="selection" align="center" />
+          <el-table-column type="index" label="序号" align="center" />
+          <el-table-column prop="tenantCode" label="账号" align="center" />
+          <el-table-column prop="tenantName" label="学校名称" align="center" />
+          <el-table-column prop="cityAndAreaName" label="区域" align="center" />
+          <el-table-column prop="schoolType" label="账号类型" align="center">
+            <template #default="{ row }">
+              {{ row.schoolType == 0 ? '单校版' : row.schoolType == 1 ? '联考版' :  '-' }}
+            </template>
+          </el-table-column>
+          <el-table-column prop="status" label="状态" align="center">
+            <template #default="{ row }">
+              <div class="table_row_status">
+                <!-- 正式 -->
+                <div class="need_summary_icon" v-if="row.status==1"></div>
+                <!-- 试用 -->
+                <div class="yes_release_icon" v-else-if="row.status==2"></div>
+                <!-- 冻结 -->
+                <div class="no_release_icon" v-else-if="row.status==3"></div>
+                <!-- {{ row.statusLabel }} -->
+                {{ row.status == 1 ? '正式' : row.status == 2 ? '试用' : row.status == 3 ? '冻结' : '-' }}
+              </div>
+            </template>
+          </el-table-column>
+      </el-table>
+    </div>
+    <template #footer>
+      <el-button @click="schoolData.showDialog = false">取消</el-button>
+      <el-button type="primary" :loading="submitLoading" @click="AddComfirm">确定</el-button>
+    </template>
+  </el-dialog>
+</template>
+<script lang="ts">
+import Vue from 'vue'
+export default {
+  name: 'AddSchool',
+}
+</script>
+<script lang="ts" setup>
+import { Search } from '@element-plus/icons-vue'
+import { ElMessage } from 'element-plus'
+import { ref, inject, reactive, onMounted } from 'vue'
+import { getSingleExamList, addSingleSchool } from '@/api/examination'
+import { provinceTree } from '@/api/school'
+interface SchoolDataItem {
+  id: string
+}
+interface SchoolData {
+  showDialog: boolean
+  data: SchoolDataItem
+}
+const schoolData = inject<SchoolData>('schoolData')
+const provincesList = ref([])
+const provinceSelect = ref('')
+const schooltName =  ref('')
+const tableData = ref([])
+const selUserList = ref([])
+// 提交按钮loading
+const submitLoading = ref(false)
+onMounted(() => {
+  GetProvinceTree()
+  GetSingleExamList()
+})
+// 获取省市区树列表
+const GetProvinceTree = async () => {
+  const res = await provinceTree()
+  if(res.code == 200 && res.data) {
+    const { data } = res
+    provincesList.value = data
+  }
+}
+const GetSingleExamList = async () => {
+  const res = await getSingleExamList({
+    queryStr: schooltName.value,
+    provinceCode: provinceSelect.value,
+    // cityCode: '',
+    // areaCode: '',
+  })
+  if (res.code === 200) {
+    tableData.value = res.data
+  }
+}
+const SearchChange = () => {
+  GetSingleExamList()
+}
+const handleSelectionChange = (val) => {
+  console.log(val)
+  selUserList.value = val.map(item => item.tenantCode)
+  console.log(selUserList.value)
+}
+const emit = defineEmits(['RefTable'])
+const AddComfirm = async () => {
+  submitLoading.value = true
+  try {
+    const res = await addSingleSchool({
+      schoolParentId: schoolData.data.id,
+      schoolChildId: selUserList.value
+    })
+    if(res.code == 200) {
+      ElMessage.success(res.msg)
+      schoolData.showDialog = false
+      emit('RefTable', false)
+    }else {
+      ElMessage.error(res.msg)
+    }
+    submitLoading.value = false
+  } catch (error) {
+    console.log(error)
+    submitLoading.value = false
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.page_search {
+  display: flex;
+}
+</style>

+ 170 - 6
src/views/examination/index.vue

@@ -1,15 +1,179 @@
 <template>
   <div>
-    联考
+    <div class="page_item">
+      <div class="page_left">
+        <div class="page_search">
+          <div class="search_content">
+            <div class="content_left">
+              <el-input v-model="schooltName" placeholder="请输入学校名称或编号" @input="GetJoinExamList">
+                <template #append>
+                <el-button :icon="Search" @click="GetJoinExamList" />
+                </template>
+              </el-input>
+            </div>
+          </div>
+        </div>
+        <div class="page_jg_20"></div>
+        <div class="page_table">
+          <el-table :data="tableData" stripe v-loading="tableLoading" element-loading-text="拼命加载中……" 
+            element-loading-spinner="el-icon-loading" element-loading-background="#ffffff" @row-click="handleRowClick">
+              <el-table-column align="center">
+                <template #default="{ row }">
+                  <el-radio v-model="selectRow.id" :value="row.id"></el-radio>
+                </template>
+              </el-table-column>
+              <el-table-column type="index" label="序号" align="center" />
+              <el-table-column prop="tenantCode" label="学校编号" align="center" />
+              <el-table-column prop="tenantName" label="学校名称" align="center" />
+              <el-table-column prop="cityAndAreaName" label="区域" align="center" />
+              <el-table-column prop="schoolType" label="账号类型" align="center">
+                <template #default="{ row }">
+                  {{ row.schoolType == 0 ? '单校版' : row.schoolType == 1 ? '联考版' :  '-' }}
+                </template>
+              </el-table-column>
+              <el-table-column prop="status" label="状态" align="center">
+                <template #default="{ row }">
+                  <div class="table_row_status">
+                    <!-- 正式 -->
+                    <div class="need_summary_icon" v-if="row.status==1"></div>
+                    <!-- 试用 -->
+                    <div class="yes_release_icon" v-else-if="row.status==2"></div>
+                    <!-- 冻结 -->
+                    <div class="no_release_icon" v-else-if="row.status==3"></div>
+                    <!-- {{ row.statusLabel }} -->
+                    {{ row.status == 1 ? '正式' : row.status == 2 ? '试用' : row.status == 3 ? '冻结' : '-' }}
+                  </div>
+                </template>
+              </el-table-column>
+              <el-table-column prop="childrenCount" label="下辖学校" align="center" />
+          </el-table>
+        </div>
+      </div>
+      <div class="page_right">
+        <div class="page_search">
+          <div class="search_content">
+            <div class="content_left">
+              {{ selectRow.tenantName }} 下辖学校:{{ selectRow.childrenCount }}
+            </div>
+            <div class="content_right">
+              <el-button type="primary" @click="addSchool">新增学校</el-button>
+            </div>
+          </div>
+        </div>
+        <div class="page_jg_20"></div>
+        <div class="page_table">
+          <el-table :data="selectRow.children" stripe v-loading="tableLoading" element-loading-text="拼命加载中……" 
+            element-loading-spinner="el-icon-loading" element-loading-background="#ffffff">
+              <el-table-column type="index" label="序号" align="center" />
+              <el-table-column prop="tenantCode" label="学校编号" align="center" />
+              <el-table-column prop="tenantName" label="学校名称" align="center" />
+              <el-table-column prop="cityAndAreaName" label="区域" align="center" />
+              <el-table-column prop="schoolType" label="账号类型" align="center">
+                <template #default="{ row }">
+                  {{ row.schoolType == 0 ? '单校版' : row.schoolType == 1 ? '联考版' :  '-' }}
+                </template>
+              </el-table-column>
+              <el-table-column prop="status" label="状态" align="center">
+                <template #default="{ row }">
+                  <div class="table_row_status">
+                    <!-- 正式 -->
+                    <div class="need_summary_icon" v-if="row.status==1"></div>
+                    <!-- 试用 -->
+                    <div class="yes_release_icon" v-else-if="row.status==2"></div>
+                    <!-- 冻结 -->
+                    <div class="no_release_icon" v-else-if="row.status==3"></div>
+                    <!-- {{ row.statusLabel }} -->
+                    {{ row.status == 1 ? '正式' : row.status == 2 ? '试用' : row.status == 3 ? '冻结' : '-' }}
+                  </div>
+                </template>
+              </el-table-column>
+              <el-table-column label="操作" align="center" width="60">
+                <template #default="{ row }">
+                  <div class="table_row_option">
+                    <div class="button_delete" @click="DeleteSchool(row)">删除</div>
+                  </div>
+                </template>
+              </el-table-column>
+          </el-table>
+        </div>
+      </div>
+    </div>
+    <div class="page_dialog">
+      <AddSchool @RefTable="RefTable" /> 
+    </div>
   </div>
 </template>
 
-<script>
-export default {
-
+<script lang="ts" setup>
+import { ref, reactive, provide, onMounted } from 'vue'
+import { Search } from '@element-plus/icons-vue'
+import { ElMessageBox, ElMessage } from 'element-plus'
+import AddSchool from '@/views/examination/addSchool.vue'
+import { getJoinExamList, deleteSchool } from '@/api/examination'
+const schooltName = ref('')
+const tableLoading = ref(false)
+const tableData = ref([])
+const selectRow = ref({})
+onMounted(() => {
+  GetJoinExamList()
+})
+const handleRowClick = (row: any) => {
+  console.log(row)
+  selectRow.value = row
+}
+const GetJoinExamList = () => {
+  console.log(schooltName.value)
+  tableLoading.value = true
+  getJoinExamList({ queryStr: schooltName.value }).then((res: any) => {
+    if(res.code == 200) {
+      tableData.value = res.data
+      selectRow.value = tableData.value[0]
+    }
+    tableLoading.value = false
+  }).catch(err => {
+    tableLoading.value = false
+  })
+}
+const schoolData = reactive({
+  showDialog: false,
+  data: {},
+})
+provide('schoolData', schoolData)
+const addSchool = () => {
+  schoolData.data = selectRow.value
+  schoolData.showDialog = true
+}
+const DeleteSchool = (row) => {
+  ElMessageBox.confirm('确定删除该学校吗?', '提示', {
+    confirmButtonText: '确定',
+    cancelButtonText: '取消',
+    type: 'warning'
+  }).then(async () => {
+    const res = await deleteSchool({ ids: row.id })
+    if(res.code == 200) {
+      ElMessage.success(res.msg)
+    }else {
+      ElMessage.error(res.msg)
+    }
+  })
+}
+const RefTable = () => {
+  schoolData.showDialog = false
+  GetJoinExamList()
 }
 </script>
 
-<style>
-
+<style lang="scss" scoped>
+.page_item {
+  display: flex;
+  gap: 20px;
+  .page_left, .page_right {
+    flex: 1;
+    min-width: 0;
+  }
+}
+.content_left {
+  font-size: 16px!important;
+  font-weight: 400;
+}
 </style>

+ 27 - 37
src/views/login/login.vue

@@ -110,7 +110,7 @@
 <script setup>
 import { ref, onMounted } from 'vue'
 // import particles from 'particles.js'
-import { login,getUserInfo } from '@/api/login'
+import { login, getUserInfo } from '@/api/login'
 import { ElMessage } from 'element-plus' // 
 import { useRouter } from 'vue-router'
 
@@ -251,43 +251,33 @@ const ClearInput = () => {
 
 // 登录提交
 const SubmitLogin = () => {
-  
-    const params = {
-        username: userName.value.trim(),
-        password: passWord.value.trim()
-    }
-
-    loadingLogin.value = true
-    login(params).then((res) => {
-        loadingLogin.value = false
-        if (res.code === 200) 
-        {
-        
-            
-            console.log("登录成功返回",res)
-            localStorage.setItem('token', res.data.tokenValue)
-            // 登录成功后调用获取用户信息接口
-            return getUserInfo(res.data.token)
-            
-        }
-    }).then((userInfoRes)=>{
-      console.log("获取用户信息返回",userInfoRes)
-       if (userInfoRes && userInfoRes.code === 200) 
-       {
-        // 存储用户信息到 localStorage 或 Pinia
-        localStorage.setItem('userInfo', JSON.stringify(userInfoRes.data))
-        ElMessage.success('登录成功');
-        // 跳转到主页
-        router.push('/main/school')
+  const params = {
+    username: userName.value.trim(),
+    password: passWord.value.trim()
+  }
+  loadingLogin.value = true
+  login(params).then((res) => {
+      loadingLogin.value = false
+      if (res.code === 200) {
+          console.log("登录成功返回",res)
+          localStorage.setItem('token', res.data.tokenValue)
+          // 登录成功后调用获取用户信息接口
+          return getUserInfo(res.data.token)
       }
-    })
-    .catch((err) => {
-        
-        loadingLogin.value = false
-        ElMessage.error('登录失败!'+err);
-        console.error(err)
-    })
-
+  }).then((userInfoRes)=>{
+    console.log("获取用户信息返回",userInfoRes)
+    if (userInfoRes && userInfoRes.code === 200) {
+      // 存储用户信息到 localStorage 或 Pinia
+      localStorage.setItem('userInfo', JSON.stringify(userInfoRes.data))
+      ElMessage.success('登录成功');
+      // 跳转到主页
+      router.push('/main/school')
+    }
+  }).catch((err) => {
+      loadingLogin.value = false
+      ElMessage.error('登录失败!'+err);
+      console.error(err)
+  })
 }
 
 // 去往官网

+ 7 - 5
src/views/manager/index.vue

@@ -3,14 +3,16 @@
     <div class="page_search">
       <div class="search_content">
         <div class="content_left">
-          <el-input placeholder="请输入姓名或账号"  v-model="keyWord"  class="input_width">
-            <el-button @click="GoSearch()"  slot="append" icon="el-icon-search"></el-button>
+          <el-input v-model="keyWord" placeholder="请输入姓名或账号">
+            <template #append>
+              <el-button :icon="Search" @click="GoSearch()" />
+            </template>
           </el-input>
         </div>
         <div class="content_right">
-          <el-button   >刷新</el-button>
+          <el-button>刷新</el-button>
           <el-button type="primary" @click="AddMultiExam" >
-                新增用户
+            新增用户
           </el-button>
         </div>
       </div>
@@ -34,7 +36,6 @@
             <el-table-column prop="stateName" label="状态" align="center"  show-overflow-tooltip>
               <template #default="scope">
                 <div class="table_row_status">
-                    
                     <!-- 需汇总 -->
                     <div class="need_summary_icon" v-if="scope.row.state==1"></div>
                     <!-- 汇总中 改成成绩中 -->
@@ -68,6 +69,7 @@
 
 <script setup>
 import { ref ,onMounted, onBeforeUnmount} from 'vue'
+import { Search } from '@element-plus/icons-vue'
 const tableData = ref([]);//表格数据
 const tableLoading = ref(false);//表格加载loading
 const tableKey = ref(0);//用于强制刷新table

+ 141 - 0
src/views/school/SearchMain.vue

@@ -0,0 +1,141 @@
+<template>
+  <div class="page_item">
+    <div class="tag_group">
+      <div class="tag_group_title">省份:</div>
+      <div class="tag_group_content">
+        <div class="tag_item" v-for="item in searchList.provinceNode" :key="item.provinceCode" @click="selectItem(item.provinceCode, 'provinceCode', item)" :class="item.provinceCode == searchData.provinceCode ? 'active' : ''">
+          {{ item.provinceName }}
+        </div>
+      </div>
+    </div>
+    <div class="tag_group" v-if="cityNode.length > 0">
+      <div class="tag_group_title">城市:</div>
+      <div class="tag_group_content">
+        <div class="tag_item" v-for="item in cityNode" :key="item.cityCode" @click="selectItem(item.cityCode, 'cityCode', item)" :class="item.cityCode == searchData.cityCode ? 'active' : ''">
+          {{ item.cityName }}
+        </div>
+      </div>
+    </div>
+    <div class="tag_group">
+      <div class="tag_group_title">运维:</div>
+      <div class="tag_group_content">
+        <div class="tag_item" v-for="item in searchList.userInfo" :key="item.userId" @click="selectItem(item.userId, 'userId', item)" :class="item.userId == searchData.userId ? 'active' : ''">
+          {{ item.nickname }}
+        </div>
+      </div>
+    </div>
+    <div class="tag_group">
+      <div class="tag_group_title">状态:</div>
+      <div class="tag_group_content">
+        <div class="tag_item" v-for="item in searchList.statusNode" :key="item.status" @click="selectItem(item.status, 'status', item)" :class="item.status == searchData.status ? 'active' : ''">
+          {{ item.statusName }}
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+export default {
+  name: 'SearchMain',
+}
+</script>
+<script lang="ts" setup>
+import { onMounted, ref } from 'vue'
+import { getSearchList } from '@/api/school'
+const searchData = ref({
+  provinceCode: '',
+  cityCode: '',
+  status: '',
+  userId: '',
+})
+const cityNode = ref([])
+const searchList =  ref({
+  examTypes: [],
+  provinceNode: [],
+  statusNode: [],
+  userInfo: []
+})
+const emit = defineEmits(['FnSearch'])
+const selectItem = (value, str, item) => {
+  searchData.value[str] = value
+  let arr = []
+  if(str == 'provinceCode') {
+    if(value) {
+      arr = [...item.children]
+      arr.splice(0, 0, { cityCode: "", cityName: "全部" })
+      cityNode.value = arr
+      searchData.value.cityCode = ''
+    }else {
+      cityNode.value = []
+      searchData.value.cityCode = ''
+    }
+  }
+  emit('FnSearch', searchData);
+}
+onMounted(() => {
+  GetSearchList()
+})
+const GetSearchList = () => {
+  getSearchList().then(res => {
+    console.log(res)
+    if(res.code == 200) {
+      const { data } = res
+      for(let i in data) {
+        if(i == 'provinceNode') {
+          data[i].splice(0, 0, { provinceCode: "", provinceName: "全部" })
+        }else if(i == 'cityNode') {
+          data[i].splice(0, 0, { cityCode: "", cityName: "全部" })
+        }else if( i == 'statusNode') {
+           data[i].splice(0, 0, { status: "", statusName: "全部" })
+        }else if( i == 'userInfo') {
+          data[i].splice(0, 0, { userId: "", nickname: "全部" })
+        }
+      }
+      searchList.value = data
+      console.log(searchList.value)
+    }
+  })
+}
+</script>
+
+<style lang="scss" scoped>
+.tag_group{
+  padding:8px 0;
+  display: flex;
+  justify-content: flex-start;
+  .tag_group_title {
+    width: auto;
+    height: auto;
+    margin-right: 8px;
+    color: #303133;
+    font-size: 14px;
+    text-align: left;
+    line-height: 30px;
+    font-weight: 500;
+    letter-spacing: 0em;
+  }
+  .tag_group_content {
+    width: calc(100% - 80px);
+    display: flex;
+    flex-wrap: wrap;
+    gap: 8px;
+    .tag_item {
+      padding: 0 8px;
+      line-height: 30px;
+      border-radius: 4px;
+      font-size: 14px;
+      background: #fff;
+      color: #999999;
+      cursor: pointer;
+      &.active {
+        background: rgba(71,113,203,0.1);
+        color: #2E64FA;
+      }
+      &:hover {
+        color: #2E64FA;
+      }
+    }
+  }
+}
+</style>

+ 343 - 0
src/views/school/addUser.vue

@@ -0,0 +1,343 @@
+<template>
+  <el-dialog v-model="showDialog" :title="dialogData.pageType == 'add' ? '新增用户' : dialogData.pageType == 'edit' ? '编辑用户' : ''" width="600px">
+    <el-form :model="userForm" :rules="rules" ref="userFormRef">
+      <el-form-item label="学校名称:" :label-width="formLabelWidth" prop="tenantName">
+        <el-input v-model="userForm.tenantName" placeholder="请输入学校名称" />
+      </el-form-item>
+      <el-form-item label="学校简称:" :label-width="formLabelWidth" prop="abbreviation">
+        <el-input v-model="userForm.abbreviation" placeholder="请输入学校名称" />
+      </el-form-item>
+      <el-form-item label="区域:" :label-width="formLabelWidth" required>
+        <el-form-item prop="provinceCode">
+          <el-select v-model="userForm.provinceCode" placeholder="请选择" @change="handleProvinceChange" style="width: 100px;">
+            <el-option
+              v-for="province in provinceTreeList || []"
+              :key="province.provinceCode"
+              :label="province.provinceName"
+              :value="province.provinceCode"
+            ></el-option>
+          </el-select>
+          <span class="margin_10">省</span>
+        </el-form-item>
+        <el-form-item prop="cityCode">
+          <el-select v-model="userForm.cityCode" placeholder="请选择" @change="handleCityChange" style="width: 100px;">
+            <el-option
+              v-for="city in cityTreeList || []"
+              :key="city.cityCode"
+              :label="city.cityName"
+              :value="city.cityCode"
+            ></el-option>
+          </el-select>
+          <span class="margin_10">市</span>
+        </el-form-item>
+        <el-form-item prop="areaCode">
+          <el-select v-model="userForm.areaCode" placeholder="请选择" @change="handleAreaChange" style="width: 100px;">
+            <el-option
+              v-for="district in areaList || []"
+              :key="district.districtCode"
+              :label="district.districtName"
+              :value="district.districtCode"
+            ></el-option>
+          </el-select>
+          <span class="margin_10">区/县</span>
+        </el-form-item>
+      </el-form-item>
+      <el-form-item label="账号类型:" :label-width="formLabelWidth" prop="schoolType">
+        <el-radio-group v-model="userForm.schoolType">
+          <el-radio :value="0">单校版</el-radio>
+          <el-radio :value="1">联考版</el-radio>
+        </el-radio-group>
+      </el-form-item>
+      <el-form-item label="用户类型:" :label-width="formLabelWidth" prop="status">
+        <el-radio-group v-model="userForm.status">
+          <el-radio :value="1">正式用户</el-radio>
+          <el-radio :value="2">体验用户</el-radio>
+        </el-radio-group>
+      </el-form-item>
+      <el-form-item label="学校logo:" :label-width="formLabelWidth">
+        <img v-if="userForm.image" :src="userForm.image" alt="" class="file_image">
+        <img v-else src="@/assets/school/default_logo.svg" alt="" class="file_image">
+        <el-upload :http-request="UploadLogo">
+          <el-button size="small" type="primary">{{ userForm.image ? '重新上传' : '上传logo'}}</el-button>
+        </el-upload>
+      </el-form-item>
+      <el-form-item label="有效时间:" :label-width="formLabelWidth" required>
+        <el-form-item prop="startDate">
+          <el-date-picker
+            v-model="userForm.startDate"
+            type="date"
+            placeholder="请选择"
+            format="YYYY/MM/DD"
+            value-format="YYYY-MM-DD"
+          /><span class="margin_10">至</span>
+        </el-form-item>
+        <el-form-item prop="endDate">
+          <el-date-picker
+            v-model="userForm.endDate"
+            type="date"
+            placeholder="请选择"
+            format="YYYY/MM/DD"
+            value-format="YYYY-MM-DD"
+          />
+        </el-form-item>
+      </el-form-item>
+      <el-form-item label="运维负责人:" :label-width="formLabelWidth" prop="selUserList">
+        <el-select v-model="userForm.selUserList" placeholder="请选择" @change="handleUserChange" style="width: 320px;" multiple collapse-tags collapse-tags-tooltip>
+          <el-option
+            v-for="item in userList"
+            :key="item.id"
+            :label="item.nickname"
+            :value="item.id"
+          ></el-option>
+        </el-select>
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <el-button @click="showDialog = false">取消</el-button>
+      <el-button type="primary" :loading="submitLoading" @click="SubmitForm(userFormRef)">确定</el-button>
+    </template>
+  </el-dialog>
+</template>
+<script lang="ts">
+export default {
+  name: 'AddUser',
+}
+</script>
+<script lang="ts" setup>
+import { ref, reactive, inject, onMounted, nextTick } from 'vue'
+import type { FormInstance, FormRules } from 'element-plus'
+import { ElMessage } from 'element-plus'
+import { getUserList, addUser, uploadFile, provinceTree, getSchoolInfo, editSchoolInfo } from '@/api/school'
+interface DialogData {
+  pageType: string
+  id: string
+}
+let showDialog = inject('showDialog')
+let dialogData = inject<DialogData>('dialogData')
+const formLabelWidth = '120px'
+// 新增用户表单数据定义
+interface UserForm {
+  tenantName: string
+  abbreviation: string
+  provinceCode: string
+  provinceName: string
+  cityCode: string
+  cityName: string
+  areaCode: string
+  areaName: string
+  schoolType: number
+  status: number
+  image: string
+  startDate: string
+  endDate: string
+  selUserList: string[]
+  AdminSchoolUserInsertList: Array <any>
+}
+// 新增用户表单数据初始化
+const userForm = reactive<UserForm>({
+  tenantName: '',
+  abbreviation: '',
+  provinceCode: '',
+  provinceName: '',
+  cityCode: '',
+  cityName: '',
+  areaCode: '',
+  areaName: '',
+  schoolType: 0,
+  status: 1,
+  image: '',
+  startDate: '',
+  endDate: '',
+  selUserList: [],
+  AdminSchoolUserInsertList: []
+})
+const userFormRef = ref<FormInstance>()
+// 表单校验规则
+const rules = reactive<FormRules<UserForm>>({
+  tenantName: [
+    { required: true, message: '请输入学校名称', trigger: 'blur' }
+  ],
+  abbreviation: [
+    { required: true, message: '请输入学校简称', trigger: 'blur' }
+  ],
+  provinceCode: [
+    { required: true, message: '请选择省', trigger: 'change' }
+  ],
+  cityCode: [
+    { required: true, message: '请选择市', trigger: 'change' }
+  ],
+  areaCode: [
+    { required: true, message: '请选择区/县', trigger: 'change' }
+  ],
+  schoolType: [
+    { required: true, message: '请选择账号类型', trigger: 'change' }
+  ],
+  status: [
+    { required: true, message: '请选择用户类型', trigger: 'change' }
+  ],
+  startDate: [
+    { required: true, message: '请选择开始时间', trigger: 'change' }
+  ],
+  endDate: [
+    { required: true, message: '请选择结束时间', trigger: 'change' }
+  ],
+  selUserList: [
+    { required: true, message: '请选择运维人员', trigger: 'change' }
+  ]
+})
+
+// 省份列表
+const provinceTreeList = ref([])
+// 市列表
+const cityTreeList = ref([])
+// 区/县列表
+const areaList = ref([])
+// 运维人员列表
+const userList = ref([])
+// 提交按钮loading
+const submitLoading = ref(false)
+onMounted (async () => {
+  await GetProvinceTree()
+  await GetUserList()
+  if(dialogData.pageType == 'edit') {
+    await GetUserDetail()
+  }
+})
+// 获取用户详情
+const GetUserDetail = () => {
+  getSchoolInfo(dialogData.id).then(res => {
+    if(res.code == 200 && res.data) {
+      const { data } = res
+      if(data.provinceCode) {
+        cityTreeList.value = provinceTreeList.value.find(item => item.provinceCode == data.provinceCode).children
+      }
+      if(data.cityCode) {
+        areaList.value = cityTreeList.value.find(item => item.cityCode == data.cityCode).children
+      }
+      userForm.tenantName = data.tenantName
+      userForm.abbreviation = data.abbreviation
+      userForm.provinceCode = data.provinceCode
+      userForm.provinceName = data.provinceName
+      userForm.cityCode = data.cityCode
+      userForm.cityName = data.cityName
+      userForm.areaCode = data.areaCode
+      userForm.areaName = data.areaName
+      userForm.schoolType = data.schoolType
+      userForm.status = data.status
+      userForm.image = data.image
+      userForm.startDate = data.startDate
+      userForm.endDate = data.endDate
+      userForm.selUserList = data.selUserList
+      userForm.AdminSchoolUserInsertList = data.AdminSchoolUserInsertList
+    }
+  })
+}
+// 获取运维人员列表
+const GetUserList = () => {
+  getUserList().then(res => {
+    if(res.code == 200 && res.data) {
+      const { data } = res
+      userList.value = data
+    }
+  })
+}
+// 获取省市区树列表
+const GetProvinceTree = (): Promise<void> => {
+  return new Promise((resolve, reject) => {
+    provinceTree().then(res => {
+      if(res.code == 200 && res.data) {
+        const { data } = res
+        provinceTreeList.value = data
+        resolve()
+      }
+    })
+  })
+}
+const fileImageUrl = ref('')
+// 选择省份
+const handleProvinceChange = (val) => {
+  userForm.provinceCode = val
+  userForm.provinceName = provinceTreeList.value.find(item => item.provinceCode == val).provinceName
+  cityTreeList.value = provinceTreeList.value.find(item => item.provinceCode == val).children
+  areaList.value = []
+  userForm.cityCode = ''
+  userForm.areaCode = ''
+}
+// 选择市
+const handleCityChange = (val) => {
+  userForm.cityCode = val
+  userForm.cityName = cityTreeList.value.find(item => item.cityCode == val).cityName
+  areaList.value = cityTreeList.value.find(item => item.cityCode == val).children
+  userForm.areaCode = ''
+}
+// 选择区/县
+const handleAreaChange = (val) => {
+  userForm.areaCode = val
+  userForm.areaName = areaList.value.find(item => item.districtCode == val).districtName
+}
+// 上传学校logo
+const UploadLogo = (file) => {
+  let formData = new FormData();
+  formData.append("file", file.file);
+  uploadFile(formData).then(res => {
+    console.log(res)
+    if(res.code == 200 && res.data) {
+      const { data } = res
+      userForm.image = data.url
+    }
+  })
+}
+// 选择运维人员
+const handleUserChange = (val) => {
+  console.log(val)
+  let result = userList.value.filter(item => val.includes(item.id)).map(item => {
+    return { id: item.id }
+  })
+  console.log(result)
+  userForm.selUserList = val
+  userForm.AdminSchoolUserInsertList = result
+}
+const emit = defineEmits(['AddUser'])
+// 表单提交
+const SubmitForm = async (formEl: FormInstance | undefined) => {
+  if (!formEl) return
+  await formEl.validate(async(valid, fields) => {
+    if (valid) {
+      submitLoading.value = true
+      try {
+        let res
+        if(dialogData.pageType == 'add') {
+          res = await addUser(userForm)
+        }else if(dialogData.pageType == 'edit') {
+          res = await editSchoolInfo(userForm)
+        }
+        if (res.code == 200) {
+          ElMessage.success(res.msg)
+          userFormRef.value.resetFields()
+          emit('AddUser', false)
+        } else {
+          ElMessage.error(res.msg)
+        }
+        submitLoading.value = false
+      }catch {
+        submitLoading.value = false
+      }
+    }
+  })
+}
+</script>
+
+<style lang="scss" scoped>
+.file_image {
+  width: 48px;
+  height: 48px;
+  margin-right: 10px;
+  border-radius: 50%;
+}
+.margin_10 {
+  margin: 0 10px;
+}
+.el-input {
+  width: 320px;
+}
+</style>

+ 42 - 0
src/views/school/authConfig.vue

@@ -0,0 +1,42 @@
+<template>
+  <el-dialog v-model="authData.showDialog" :title="`${authData.data.schoolName} 权限管理`">
+    <div class="page_tag">
+      <el-radio-group v-model="authType" @change="ChangeTab">
+        <el-radio-button label="1">开通年级</el-radio-button>
+        <el-radio-button label="2" disabled>开通模块</el-radio-button>
+      </el-radio-group>
+    </div>
+    <div v-if="authType == '1'">
+      年级模块
+    </div>
+    
+  </el-dialog>
+</template>
+<script lang="ts">
+export default {
+  name: 'AuthConfig'
+}
+</script>
+<script lang="ts" setup>
+import { ref, inject, onMounted } from 'vue'
+import { getAllGrade } from '@/api/school'
+let authData = inject('authData')
+const authType = ref('1')
+onMounted (() => {
+  GetAllGrade()
+})
+const GetAllGrade = () => {
+  getAllGrade({
+    
+  }).then(res => {
+    console.log(res)
+  })
+}
+const ChangeTab = (val) => {
+  console.log(val)
+}
+</script>
+
+<style>
+
+</style>

+ 273 - 112
src/views/school/index.vue

@@ -6,118 +6,256 @@
           <img src="@/assets/icon/user_all.svg" alt="">
           <p>全部用户</p>
         </div>
-        <div class="item_right">5185</div>
+        <div class="item_right">{{ userNumber.allUser }}</div>
       </div>
       <div class="user_item blue">    
         <div class="item_left">
           <img src="@/assets/icon/user_blue.svg" alt="">
           <p>正式用户</p>
         </div>
-        <div class="item_right">5185</div>
+        <div class="item_right">{{ userNumber.officialUser }}</div>
       </div>
       <div class="user_item green">
         <div class="item_left">
           <img src="@/assets/icon/user_green.svg" alt="">
           <p>体验用户</p>
         </div>
-        <div class="item_right">5185</div>
+        <div class="item_right">{{ userNumber.experienceUser }}</div>
       </div>
       <div class="user_item orange">
         <div class="item_left">
           <img src="@/assets/icon/user_orange.svg" alt="">
           <p>冻结用户</p>
         </div>
-        <div class="item_right">5185</div>
+        <div class="item_right">{{ userNumber.freezeUser }}</div>
       </div>
     </div>
-    <SearchMain />
-    <div class="module_content">
-      <div class="module_title">
-        <div class="title_left">
-          <el-select v-model="accountTypeSelect">
-            <el-option label="全部类型" :value="0"></el-option>
-            <el-option label="单校" :value="1"></el-option>
-            <el-option label="联考" :value="2"></el-option>
-          </el-select>
-          <el-input v-model="studentName" placeholder="请输入学生姓名">
-            <template #append>
-              <img src="@/assets/icon/search_line.svg" alt="">
-            </template>
-          </el-input>
-        </div>
-        <div class="title_right">
-          <el-button>
-            <img src="@/assets/icon/refresh.svg" alt="" style="margin-right: 5px;">刷新
-          </el-button>
-          <el-button>开通科目</el-button>
-          <el-button type="primary">新增用户</el-button>
+    <SearchMain @FnSearch="FnSearch" />
+    <div class="page_jg_20"></div>
+    <div class="page_item">
+      <div class="page_search">
+        <div class="search_content">
+          <div class="content_left">
+            <el-select v-model="schoolTypeSelect" @change="SearchChange" placeholder="">
+              <el-option label="全部类型" value=" "></el-option>
+              <el-option label="单校" :value="0"></el-option>
+              <el-option label="联考" :value="1"></el-option>
+            </el-select>
+            <el-input v-model="schoolNameCode" placeholder="请输入学校名称或编号" @input="SearchChange">
+              <template #append>
+              <el-button :icon="Search" @click="SearchChange" />
+              </template>
+            </el-input>
+          </div>
+          <div class="content_right">
+            <el-button>
+              <img src="@/assets/icon/refresh.svg" alt="" style="margin-right: 5px;">刷新
+            </el-button>
+            <el-button @click="OpenSubject">开通科目</el-button>
+            <el-button type="primary" @click="addUser">新增用户</el-button>
+          </div>
         </div>
       </div>
-      <div class="module_table">
-        <el-table :data="tableData">
-          <el-table-column type="index" label="序号" />
-          <el-table-column prop="schoolCode" label="学校编号" width="180" />
-          <el-table-column prop="schoolName" label="学校名称" width="180" />
-          <el-table-column prop="schoolAbbreviation" label="学校简称" />
-          <el-table-column prop="region" label="区域" />
-          <el-table-column prop="accountType" label="账号类型" />
-          <el-table-column prop="startTime" label="开始时间" />
-          <el-table-column prop="endTime" label="结束时间" />
-          <el-table-column prop="remainingDays" label="剩余天数" />
-          <el-table-column prop="status" label="状态" />
-          <el-table-column prop="maintenanceStaff" label="运维人员" />
-          <el-table-column label="操作" width="250" fixed="right">
-            <template #default="{ row }">
-              <el-button link>{{ row.status == '3' ? "解冻" : "冻结"}}</el-button>
-              <el-button link>编辑</el-button>
-              <el-button link>权限</el-button>
-              <el-button link>重置密码</el-button>
-              <el-button link>进入学校</el-button>
-            </template>
-        </el-table-column>
+      <div class="page_jg_20"></div>
+      <div class="page_table">
+        <el-table :data="tableData" stripe v-loading="tableLoading" element-loading-text="拼命加载中……" 
+          element-loading-spinner="el-icon-loading" element-loading-background="#ffffff">
+            <el-table-column type="index" label="序号" fixed="left" align="center" />
+            <el-table-column prop="tenantCode" label="学校编号" width="80" fixed="left" align="center" show-overflow-tooltip />
+            <el-table-column prop="tenantName" label="学校名称" width="150" fixed="left" align="center" show-overflow-tooltip />
+            <el-table-column prop="schoolAbbreviation" label="学校简称" width="100" align="center" show-overflow-tooltip />
+            <el-table-column prop="region" label="区域" align="center" show-overflow-tooltip />
+            <el-table-column prop="schoolType" label="账号类型" align="center">
+              <template #default="{ row }">
+                {{ row.schoolType == 0 ? '单校版' : row.schoolType == 1 ? '联考版' :  '-' }}
+              </template>
+            </el-table-column>
+            <el-table-column prop="startDate" label="开始时间" width="100" align="center" show-overflow-tooltip />
+            <el-table-column prop="endDate" label="结束时间" width="100" align="center" show-overflow-tooltip />
+            <el-table-column prop="dateDiffDayS" label="剩余天数" align="center" />
+            <el-table-column prop="status" label="状态" align="center">
+              <template #default="{ row }">
+                <div class="table_row_status">
+                  <!-- 正式 -->
+                  <div class="need_summary_icon" v-if="row.status==1"></div>
+                  <!-- 试用 -->
+                  <div class="yes_release_icon" v-else-if="row.status==2"></div>
+                  <!-- 冻结 -->
+                  <div class="no_release_icon" v-else-if="row.status==3"></div>
+                  <!-- {{ row.statusLabel }} -->
+                  {{ row.status == 1 ? '正式' : row.status == 2 ? '试用' : row.status == 3 ? '冻结' : '-' }}
+                </div>
+              </template>
+            </el-table-column>
+            <el-table-column prop="ityAndAreaNam" label="运维人员" align="center" />
+            <el-table-column label="操作" width="280" fixed="right" align="center">
+              <template #default="{ row }">
+                <div class="table_row_option">
+                  <div v-if="row.status == 3" class="button_delete" @click="ChangeStatus(row)">解冻</div>
+                  <div v-else class="button_editor text_color_orange" @click="ChangeStatus(row)">冻结</div>
+                  <div class="button_editor" @click="EditUser(row)">编辑</div>
+                  <div class="button_editor" @click="OpenAuth(row)">权限</div>
+                  <div class="button_editor">重置密码</div>
+                  <div class="button_editor">进入学校</div>
+                </div>
+              </template>
+            </el-table-column>
         </el-table>
       </div>
     </div>
+
+    <div class="page_dialog">
+      <AddUser v-if="showDialog" @AddUser="RefTable" /> 
+      <AuthConfig v-if="authData.showDialog" />
+      <SubjectList v-if="subjectShowDialog" />
+    </div>
   </div>
 </template>
 
-<script>
-import SearchMain from '@/components/SearchMain.vue'
-import { ref } from 'vue'
-// import { Search } from '@element-plus/icons-vue'
-export default {
-  components: {
-    SearchMain,
-    // Search
-  },
-  setup() {
-    const tableData = ref([
-      {
-        schoolCode: '182900',
-        schoolName: '霍格沃茨魔法学校',
-        schoolAbbreviation: '霍格沃茨',
-        region: '九又四分之三站台',
-        accountType: '魔法版',
-        startTime: '2024-10-15',
-        endTime: '2024-10-16',
-        remainingDays: '1',
-        status: '1',
-        statusName: '正式',
-        maintenanceStaff: '哈利·波特',
-      }
-    ])
-    const accountTypeSelect = ref(0)
-    const studentName = ref('')
-    return {
-      accountTypeSelect,
-      studentName,
-      tableData
+<script lang="ts" setup>
+import SearchMain from '@/views/school/SearchMain.vue'
+import AddUser from '@/views/school/addUser.vue'
+import AuthConfig from '@/views/school/authConfig.vue'
+import SubjectList from '@/views/school/subjectList.vue'
+import { Search } from '@element-plus/icons-vue'
+import { getSchoolNumber, getSchoolList, changeStatus } from '@/api/school'
+import { ref, reactive, provide, onMounted } from 'vue'
+interface UserNumber {
+  allUser: number
+  officialUser: number
+  experienceUser: number
+  freezeUser: number
+}
+const userNumber = ref<UserNumber>({
+  allUser: 0,
+  officialUser: 0,
+  experienceUser: 0,
+  freezeUser: 0
+})
+interface User {
+  tenantCode: String;
+  tenantName: String;
+  status: number;
+  statusLabel: String;
+  schoolType: String;
+  abbreviation: String;
+  dateDiffDayS: String;
+  startDate:  String;
+  endDate:  String;
+  ityAndAreaNam: string;
+  region:  String;
+}
+const tableData = ref<User[]>([])
+
+const schoolTypeSelect = ref<string>(" ")
+const schoolNameCode = ref('')
+const tableLoading = ref(false)
+const showDialog = ref(false)
+const dialogData = reactive({
+  id: '',
+  pageType: '',
+  tenantName: ''
+})
+const subjectShowDialog = ref(false)
+const authData = reactive({
+  showDialog: false,
+  data: {}
+})
+provide('showDialog', showDialog) // 新增用户弹窗是否展示
+provide('dialogData', dialogData) // 新增用户弹窗数据
+provide('authData', authData) // 设置权限弹窗数据
+provide('subjectShowDialog', subjectShowDialog) // 开通科目弹窗数据
+onMounted(() => {
+  GetSchoolNumber()
+  GetSchoolList()
+})
+// 新增用户
+const addUser = () => {
+  showDialog.value = true
+  dialogData.pageType = 'add'
+}
+// 编辑
+const EditUser =  (row) => {
+  console.log(row)
+  showDialog.value = true
+  dialogData.pageType = 'edit'
+  dialogData.id = row.id
+  dialogData.tenantName = row.tenantName
+}
+// 设置权限
+const OpenAuth = (row) => {
+  authData.data = row
+  authData.showDialog = true
+}
+// 开通科目
+const OpenSubject = () => {
+  console.log('OpenSubject')
+  subjectShowDialog.value = true
+}
+// 筛选项数据
+const searchData = ref({
+  provinceCode: '',
+  cityCode: '',
+  status: '',
+  userId: ''
+})
+// 筛选项查询
+const FnSearch = (data) => {
+  searchData.value = data.value
+  GetSchoolList()
+}
+// 表格上面筛选查询
+const SearchChange = () => {
+  GetSchoolList()
+}
+// 获取用户数量
+const GetSchoolNumber = () => {
+  getSchoolNumber().then(res => {
+    if(res.code == 200 && res.data) {
+      userNumber.value = res.data
     }
+  })
+}
+// 获取学校列表
+const GetSchoolList = () => {
+  let params = {
+    status: searchData.value.status,
+    // userId: searchData.value.userId,
+    cityCode: searchData.value.cityCode,
+    provinceCode: searchData.value.provinceCode,
+    schoolType:  schoolTypeSelect.value,
+    queryStr: schoolNameCode.value
   }
+  tableLoading.value = true
+  getSchoolList(params).then(res => {
+    if(res.code == 200 && res.data) {
+      tableData.value = res.data
+    }
+    tableLoading.value = false
+  }).catch(err => {
+    tableLoading.value = false
+  })
+}
+// 新增/修改学校信息后刷新列表
+const RefTable = (val) => {
+  showDialog.value = val
+  GetSchoolList()
+}
+// 改变用户状态
+const ChangeStatus = (row) => {
+  console.log(row)
+  changeStatus({
+    id: row.id,
+    status: row.status
+  }).then(res => {
+    if(res.code == 200) {
+      GetSchoolList()
+    }
+  })
 }
 </script>
 
-<style scoped>
+<style lang="scss" scoped>
 .user_num_content {
   width: 100%;
   display: flex;
@@ -125,40 +263,63 @@ export default {
   justify-content: space-between;
   align-items: center;
   margin-bottom: 20px;
+  .user_item {
+    flex-grow: 1;
+    height: 80px;
+    padding: 20px;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    border-radius: 10px;
+    font-weight: 500;
+    .item_left {
+      display: flex;
+      align-items: center;
+      img {
+        margin-right: 10px;
+      }
+    }
+    &.all {
+      background: #2E64FA;
+      color: #fff;
+      .item_right {
+        color: #fff;
+      }
+    }
+    &.blue {
+      border: 1px solid #2E64FA;
+      background: #EAF0FF;
+      color: #333;
+      .item_right {
+        color: #2E64FA;
+      }
+    }
+    &.green {
+      border: 1px solid #2BC644;
+      background: #EAF9EC;
+      color: #333;
+      .item_right {
+        color: #2BC644;
+      }
+    }
+    &.orange {
+      border: 1px solid #FB9F34;
+      background: #FFF5EB;
+      color: #333;
+      .item_right {
+        color: #FB9F34;
+      }
+    }
+    .item_right {
+      font-size: 24px;
+      font-weight: 700;
+    }
+  }
 }
-.user_num_content .user_item {
-  flex-grow: 1;
-  height: 80px;
-  padding: 20px;
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  border-radius: 10px;
-}
-.user_num_content .user_item .item_left {
-  display: flex;
-  align-items: center;
-}
-.item_left img {
-  margin-right: 10px;
-}
-.user_item.all {
-  background: #2E64FA;
-  color: #fff;
-}
-.user_item.blue {
-  border: 1px solid #2E64FA;
-  background: #EAF0FF;
-  color: #333;
-}
-.user_item.green {
-  border: 1px solid #2BC644;
-  background: #EAF9EC;
-  color: #333;
+.button_editor.text_color_orange {
+  color: #FB9F34;
 }
-.user_item.orange {
-  border: 1px solid #FB9F34;
-  background: #FFF5EB;
-  color: #333;
+.button_editor.text_color_red {
+  color: #F56C6C;
 }
 </style>

+ 174 - 0
src/views/school/subjectList.vue

@@ -0,0 +1,174 @@
+<template>
+  <el-dialog v-model="subjectShowDialog" title="科目列表" width="600px">
+    <div class="subject_list">
+      <div v-for="tag in subjectList" :key="tag.id" class="list_item">
+        <span>{{ tag.courseName }}</span>
+        <img src="@/assets/icon/delete.svg" @click="delSubject(tag.id)">
+      </div>
+      <el-input ref="InputRef" v-if="inputVisible" v-model="inputValue" @keyup.enter="handleInputConfirm" @blur="InputBlur" class="input_item">
+        <template #append>
+          <img src="@/assets/icon/confirm.svg" @click="handleInputConfirm">
+        </template>
+      </el-input>
+      <div class="list_item cancel" v-if="inputVisible" @click="cancelAdd">
+        <img src="@/assets/icon/cancel.svg">
+        <span>取消</span>
+      </div>
+      <div v-else class="list_item add" @click="AddCourse">
+        <img src="@/assets/icon/add.svg">
+        <span>添加</span>
+      </div>
+    </div>
+  </el-dialog>
+</template>
+
+<script lang="ts">
+export default {
+  name: 'SubjectList'
+}
+</script>
+<script lang="ts" setup>
+import { ref, inject, reactive, nextTick, onMounted } from 'vue'
+import { ElMessage } from 'element-plus'
+import { getAllSubject, deleteSubject, addSubject } from '@/api/school'
+let subjectShowDialog = inject('subjectShowDialog')
+const subjectList = ref([])
+const inputValue = ref('')
+const inputVisible = ref(false)
+onMounted(() => {
+  GetAllSubject()
+})
+// 获取所有科目
+const GetAllSubject = () => {
+  getAllSubject({}).then((res) => {
+    if (res.code === 200 && res.data) {
+      const { data } = res
+      subjectList.value = data
+    }
+  })
+}
+
+const InputBlur = () => {
+  if(!inputValue.value) {
+    inputVisible.value = false
+    inputValue.value = ''
+  }
+}
+const handleInputConfirm = () => {
+  if (inputValue.value) {
+    addSubject({ courseName: inputValue.value }).then((res) => {
+      if (res.code === 200 && res.data) {
+        ElMessage.success(res.msg)
+        GetAllSubject()
+        inputVisible.value = false
+        inputValue.value = ''
+      } else {
+        ElMessage.error(res.msg)
+      }
+    })
+  } else {
+    inputVisible.value = false
+    inputValue.value = ''
+  }
+}
+const InputRef = ref<HTMLElement | null>(null)
+const AddCourse = () => {
+  inputVisible.value = true;
+  nextTick(() => {
+    // (InputRef.value as HTMLInputElement).focus()
+  })
+  
+}
+const cancelAdd = () => {
+  inputVisible.value = false
+  inputValue.value = ''
+}
+const delSubject = (id) => {
+  deleteSubject({ ids: [id] }).then((res) => { 
+    if (res.code === 200) {
+      ElMessage.success(res.msg)
+      GetAllSubject()
+    } else {
+      ElMessage.error(res.msg)
+    }
+  })
+}
+</script>
+
+<style lang="scss" scoped>
+ .el-dialog  ::deep.el-dialog__body  {
+  min-height: 300px !important;
+  padding: 50px!important;
+}
+.subject_list {
+  height: auto;
+  display: flex;
+  justify-content: flex-start;
+  flex-wrap: wrap;
+  gap:20px ;
+  .list_item {
+    background: rgba(46,100,250,0.1);
+    border-radius: 4px 4px 4px 4px;
+    border: 1px solid #2E64FA;
+    width: auto;
+    height: 36px;
+    line-height: 36px;
+    padding: 0 10px;
+    font-weight: 400;
+    font-size: 14px;
+    color: #2E64FA;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    span {
+      margin-right: 8px;
+    }
+    img {
+      width: 16px;
+      height: 16px;
+      cursor: pointer;
+    }
+    img:hover {
+      opacity: 0.8;
+    }
+    &.add, 
+    &.cancel {
+      background: #fff;
+      span {
+        margin-right: 0;
+      }
+      img {
+        margin-right: 5px;
+      }
+      cursor: pointer;
+    }
+    &.cancel {
+      border-color: #BBBBBB;
+      color: #BBBBBB;
+    }
+  }
+  ::v-deep(.input_item.el-input) {
+    width: 74px;
+    // width: auto;
+    height: 36px;
+    border-radius: 4px;
+    border: 1px solid #2E64FA!important;
+    .el-input__wrapper {
+      height: 34px;
+      box-shadow: none;
+      padding: 1px 5px;
+    }
+    .el-input__inner {
+      border: none;  /* 去掉边框 */
+      background-color: transparent;  /* 去掉背景色 */
+      box-shadow: none;  /* 去掉阴影 */
+    }
+    .el-input-group__append {
+      padding: 0 5px 0 0;
+      background: #fff;
+      box-shadow: none;
+      cursor: pointer;
+    }
+  }
+}
+</style>

+ 7 - 7
vite.config.ts

@@ -11,14 +11,14 @@ export default defineConfig({
   },
   server: {
     proxy:{
-      '/api':{
-        // target:'https://dev.k12100.net/admin/adminApi',
-        target:'https://dev.k12100.net/teaching/api/',
-        changeOrigin:true,
-        rewrite:path => path.replace(/^\/api/, '')
-      },
+      // '/api':{
+      //   // target:'https://dev.k12100.net/admin/adminApi',
+      //   target:'https://dev3.k12100.net/teaching/api/',
+      //   changeOrigin:true,
+      //   rewrite:path => path.replace(/^\/api/, '')
+      // },
       '/adminApi':{
-        target:'https://dev.k12100.net/admin/adminApi',
+        target:'https://dev3.k12100.net/admin/adminApi',
         changeOrigin:true,
         rewrite:path => path.replace(/^\/adminApi/, '')
       }