index.vue 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839
  1. <template>
  2. <div class="main_container" @scroll="OnScroll" ref="scrollContainer">
  3. <div class="user_num_content">
  4. <div class="user_item all">
  5. <div class="item_left">
  6. <img src="@/assets/icon/user_all.svg" alt="">
  7. <p>全部用户</p>
  8. </div>
  9. <div class="item_right">{{ userNumber.allUser }}</div>
  10. </div>
  11. <div class="user_item blue">
  12. <div class="item_left">
  13. <img src="@/assets/icon/user_blue.svg" alt="">
  14. <p>正式用户</p>
  15. </div>
  16. <div class="item_right">{{ userNumber.officialUser }}</div>
  17. </div>
  18. <div class="user_item green">
  19. <div class="item_left">
  20. <img src="@/assets/icon/user_green.svg" alt="">
  21. <p>体验用户</p>
  22. </div>
  23. <div class="item_right">{{ userNumber.experienceUser }}</div>
  24. </div>
  25. <div class="user_item orange">
  26. <div class="item_left">
  27. <img src="@/assets/icon/user_orange.svg" alt="">
  28. <p>冻结用户</p>
  29. </div>
  30. <div class="item_right">{{ userNumber.freezeUser }}</div>
  31. </div>
  32. </div>
  33. <div ref="filterRef" class="filter_area">
  34. <!-- <SearchMain @FnSearch="FnSearch" /> -->
  35. <div class="page_item">
  36. <div class="tag_group" v-if="searchList.provinceNode.length > 1">
  37. <div class="tag_group_title">省份:</div>
  38. <div class="tag_group_content">
  39. <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' : ''">
  40. {{ item.provinceName }}
  41. </div>
  42. </div>
  43. </div>
  44. <div class="tag_group" v-if="cityNode.length > 0 && !isMunicipality">
  45. <div class="tag_group_title">城市:</div>
  46. <div class="tag_group_content">
  47. <div class="tag_item" v-for="item in cityNode" :key="item.cityCode" @click="selectItem(item.cityCode, 'cityCode', item)" :class="item.cityCode === searchData.cityCode ? 'active' : ''">
  48. {{ item.cityName }}
  49. </div>
  50. </div>
  51. </div>
  52. <div class="tag_group" v-if="districtNode.length > 0">
  53. <div class="tag_group_title">区/县:</div>
  54. <div class="tag_group_content">
  55. <div class="tag_item" v-for="item in districtNode" :key="item.districtCode" @click="selectItem(item.districtCode, 'districtCode', item)" :class="item.districtCode === searchData.districtCode ? 'active' : ''">
  56. {{ item.districtName }}
  57. </div>
  58. </div>
  59. </div>
  60. <div class="tag_group" v-if="searchList.userInfo.length > 1">
  61. <div class="tag_group_title">运维:</div>
  62. <div class="tag_group_content">
  63. <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' : ''">
  64. {{ item.nickname }}
  65. </div>
  66. </div>
  67. </div>
  68. <div class="tag_group" v-if="searchList.typeNode.length > 1">
  69. <div class="tag_group_title">类型:</div>
  70. <div class="tag_group_content">
  71. <div class="tag_item" v-for="item in searchList.typeNode" :key="item.type" @click="selectItem(item.type, 'type', item)" :class="item.type === searchData.type ? 'active' : ''">
  72. {{ item.typeName }}
  73. </div>
  74. </div>
  75. </div>
  76. <div class="tag_group" v-if="searchList.statusNodes.length > 1">
  77. <div class="tag_group_title">状态:</div>
  78. <div class="tag_group_content">
  79. <div class="tag_item" v-for="item in searchList.statusNodes" :key="item.status" @click="selectItem(item.status, 'status', item)" :class="item.status === searchData.status ? 'active' : ''">
  80. {{ item.statusName }}
  81. </div>
  82. </div>
  83. </div>
  84. </div>
  85. </div>
  86. <div class="page_jg_20"></div>
  87. <div class="page_item">
  88. <div class="page_search">
  89. <div class="search_content" ref="searchContentRef">
  90. <div class="content_left" style="flex: 1;">
  91. <div class="simple_filter" v-if="isShowSimpleFilter">
  92. <el-select v-model="searchData.provinceCode" @change="ChangeRegion('provinceCode')" v-if="searchList.provinceNode && searchList.provinceNode.length > 1">
  93. <el-option v-for="item in searchList.provinceNode" :key="item.provinceCode" :value="item.provinceCode" :label="item.provinceName == '全部' ? '全部省份' : item.provinceName"></el-option>
  94. </el-select>
  95. <el-select v-model="searchData.cityCode" @change="ChangeRegion('cityCode')" v-if="cityNode && cityNode.length > 1 && !isMunicipality">
  96. <el-option v-for="item in cityNode" :key="item.cityCode" :value="item.cityCode" :label="item.cityName == '全部' ? '全部城市' : item.cityName"></el-option>
  97. </el-select>
  98. <el-select v-model="searchData.districtCode" @change="ChangeRegion('districtCode')" v-if="districtNode && districtNode.length > 1">
  99. <el-option v-for="item in districtNode" :key="item.districtCode" :value="item.districtCode" :label="item.districtName == '全部' ? '全部区县' : item.districtName"></el-option>
  100. </el-select>
  101. <el-select v-model="searchData.userId" @change="SearchChange" v-if="searchList.userInfo && searchList.userInfo.length > 1">
  102. <el-option v-for="item in searchList.userInfo" :key="item.userId" :value="item.userId" :label="item.nickname == '全部' ? '全部运维' : item.nickname"></el-option>
  103. </el-select>
  104. <el-select v-model="searchData.type" @change="SearchChange" v-if="searchList.typeNode && searchList.typeNode.length > 1">
  105. <el-option v-for="item in searchList.typeNode" :key="item.type" :value="item.type" :label="item.typeName == '全部' ? '全部类型' : item.typeName"></el-option>
  106. </el-select>
  107. <el-select v-model="searchData.status" @change="SearchChange" v-if="searchList.statusNodes && searchList.statusNodes.length > 1">
  108. <el-option v-for="item in searchList.statusNodes" :key="item.status" :value="item.status" :label="item.statusName == '全部' ? '全部状态' : item.statusName"></el-option>
  109. </el-select>
  110. </div>
  111. <el-select v-model="schoolTypeSelect" @change="SearchChange">
  112. <el-option label="全部类型" value="all"></el-option>
  113. <el-option label="单校" :value="0"></el-option>
  114. <el-option label="联考" :value="1"></el-option>
  115. </el-select>
  116. <el-input v-model="schoolNameCode" placeholder="请输入学校名称或编号" @input="SearchChange">
  117. <template #append>
  118. <el-button :icon="Search" @click="SearchChange" />
  119. </template>
  120. </el-input>
  121. </div>
  122. <div class="content_right">
  123. <!-- <el-button class="button_refresh" @click="Refresh">
  124. <img src="@/assets/icon/refresh.svg" alt="" style="margin-right: 5px;">刷新
  125. </el-button> -->
  126. <el-button class="button_border" @click="OpenSubject" v-if="permissions.includes('schoolOpenSubjects')">开通科目</el-button>
  127. <el-button class="button_background" @click="addUser" v-if="permissions.includes('schoolCreate')">新增用户</el-button>
  128. </div>
  129. </div>
  130. </div>
  131. <div class="page_jg_20"></div>
  132. <div class="page_table">
  133. <el-table :data="tableData" stripe v-loading="tableLoading" element-loading-text="拼命加载中……" :height="tableHeight"
  134. element-loading-spinner="el-icon-loading" element-loading-background="#ffffff">
  135. <el-table-column prop="index" label="序号" fixed="left" align="center" />
  136. <el-table-column prop="tenantCode" label="学校编号" width="80" fixed="left" align="center" show-overflow-tooltip />
  137. <el-table-column prop="tenantName" label="学校名称" min-width="160" fixed="left" align="center" show-overflow-tooltip>
  138. <template #default="{row}">
  139. <span @click="GoTeach(row)" :class="row.status == 0 || row.dateDiffDayS==0 || !permissions.includes('UserInfo') ? '' : 'text_color_blue'">{{ row.tenantName }}</span>
  140. </template>
  141. </el-table-column>
  142. <el-table-column prop="abbreviation" label="学校简称" width="120" align="center" show-overflow-tooltip>
  143. <template #default="{ row }">
  144. {{ row.abbreviation || '-' }}
  145. </template>
  146. </el-table-column>
  147. <el-table-column prop="cityAndAreaName" label="区域" width="160" align="center" show-overflow-tooltip>
  148. <template #default="{ row }">
  149. {{ row.cityAndAreaName || '-' }}
  150. </template>
  151. </el-table-column>
  152. <el-table-column prop="schoolType" label="账号类型" width="100" align="center">
  153. <template #default="{ row }">
  154. {{ row.schoolType == 0 ? '单校版' : row.schoolType == 1 ? '联考版' : '-' }}
  155. </template>
  156. </el-table-column>
  157. <el-table-column prop="startDate" label="开始时间" width="100" align="center" show-overflow-tooltip />
  158. <el-table-column prop="endDate" label="结束时间" width="100" align="center" show-overflow-tooltip />
  159. <el-table-column prop="dateDiffDayS" label="剩余天数" width="100" align="center" />
  160. <el-table-column prop="type" label="状态" width="80" align="center">
  161. <template #default="{ row }">
  162. <div class="table_row_status">
  163. <!-- 冻结 -->
  164. <div class="no_release_icon" v-if="row.status==0"></div>
  165. <!-- 正式 -->
  166. <div class="need_summary_icon" v-if="row.status==1 && row.type==1"></div>
  167. <!-- 体验 -->
  168. <div class="yes_release_icon" v-if="row.status==1 && row.type==0"></div>
  169. <!-- {{ row.statusLabel }} -->
  170. {{ row.status == 0 ? '冻结' : row.type == 1 ? '正式' : row.type == 0 ? '体验' : '-' }}
  171. </div>
  172. </template>
  173. </el-table-column>
  174. <el-table-column prop="sysUserName" width="120" label="运维人员" align="center" show-overflow-tooltip>
  175. <template #default="{ row }">
  176. {{ row.sysUserName || '-' }}
  177. </template>
  178. </el-table-column>
  179. <el-table-column label="操作" width="280" fixed="right" align="center">
  180. <template #default="{ row }">
  181. <div class="table_row_option">
  182. <span v-if="row.status == 0 && permissions.includes('schoolFreeze')" class="button_delete" @click="ChangeStatus(row, '解冻')">
  183. 解冻
  184. </span>
  185. <span v-if="row.status != 0 && permissions.includes('schoolFreeze')" class="button_editor text_color_orange" @click="ChangeStatus(row, '冻结')">
  186. 冻结
  187. </span>
  188. <span class="button_editor" @click="EditUser(row)" v-if="permissions.includes('schoolUpdate')">编辑</span>
  189. <span class="button_editor" @click="OpenAuth(row)" v-if="permissions.includes('schoolPermission')">权限</span>
  190. <span class="button_editor" @click="resetPassWord(row)" v-if="permissions.includes('schoolPassword')">重置密码</span>
  191. <span class="button_editor" :class="row.status == 0 || row.dateDiffDayS==0 ? 'disabled_text' : ''" @click="GoTeach(row)" v-if="permissions.includes('UserInfo') ">进入学校</span>
  192. </div>
  193. </template>
  194. </el-table-column>
  195. </el-table>
  196. <div class="page_pagination">
  197. <el-pagination v-model:current-page="pageNum" v-model:page-size="pageSize" :page-sizes="[20, 50, 100]" background layout="sizes, prev, pager, next" :total="total"
  198. @current-change="HandleCurrentChange" @size-change="HandleSizeChange" />
  199. </div>
  200. </div>
  201. </div>
  202. <div class="page_dialog">
  203. <AddUser v-if="showDialog" @AddUser="RefTable" />
  204. <AuthConfig v-if="authData.showDialog" />
  205. <SubjectList v-if="subjectShowDialog" />
  206. <ResetPassword v-if="passwordDialog.dialogShow" />
  207. </div>
  208. </div>
  209. </template>
  210. <script lang="ts" setup>
  211. import SearchMain from '@/views/school/SearchMain.vue'
  212. import AddUser from '@/views/school/addUser.vue'
  213. import AuthConfig from '@/views/school/authConfig.vue'
  214. import SubjectList from '@/views/school/subjectList.vue'
  215. import ResetPassword from '@/components/resetPassword.vue'
  216. import { Search } from '@element-plus/icons-vue'
  217. import { ElMessageBox, ElMessage } from 'element-plus'
  218. import { ref, reactive, provide, onMounted, nextTick, onUnmounted } from 'vue'
  219. import { getSchoolNumber, getSchoolList, changeStatus ,getTicket, getSearchList} from '@/api/school'
  220. import { throttle } from 'lodash';
  221. interface UserNumber {
  222. allUser: number
  223. officialUser: number
  224. experienceUser: number
  225. freezeUser: number
  226. }
  227. const userNumber = ref<UserNumber>({
  228. allUser: 0,
  229. officialUser: 0,
  230. experienceUser: 0,
  231. freezeUser: 0
  232. })
  233. const searchData = ref({
  234. provinceCode: 'all',
  235. cityCode: 'all',
  236. districtCode: 'all',
  237. type: 'all',
  238. userId: 'all',
  239. status: 'all',
  240. })
  241. interface Province {
  242. provinceCode: string;
  243. provinceName: string;
  244. children?: City[]; // 如果有子级城市数据的话
  245. }
  246. interface City {
  247. cityCode: string
  248. cityName: string
  249. children?: District[]; // 如果有子级区县数据的话
  250. }
  251. interface District {
  252. districtCode: string
  253. districtName: string
  254. }
  255. const cityNode = ref<City[]>([])
  256. const districtNode = ref<District[]>([])
  257. const isMunicipality = ref(false) // 是否为直辖市
  258. const municipalityList = ['北京', '上海', '天津', '重庆']
  259. interface TypeNode {
  260. type: string;
  261. typeName: string;
  262. }
  263. interface UserInfo {
  264. userId: string;
  265. nickname: string;
  266. }
  267. interface StatusNodes {
  268. status: string;
  269. statusName: string;
  270. }
  271. interface SearchList {
  272. examTypes: any[];
  273. provinceNode: Province[];
  274. cityNode: City[];
  275. districtNode: District[];
  276. typeNode: TypeNode[];
  277. userInfo: UserInfo[];
  278. statusNodes: StatusNodes[];
  279. }
  280. const searchList = ref<SearchList>({
  281. examTypes: [],
  282. provinceNode: [],
  283. cityNode: [],
  284. districtNode: [],
  285. typeNode: [],
  286. userInfo: [],
  287. statusNodes: []
  288. })
  289. // const emit = defineEmits(['FnSearch'])
  290. type SearchDataKey = 'provinceCode' | 'cityCode' | 'districtCode' | 'type' | 'userId' | 'status'
  291. const selectItem = (value: string, str: SearchDataKey, item: any) => {
  292. searchData.value[str] = value
  293. let arr: any[] = []
  294. if(str == 'provinceCode') {
  295. if(value && value != 'all') {
  296. // 判断是否为直辖市
  297. isMunicipality.value = municipalityList.some(m => item.provinceName.includes(m))
  298. arr = [...item.children]
  299. arr.splice(0, 0, { cityCode: "all", cityName: "全部" })
  300. cityNode.value = arr
  301. districtNode.value = []
  302. searchData.value.districtCode = 'all'
  303. if(isMunicipality.value) {
  304. // 直辖市:自动选中第一个非"全部"的城市,加载区县
  305. const firstCity = item.children[0]
  306. if(firstCity) {
  307. searchData.value.cityCode = firstCity.cityCode
  308. const districtArr = [...(firstCity.children || [])]
  309. districtArr.splice(0, 0, { districtCode: "all", districtName: "全部" })
  310. districtNode.value = districtArr
  311. }
  312. } else {
  313. searchData.value.cityCode = 'all'
  314. }
  315. }else {
  316. isMunicipality.value = false
  317. cityNode.value = []
  318. searchData.value.cityCode = 'all'
  319. districtNode.value = []
  320. searchData.value.districtCode = 'all'
  321. }
  322. }
  323. if(str == 'cityCode') {
  324. if(value && value != 'all') {
  325. arr = [...item.children]
  326. arr.splice(0, 0, { districtCode: "all", districtName: "全部" })
  327. districtNode.value = arr
  328. searchData.value.districtCode = 'all'
  329. }else {
  330. districtNode.value = []
  331. searchData.value.districtCode = 'all'
  332. }
  333. }
  334. FnSearch(searchData)
  335. // emit('FnSearch', searchData);
  336. }
  337. const ChangeRegion = (str: SearchDataKey) => {
  338. let arr: any[] = []
  339. if(str == 'provinceCode') {
  340. if(searchData.value.provinceCode && searchData.value.provinceCode != 'all') {
  341. const provinceItem = searchList.value.provinceNode.find(item => item.provinceCode == searchData.value.provinceCode)
  342. isMunicipality.value = provinceItem ? municipalityList.some(m => provinceItem.provinceName.includes(m)) : false
  343. arr = [...(provinceItem?.children || [])]
  344. arr.splice(0, 0, { cityCode: "all", cityName: "全部" })
  345. cityNode.value = arr
  346. districtNode.value = []
  347. searchData.value.districtCode = 'all'
  348. if(isMunicipality.value && provinceItem) {
  349. // 直辖市:自动选中第一个城市,加载区县
  350. const firstCity = provinceItem.children?.[0]
  351. if(firstCity) {
  352. searchData.value.cityCode = firstCity.cityCode
  353. const districtArr = [...(firstCity.children || [])]
  354. districtArr.splice(0, 0, { districtCode: "all", districtName: "全部" })
  355. districtNode.value = districtArr
  356. }
  357. } else {
  358. searchData.value.cityCode = 'all'
  359. }
  360. } else {
  361. isMunicipality.value = false
  362. cityNode.value = []
  363. searchData.value.cityCode = 'all'
  364. districtNode.value = []
  365. searchData.value.districtCode = 'all'
  366. }
  367. }
  368. if(str == 'cityCode') {
  369. if(searchData.value.cityCode && searchData.value.cityCode != 'all') {
  370. arr = [...(cityNode.value.find(item => item.cityCode == searchData.value.cityCode)?.children || [])]
  371. arr.splice(0, 0, { districtCode: "all", districtName: "全部" })
  372. districtNode.value = arr
  373. searchData.value.districtCode = 'all'
  374. }else {
  375. districtNode.value = []
  376. searchData.value.districtCode = 'all'
  377. }
  378. }
  379. SearchChange()
  380. }
  381. const GetSearchList = () => {
  382. getSearchList().then((res: any) => {
  383. console.log(res)
  384. if(res.code == 200) {
  385. const { data } = res
  386. for(let i in data) {
  387. if(i == 'provinceNode') {
  388. data[i].splice(0, 0, { provinceCode: "all", provinceName: "全部" })
  389. }else if(i == 'cityNode') {
  390. data[i].splice(0, 0, { cityCode: "all", cityName: "全部" })
  391. }else if(i == 'districtNode') {
  392. data[i].splice(0, 0, { districtCode: "all", districtName: "全部" })
  393. }else if( i == 'typeNode') {
  394. data[i].splice(0, 0, { type: "all", typeName: "全部" })
  395. }else if( i == 'userInfo') {
  396. data[i].splice(0, 0, { userId: "all", nickname: "全部" })
  397. }else if( i == 'statusNodes') {
  398. data[i].splice(0, 0, { status: "all", statusName: "全部" })
  399. }
  400. }
  401. searchList.value = data
  402. console.log(searchList.value)
  403. }
  404. })
  405. }
  406. // 定义列表数据类型
  407. interface User {
  408. index: number;
  409. tenantCode: String;
  410. tenantName: String;
  411. status: number;
  412. statusLabel: String;
  413. schoolType: String;
  414. abbreviation: String;
  415. dateDiffDayS: String;
  416. startDate: String;
  417. endDate: String;
  418. sysUserId: String;
  419. sysUserName: String;
  420. cityAndAreaName: String;
  421. }
  422. // 初始化列表数据
  423. const tableData = ref<User[]>([])
  424. const schoolTypeSelect = ref<string>("all")
  425. const schoolNameCode = ref('')
  426. const pageNum = ref(1)
  427. const pageSize = ref(20)
  428. const total = ref(0)
  429. const tableLoading = ref(false)
  430. const showDialog = ref(false)
  431. const dialogData = reactive({
  432. id: '',
  433. pageType: '',
  434. isChild: false,
  435. tenantName: ''
  436. })
  437. const subjectShowDialog = ref(false)
  438. // 编辑权限传递参数
  439. const authData = reactive({
  440. showDialog: false,
  441. id: '',
  442. schoolName: '',
  443. tenantCode: ''
  444. })
  445. // 重置密码弹窗数据
  446. const passwordDialog = reactive({
  447. dialogShow: false,
  448. id: '', // 学校id
  449. type: '', // 学校状态
  450. name: '', // 学校名称
  451. label: '', // 名称label
  452. remark: '' // 提示语
  453. }) // 重置密码弹窗数据
  454. const permissions = ref<string[]>([]) // 权限列表
  455. provide('showDialog', showDialog) // 新增用户弹窗是否展示
  456. provide('dialogData', dialogData) // 新增用户弹窗数据
  457. provide('authData', authData) // 设置权限弹窗数据
  458. provide('subjectShowDialog', subjectShowDialog) // 开通科目弹窗数据
  459. provide('passwordDialog', passwordDialog) // 重置密码弹窗数据
  460. onMounted(() => {
  461. permissions.value = JSON.parse(localStorage.getItem('permissions') || '[]')
  462. GetSearchList()
  463. GetSchoolNumber()
  464. GetSchoolList()
  465. nextTick(() => {
  466. SetTableHeight()
  467. })
  468. window.addEventListener('resize', HandleResize);
  469. })
  470. onUnmounted(() => {
  471. window.removeEventListener('resize', HandleResize);
  472. });
  473. // 新增用户
  474. const addUser = () => {
  475. showDialog.value = true
  476. dialogData.isChild = false
  477. dialogData.pageType = 'add'
  478. }
  479. // 编辑
  480. const EditUser = (row: any) => {
  481. console.log(row)
  482. showDialog.value = true
  483. dialogData.pageType = 'edit'
  484. dialogData.id = row.id
  485. dialogData.isChild = row.isChild
  486. dialogData.tenantName = row.tenantName
  487. }
  488. // 设置权限
  489. const OpenAuth = (row: any) => {
  490. authData.id = row.id
  491. authData.schoolName = row.tenantName
  492. authData.tenantCode = row.tenantCode
  493. authData.showDialog = true
  494. }
  495. // 开通科目
  496. const OpenSubject = () => {
  497. console.log('OpenSubject')
  498. subjectShowDialog.value = true
  499. }
  500. // 筛选项查询
  501. const FnSearch = (data: any) => {
  502. searchData.value = data.value
  503. pageNum.value = 1
  504. GetSchoolList()
  505. }
  506. // 表格上面筛选查询
  507. const SearchChange = () => {
  508. pageNum.value = 1
  509. GetSchoolList()
  510. }
  511. // 获取用户数量
  512. const GetSchoolNumber = () => {
  513. getSchoolNumber().then((res: any) => {
  514. if(res.code == 200 && res.data) {
  515. userNumber.value = res.data
  516. }
  517. })
  518. }
  519. // 获取学校列表
  520. const GetSchoolList = () => {
  521. let params = {
  522. type: searchData.value.type == 'all' ? '' : searchData.value.type,
  523. status: searchData.value.status == 'all' ? '' : searchData.value.status,
  524. userId: searchData.value.userId == 'all' ? '' : searchData.value.userId,
  525. cityCode: searchData.value.cityCode == 'all' ? '' : searchData.value.cityCode,
  526. provinceCode: searchData.value.provinceCode == 'all' ? '' : searchData.value.provinceCode,
  527. areaCode: searchData.value.districtCode == 'all' ? '' : searchData.value.districtCode,
  528. schoolType: schoolTypeSelect.value == 'all' ? '' : schoolTypeSelect.value,
  529. queryStr: schoolNameCode.value,
  530. pageNum: pageNum.value,
  531. pageSize: pageSize.value,
  532. }
  533. tableLoading.value = true
  534. getSchoolList(params).then((res: any) => {
  535. if(res.code == 200 && res.data) {
  536. tableData.value = res.data.records
  537. total.value = parseInt(res.data.total)
  538. tableData.value.map((item, index) => {
  539. item.index = (pageNum.value - 1) * pageSize.value + index + 1
  540. })
  541. }
  542. tableLoading.value = false
  543. }).catch(() => {
  544. tableLoading.value = false
  545. })
  546. }
  547. // 切换分页页码
  548. const HandleCurrentChange = (val: number) => {
  549. pageNum.value = val
  550. GetSchoolList()
  551. }
  552. const HandleSizeChange = (val: number) => {
  553. pageSize.value = val
  554. pageNum.value = 1
  555. GetSchoolList()
  556. }
  557. // 新增/修改学校信息后刷新列表
  558. const RefTable = (val: any) => {
  559. showDialog.value = val
  560. GetSearchList()
  561. GetSchoolNumber()
  562. GetSchoolList()
  563. }
  564. // 改变用户状态
  565. const ChangeStatus = (row: any, text: String) => {
  566. console.log(row)
  567. ElMessageBox.confirm(`是否${text}${row.tenantName}?`, '提示', {
  568. confirmButtonText: '确定',
  569. cancelButtonText: '取消',
  570. type: 'warning'
  571. }).then(async () => {
  572. changeStatus({
  573. id: row.id,
  574. status: row.status == 0 ? 1 : 0
  575. }).then((res: any) => {
  576. if(res.code == 200) {
  577. ElMessage.success(res.msg)
  578. GetSearchList()
  579. GetSchoolNumber()
  580. GetSchoolList()
  581. }
  582. })
  583. })
  584. }
  585. // 进入学校
  586. const GoTeach = (row: any) => {
  587. //剩余天数为0的禁止进入学校
  588. if(row.status == 0 || row.dateDiffDayS==0)
  589. {
  590. if(row.dateDiffDayS==0)
  591. {
  592. ElMessage.error('该学校已到期,请续费!')
  593. }
  594. return
  595. }
  596. console.log("打印row",row)
  597. let params = {
  598. schoolId: row.id,
  599. }
  600. getTicket(params).then((res: any) => {
  601. console.log("打印获取票据接口返回res",res)
  602. if(res.code == 200 && res.data)
  603. {
  604. let ticket = res.data;
  605. let url=`https://www.k12100.com/#/otherLogin?schoolId=${row.id}&ticket=${ticket}`;//正式环境
  606. if(import.meta.env.VITE_API_BASE_URL=='https://admin.k12100.com/admin/')
  607. {
  608. url=`https://www.k12100.com/#/otherLogin?schoolId=${row.id}&ticket=${ticket}`;//正式环境
  609. }
  610. else
  611. {
  612. url=`https://dev3.k12100.net/#/otherLogin?schoolId=${row.id}&ticket=${ticket}`;//测试环境
  613. }
  614. // let url=`https://dev3.k12100.net/#/otherLogin?schoolId=${row.id}&ticket=${ticket}`;//测试环境
  615. console.log("打印获取票据接口返回ticket",ticket)
  616. console.log("打印第三方登录地址",url);
  617. // window.open(`https://www.k12100.com/#/otherLogin?schoolId=${row.id}&ticket=${ticket}`)
  618. window.open(url)
  619. }
  620. }).catch(() => {
  621. })
  622. }
  623. // 重置密码
  624. const resetPassWord = (row: any) => {
  625. passwordDialog.dialogShow = true
  626. passwordDialog.id = row.id
  627. passwordDialog.type = '2'
  628. passwordDialog.name = row.tenantName
  629. passwordDialog.label = '学校名称'
  630. passwordDialog.remark = '学校编号'
  631. }
  632. // 刷新
  633. const Refresh = () => {
  634. schoolTypeSelect.value = " "
  635. schoolNameCode.value = ""
  636. GetSchoolList()
  637. }
  638. const filterRef = ref<HTMLDivElement | null>(null)
  639. const scrollContainer = ref<HTMLElement | null>(null);
  640. const isShowSimpleFilter = ref(false);
  641. // 监听页面滚动
  642. const OnScroll = () => {
  643. nextTick(() => {
  644. const filterElement = (filterRef.value as any)?.$el || filterRef.value // 获取 DOM 元素
  645. if (!filterElement) return
  646. const filterElementHeight = filterElement.getBoundingClientRect().height
  647. const scrollTop = scrollContainer.value?.scrollTop || window.scrollY
  648. // console.log('筛选区域高度', filterElementHeight)
  649. // console.log('scrolltop滑动距离', scrollTop)
  650. // 判断是否滚动到了筛选区域的底部
  651. if (scrollTop > filterElementHeight + 60) {
  652. isShowSimpleFilter.value = true
  653. SetTableHeight()
  654. } else {
  655. isShowSimpleFilter.value = false
  656. }
  657. })
  658. };
  659. const tableHeight = ref(0)
  660. const searchContentRef = ref<HTMLDivElement | null>(null);
  661. const SetTableHeight = () => {
  662. const clientHeight = scrollContainer.value?.clientHeight || document.documentElement.clientHeight;
  663. const searchContentHeight = searchContentRef.value?.clientHeight || document.documentElement.clientHeight;
  664. tableHeight.value = clientHeight - searchContentHeight - 148
  665. }
  666. // 监听窗口大小变化,重新计算表格高度 使用节流防止频繁改变窗口大小导致计算量过大而页面卡顿
  667. const HandleResize = throttle(() => {
  668. SetTableHeight();
  669. }, 500) // 节流 500 毫秒内最多执行一次
  670. </script>
  671. <style lang="scss" scoped>
  672. .simple_filter {
  673. display: flex;
  674. flex-wrap: nowrap;
  675. gap: 10px;
  676. flex-shrink: 0;
  677. }
  678. .search_content {
  679. align-items: center !important;
  680. }
  681. .content_left {
  682. display: flex;
  683. align-items: center;
  684. gap: 10px;
  685. flex-wrap: wrap;
  686. }
  687. .user_num_content {
  688. width: 100%;
  689. display: flex;
  690. gap: 20px;
  691. justify-content: space-between;
  692. align-items: center;
  693. margin-bottom: 20px;
  694. .user_item {
  695. flex-grow: 1;
  696. height: 80px;
  697. padding: 20px;
  698. display: flex;
  699. justify-content: space-between;
  700. align-items: center;
  701. border-radius: 10px;
  702. font-weight: 500;
  703. .item_left {
  704. display: flex;
  705. align-items: center;
  706. img {
  707. margin-right: 10px;
  708. }
  709. }
  710. &.all {
  711. background: #2E64FA;
  712. color: #fff;
  713. .item_right {
  714. color: #fff;
  715. }
  716. }
  717. &.blue {
  718. border: 1px solid #2E64FA;
  719. background: #EAF0FF;
  720. color: #333;
  721. .item_right {
  722. color: #2E64FA;
  723. }
  724. }
  725. &.green {
  726. border: 1px solid #2BC644;
  727. background: #EAF9EC;
  728. color: #333;
  729. .item_right {
  730. color: #2BC644;
  731. }
  732. }
  733. &.orange {
  734. border: 1px solid #FB9F34;
  735. background: #FFF5EB;
  736. color: #333;
  737. .item_right {
  738. color: #FB9F34;
  739. }
  740. }
  741. .item_right {
  742. font-size: 24px;
  743. font-weight: 700;
  744. }
  745. }
  746. }
  747. .button_editor.text_color_orange {
  748. color: #FB9F34;
  749. }
  750. .button_delete {
  751. margin-right: 5px;
  752. }
  753. .button_editor.text_color_red {
  754. color: #F56C6C;
  755. }
  756. .text_color_blue {
  757. color: #2E64FA;
  758. cursor: pointer;
  759. }
  760. .disabled_text {
  761. color: #ccc;
  762. cursor: not-allowed;
  763. }
  764. .filter_area .page_item {
  765. padding: 10px 20px !important;
  766. }
  767. .tag_group{
  768. padding:8px 0;
  769. display: flex;
  770. justify-content: flex-start;
  771. .tag_group_title {
  772. // width: auto;
  773. width: 50px;
  774. height: auto;
  775. margin-right: 8px;
  776. color: #303133;
  777. font-size: 14px;
  778. text-align: right;
  779. line-height: 30px;
  780. font-weight: 500;
  781. letter-spacing: 0em;
  782. }
  783. .tag_group_content {
  784. width: calc(100% - 80px);
  785. display: flex;
  786. flex-wrap: wrap;
  787. gap: 8px;
  788. .tag_item {
  789. padding: 0 8px;
  790. line-height: 30px;
  791. border-radius: 4px;
  792. font-size: 14px;
  793. background: #fff;
  794. color: #999999;
  795. cursor: pointer;
  796. &.active {
  797. background: rgba(71,113,203,0.1);
  798. color: #2E64FA;
  799. }
  800. &:hover {
  801. color: #2E64FA;
  802. }
  803. }
  804. }
  805. }
  806. </style>