From 911f31668f0979a04f85b376d9eaef3e89508294 Mon Sep 17 00:00:00 2001 From: "DESKTOP-RQ919RC\\Pc" <1300399510@qq.com> Date: Fri, 14 Mar 2025 18:56:05 +0800 Subject: [PATCH] no message --- css/projectList.css | 121 ++++++++++- css/projectList.less | 130 +++++++++++- html/projectList.html | 483 ++++++++++++++++++++++++++++++++++++++++-- js/common.js | 2 +- 4 files changed, 714 insertions(+), 22 deletions(-) diff --git a/css/projectList.css b/css/projectList.css index bf3f30d..ec38d3a 100644 --- a/css/projectList.css +++ b/css/projectList.css @@ -99,6 +99,8 @@ color: #7f7f7f; text-align: right; font-size: 13px; + cursor: pointer; + user-select: none; } .boxbox .content .content-left .message .only .icon { width: 14px; @@ -142,12 +144,20 @@ color: #7f7f7f; line-height: 20px; } +.boxbox .content .content-left .list .item .right { + align-items: flex-end; + position: relative; +} .boxbox .content .content-left .list .item .grade { font-size: 14px; line-height: 28px; color: #aaaaaa; margin-bottom: 16px; text-align: right; + position: absolute; + top: 0; + right: 0; + width: max-content; } .boxbox .content .content-left .list .item .grade .sum { font-weight: 650; @@ -169,6 +179,7 @@ .boxbox .content .screen-box { width: 360px; background-color: #fff; + border-radius: 0 0 12px 0; } .boxbox .content .screen-box .head { font-weight: 650; @@ -178,7 +189,7 @@ border-bottom: 1px dotted #ebebeb; text-align: center; padding: 38px 0 22px; - margin: 0 30px; + margin: 0 30px 21px; } .boxbox .content .screen-box .head .icon { width: 18px; @@ -194,6 +205,7 @@ font-style: normal; font-size: 14px; color: #000000; + margin-bottom: 15px; } .boxbox .content .screen-box .screen-item .title .dot { width: 6px; @@ -203,12 +215,119 @@ border-radius: 5px; margin-right: 15px; } +.boxbox .content .screen-box .screen-item .list { + margin-left: 21px; + margin-bottom: 20px; + flex-wrap: wrap; +} .boxbox .content .screen-box .screen-item .list .item { + width: max-content; height: 32px; + line-height: 32px; background-color: #f6f6f6; border-radius: 48px; font-family: "PingFangSC-Regular", "PingFang SC", sans-serif; font-weight: 400; font-size: 14px; padding: 0 20px; + color: #333333; + cursor: pointer; + margin-bottom: 10px; +} +.boxbox .content .screen-box .screen-item .list .item.pitch { + background-color: #fff; + border: 1px solid #6fc16d; + color: #6fc16d; +} +.boxbox .content .screen-box .screen-item .list .item:not(:last-of-type) { + margin-right: 10px; +} +.boxbox .content .screen-box .screen-item.major { + margin: 0 0 0 30px; +} +.boxbox .content .screen-box .screen-item.major .title { + margin-bottom: 0; +} +.boxbox .content .screen-box .screen-item.major .major-box { + padding: 15px 0 15px 11px; + margin-left: 10px; + z-index: 1; + position: relative; +} +.boxbox .content .screen-box .screen-item.major .major-box .major-text { + width: 281px; + height: 32px; + background-color: #f6f6f6; + border-radius: 48px; + font-size: 14px; + color: #aaaaaa; + justify-content: space-between; + padding: 0 15px; + cursor: pointer; +} +.boxbox .content .screen-box .screen-item.major .major-box .major-text.pitch { + background-color: #ffffff; +} +.boxbox .content .screen-box .screen-item.major .major-box .major-text.have { + color: #000000; +} +.boxbox .content .screen-box .screen-item.major .major-box .major-list { + position: absolute; + z-index: -1; + background-color: #f2f2f2; + box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.17254902); + border-radius: 15px; + top: 0; + left: 0; + width: 100%; +} +.boxbox .content .screen-box .screen-item.major .major-box .major-list .major-list-mask { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: transparent; + z-index: -1; +} +.boxbox .content .screen-box .screen-item.major .major-box .major-list .major-list-list { + margin: 64px 11px 20px; + overflow: auto; + height: 400px; +} +.boxbox .content .screen-box .screen-item.major .major-box .major-list .major-item { + line-height: 26px; + font-size: 14px; + color: #333333; + cursor: pointer; + width: max-content; + padding: 0 15px; + border-radius: 75px; + width: 100%; +} +.boxbox .content .screen-box .screen-item.major .major-box .major-list .major-item:hover { + background-color: #fff; + color: #6fc16d; +} +.boxbox .content .screen-box .screen-item.major .major-box .major-list .major-item.pitch { + color: #6fc16d; +} +.boxbox .content .screen-box .btn-box { + border-top: 1px dotted #ebebeb; + padding-top: 30px; + margin: 0 30px 21px; +} +.boxbox .content .screen-box .btn-box .btn { + width: 200px; + height: 32px; + line-height: 32px; + text-align: center; + margin: 0 auto; + background-color: #6fc16d; + border-radius: 6px; + font-family: "PingFangSC-Regular", "PingFang SC", sans-serif; + font-weight: 400; + font-size: 15px; + color: #ffffff; + cursor: pointer; } diff --git a/css/projectList.less b/css/projectList.less index 8ca8e21..9ede363 100644 --- a/css/projectList.less +++ b/css/projectList.less @@ -116,6 +116,8 @@ color: #7f7f7f; text-align: right; font-size: 13px; + cursor: pointer; + user-select: none; } } @@ -161,12 +163,21 @@ } } + .right { + align-items: flex-end; + position: relative; + } + .grade { font-size: 14px; line-height: 28px; color: #aaaaaa; margin-bottom: 16px; text-align: right; + position: absolute; + top: 0; + right: 0; + width: max-content; .sum { font-weight: 650; @@ -195,6 +206,7 @@ .screen-box { width: 360px; background-color: #fff; + border-radius: 0 0 12px 0; .head { .icon { @@ -210,7 +222,7 @@ border-bottom: 1px dotted #ebebeb; text-align: center; padding: 38px 0 22px; - margin: 0 30px; + margin: 0 30px 21px; } .screen-item { @@ -230,19 +242,135 @@ font-style: normal; font-size: 14px; color: #000000; + margin-bottom: 15px; } .list { + margin-left: 21px; + margin-bottom: 20px; + flex-wrap: wrap; .item { + width: max-content; height: 32px; + line-height: 32px; background-color: rgba(246, 246, 246, 1); border-radius: 48px; font-family: "PingFangSC-Regular", "PingFang SC", sans-serif; font-weight: 400; font-size: 14px; padding: 0 20px; + color: #333333; + cursor: pointer; + margin-bottom: 10px; + + &.pitch { + background-color: #fff; + border: 1px solid rgba(111, 193, 109, 1); + color: #6fc16d; + } + + &:not(:last-of-type) { + margin-right: 10px; + } } } + + &.major { + margin: 0 0 0 30px; + .title { + margin-bottom: 0; + } + .major-box { + padding: 15px 0 15px 11px; + margin-left: 10px; + z-index: 1; + position: relative; + .major-text { + width: 281px; + height: 32px; + background-color: rgba(246, 246, 246, 1); + + border-radius: 48px; + font-size: 14px; + color: #aaaaaa; + justify-content: space-between; + padding: 0 15px; + cursor: pointer; + + &.pitch { + background-color: rgba(255, 255, 255, 1); + } + + &.have { + color: #000000; + } + } + .major-list { + position: absolute; + z-index: -1; + background-color: rgba(242, 242, 242, 1); + box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.172549019607843); + border-radius: 15px; + top: 0; + left: 0; + width: 100%; + + .major-list-mask { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: transparent; + z-index: -1; + } + .major-list-list { + margin: 64px 11px 20px; + overflow: auto; + height: 400px; + } + .major-item { + line-height: 26px; + font-size: 14px; + color: #333333; + cursor: pointer; + width: max-content; + padding: 0 15px; + border-radius: 75px; + width: 100%; + &:hover { + background-color: #fff; + color: #6fc16d; + } + + &.pitch { + color: #6fc16d; + } + } + } + } + } + } + + .btn-box { + border-top: 1px dotted #ebebeb; + padding-top: 30px; + margin: 0 30px 21px; + + .btn { + width: 200px; + height: 32px; + line-height: 32px; + text-align: center; + margin: 0 auto; + background-color: rgba(111, 193, 109, 1); + border-radius: 6px; + font-family: "PingFangSC-Regular", "PingFang SC", sans-serif; + font-weight: 400; + font-size: 15px; + color: #ffffff; + cursor: pointer; + } } } } diff --git a/html/projectList.html b/html/projectList.html index 818aa23..fa2b3cb 100644 --- a/html/projectList.html +++ b/html/projectList.html @@ -26,8 +26,8 @@
- -
+
{{ rankingKeyObj[comText.jg] || comText.jg }}世界综合排名({{ comText.year }}年)
+
QS 世界综合排名(2025) @@ -35,7 +35,7 @@
共 -
{{ 000 }}
+
{{ comTotal }}
个排名
@@ -45,24 +45,24 @@
-
-
1
+
+
{{ item.rank }}
-
麻省理工学院(MIT)
-
Massachusetts Institute of Technology (MIT)
-
美国剑桥
+
{{ item.name || item.subject }}
+
{{ item.enname }} {{ item.simple ? `(${ item.simple })` : ''}}
+
{{ item.city || '' }}
-
-
100
-
+
+
{{ item.total_score }}
+ 学校主页 -
+
-
- End -
+
- End -
@@ -72,12 +72,77 @@
- 学校类型 + 榜单分类
-
-
学校排名
+
+
学校排名
+
专业排名
+ +
@@ -87,13 +152,393 @@ const { createApp, ref, onMounted, nextTick, onUnmounted, computed, getCurrentInstance } = Vue; const projectIndex = createApp({ setup() { - console.log("rankingKey", rankingKey); + const rankingKeyObj = ref(rankingKey); onMounted(() => { - console.log("25222"); + window.addEventListener("scroll", handleScroll); + + getBaseData().then((data) => { + const university = JSON.parse(JSON.stringify(data.university || {})); + university.forEach((element) => { + universityArr.push(element.value); + }); + + getRankings(); + }); }); - return { rankingKey }; + let universityArr = []; + + let comOption = ref({}); + let comText = ref({}); + let comDefault = ref({ + jg: "", + year: "", + }); // 列表的筛选 + let comChoose = ref({ + jg: "", + year: "", + }); // 右边的筛选 + let comList = ref([]); + let comPage = ref(1); + let comTotal = ref(0); + let comOnly = ref(0); // 综合 仅显示香港学校 + + let majOption = ref({}); + let majDefault = ref({}); + let majChoose = ref({ + major: "", + jg: "", + year: "", + }); // 右边的筛选 + let majList = ref([]); + let majPage = ref(1); + let majTotal = ref(0); + let majOnly = ref(0); // 综合 仅显示香港学校 + + let classify = ref("school"); // school subject // 左边显示的 + let classifySelect = ref("school"); // school subject // 右边筛选的 + + const getRankings = () => { + $ajax("https://api.gter.net/v1/program/rankings", {}).then((res) => { + if (res.code != 200) return; + const data = res.data; + console.log(11111, data); + + const comprehensive = data.comprehensive; + let com = JSON.parse(JSON.stringify(comChoose.value)); + + for (const key in comprehensive) { + let element = comprehensive[key]; + const years = Object.keys(element) || []; + years.sort((a, b) => b - a); + let obj = {}; + years.forEach((ele) => { + obj[ele] = element[ele]; + }); + element = obj; + } + + let organizationSet = [...objectOne(comprehensive)]; + if (!com["jg"]) com["jg"] = organizationSet[0]; + + let yearsSet = [...collectYears(comprehensive)].sort((a, b) => b - a); + if (!com["year"]) com["year"] = yearsSet[0]; + com["token"] = comprehensive[com.jg]?.[com.year] || ""; + + const discipline = data.discipline; + let maj = JSON.parse(JSON.stringify(majChoose.value)); + const [dOrganizationKey, dOrganizationValue] = Object.entries(discipline)[0]; + if (!maj["jg"]) maj["jg"] = dOrganizationKey; + if (!maj["major"]) maj["major"] = Object.entries(dOrganizationValue)[0][0]; + + const dYear = [...collectYears(discipline)].sort((a, b) => b - a); + if (!maj["year"]) maj["year"] = dYear[0]; + maj["token"] = discipline[maj.jg]?.[maj.major]?.[maj.year] || ""; + + comOption.value = comprehensive; + comDefault.value = com; + + majOption.value = discipline; + majDefault.value = maj; + + initCom(); + initMaj(); + + if (classify.value == "school") getSynthesizeData(); + else getMajorData(); + }); + }; + + let majMajorList = ref([]); + let majJgList = ref([]); + let majYearList = ref([]); + const initMaj = () => { + const option = majOption.value; + const allMajor = Object.values(option).flatMap((institutionData) => Object.keys(institutionData)); + const majorList = [...new Set(allMajor)].sort((a, b) => { + if (a < b) return -1; + if (a > b) return 1; + return 0; + }); + + const maj = majDefault.value; + + // if (maj.jg) { + // if (maj.major) majMajorToJG(); + // let yearList = option[maj.jg][maj.major] || []; + // yearList = Object.keys(yearList).sort((a, b) => b - a); + + // majMajorList.value = majorList; + // majYearList.value = yearList; + // } else { + let jgList = Object.keys(option); + + let yearList = option[jgList[0]][majorList[0]] || []; + yearList = Object.keys(yearList).sort((a, b) => b - a); + + majMajorList.value = majorList; + majYearList.value = yearList; + majJgList.value = jgList; + // } + }; + + const majMajorToJG = () => { + const option = majOption.value; + let maj = JSON.parse(JSON.stringify(majChoose.value)); + let major = maj.major; + + let jgList = []; + + for (const key in option) { + if (Object.hasOwnProperty.call(option, key)) { + const element = option[key]; + for (const k in element) { + if (k == major) jgList.push(key); + } + } + } + + let jg = maj.jg; + if (!jgList.includes(jg)) jg = ""; + + majJgList.value = jgList; + majChoose.value.jg = jg; + majMajorToYear(); + }; + + // 计算出对象所有 一级 key + const objectOne = (obj) => { + let arr = []; + for (const key in obj) { + arr.push(key); + } + return arr; + }; + + // 计算出 对象 所有 二级 key 不重复 + const collectYears = (obj, arr = new Set()) => { + for (let key in obj) { + if (obj.hasOwnProperty(key)) { + if (!isNaN(key) && key.length === 4) arr.add(key); + const value = obj[key]; + if (typeof value === "object" && value !== null) collectYears(value, arr); + } + } + return [...arr]; + }; + + // 切换查看类型 + const cutClassify = (type) => { + if (type == classifySelect.value) return; + classifySelect.value = type; + }; + + let compAllList = []; + // 获取 综合排名 数据 + const getSynthesizeData = () => { + let comChooseObj = comChoose.value || {}; + let comDefaultObj = comDefault.value || {}; + $ajaxget("https://api.gter.net/v1/program/comprehensiverankings", { + token: comChooseObj["token"] || comDefaultObj["token"], + ishongkong: comOnly.value || 0, + limit: 2000, + }).then((res) => { + if (res.code != 200) return; + let data = res.data || {}; + let list = data.data || []; + + // 遍历去掉非香港院校 + list.forEach((element) => { + if (!universityArr.includes(element.sid)) element.sid = 0; + }); + + compAllList = list; + comTotal.value = data.count; + comList.value = []; + comPage.value = 1; + renderComprehensiveList(); + + if (comChooseObj["token"]) comText.value = JSON.parse(JSON.stringify(comChooseObj)); + else comText.value = JSON.parse(JSON.stringify(comDefaultObj)); + + comDefault.value = {}; + + classify.value = "school"; + }); + }; + + // 渲染 综合排名 limit: 20 + const renderComprehensiveList = () => { + const limit = 20; + let page = comPage.value; + if (page == 0) return; + const allList = compAllList; + const target = allList.slice((page - 1) * limit, page * limit); + comPage.value = target.length < limit ? 0 : page + 1; + comList.value = comList.value.concat(target); + }; + + const handleScroll = () => { + const scrollHeight = document.documentElement.scrollHeight; + const clientHeight = document.documentElement.clientHeight; + const scrollTop = window.pageYOffset; + if (scrollTop + clientHeight >= scrollHeight - 50) renderComprehensiveList(); + }; + + const sortedYears = computed(() => { + const years = Object.keys(comOption.value[comChoose.value.jg] || {}); + return years.reverse(); // 反转数组顺序 + }); + + const selectComJg = (value) => { + comChoose.value.jg = value; + const option = comOption.value; + const yearList = Object.keys(option[value] || {}); + yearList.sort((a, b) => b - a); + + const com = comChoose.value; + let year = com.year; + if (!yearList.includes(year)) year = ""; + comChoose.value.year = year; + + comYearList.value = yearList; + }; + + const selectComYear = (value) => { + comChoose.value.year = value; + }; + + let majorState = ref(false); // 专业选择状态 + // 切换专业选择状态 + const cutMajorState = () => (majorState.value = !majorState.value); + + // 选择专业 + const selectMajMajor = (value) => { + const maj = majChoose.value; + majChoose.value.major = value; + cutMajorState(); + majMajorToJG(); + if (value && maj.jg) majMajorToYear(); + }; + + // 专业 选择 机构 + const selectMajJg = (jg) => { + const maj = JSON.parse(JSON.stringify(majChoose.value)); + majChoose.value.jg = jg; + + if (maj.major && jg) majMajorToYear(); + }; + + const majMajorToYear = () => { + const maj = majChoose.value; + const mOption = JSON.parse(JSON.stringify(majOption.value)); + let yearList = []; + let year = maj.year; + + if (maj.major && !maj.jg) { + for (const key in mOption) { + const element = mOption[key]; + for (const key in element) { + const ele = element[key]; + if (key == maj.major) yearList = [...yearList, ...Object.keys(ele)]; + } + } + yearList = [...new Set(yearList)].sort((a, b) => b - a); + } else { + const option = mOption[maj.jg][maj.major] || {}; + yearList = Object.keys(option).sort((a, b) => b - a) || []; + } + + if (!yearList.includes(year)) year = ""; + majYearList.value = yearList; + majChoose.value.year = year; + }; + + const selectMajYear = (value) => { + majChoose.value.year = value; + }; + + const haveChosen = (type) => { + if (type == "school") { + const option = comOption.value; + let com = JSON.parse(JSON.stringify(comChoose.value)); + + let text = ""; + if (!com.jg) text = "请选择机构"; + else if (!com.year) text = "请选择年份"; + + if (text) return; + comChoose.value["token"] = option[com.jg][com.year]; + getSynthesizeData(); + } else { + const option = majOption.value; + const maj = JSON.parse(JSON.stringify(majChoose.value)); + let text = ""; + if (!maj.major) text = "请选择专业"; + else if (!maj.jg) text = "请选择机构"; + else if (!maj.year) text = "请选择年份"; + + if (text) return; + + majChoose["token"] = option[maj.jg][maj.major][maj.year]; + getMajorData(); + } + }; + + let comJgList = ref([]); + let comYearList = ref([]); + + const initCom = () => { + const com = comOption.value; + let jgList = Object.keys(com); + comJgList.value = jgList; + let yearList = []; + + for (const key in com) { + const element = com[key]; + yearList = [...yearList, ...Object.keys(element)]; + } + comYearList.value = [...new Set(yearList)].sort((a, b) => b - a); + }; + + let majAllList = []; + const getMajorData = () => { + let majChooseObj = majChoose.value || {}; + let majDefaultObj = majDefault.value || {}; + $ajaxget("https://api.gter.net/v1/program/disciplinerankings", { + token: majChooseObj["token"] || majDefaultObj["token"], + ishongkong: majOnly.value || 0, + }).then((res) => { + if (res.code != 200) return; + let data = res.data; + let list = data.data || []; + + majAllList = list; + majTotal.value = data.count; + majList.value = []; + majPage.value = 1; + renderDisciplineList(); + + if (comChooseObj["token"]) comText.value = JSON.parse(JSON.stringify(comChooseObj)); + else comText.value = JSON.parse(JSON.stringify(comDefaultObj)); + + classify.value = "subject"; + }); + }; + + const renderDisciplineList = () => { + const limit = 20; + let page = majPage.value; + if (page == 0) return; + + const allList = majAllList; + const target = allList.slice((page - 1) * limit, page * limit); + majPage.value = target.length < limit ? 0 : page + 1; + majList.value = majList.value.concat(target); + }; + + return { majList, majOnly, comOnly, comYearList, comJgList, haveChosen, selectMajYear, selectMajJg, majYearList, majJgList, majMajorList, selectMajMajor, majOption, majChoose, cutMajorState, majorState, selectComJg, selectComYear, sortedYears, classify, classifySelect, cutClassify, rankingKeyObj, comText, comChoose, comList, comTotal, comOption }; }, }); projectIndex.mount("#app"); diff --git a/js/common.js b/js/common.js index a97ad8a..5117ad1 100644 --- a/js/common.js +++ b/js/common.js @@ -63,7 +63,7 @@ function $ajaxget(url, data) { resolve(data) }) .catch(error => { - console.log("resolve", resolve) + // console.log("resolve", resolve) // if (error.response?.status == 401) openShowWindow() resolve("")