no message

This commit is contained in:
DESKTOP-RQ919RC\Pc 2025-03-14 18:56:05 +08:00
parent 5e356892b4
commit 911f31668f
4 changed files with 714 additions and 22 deletions

View File

@ -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;
}

View File

@ -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;
}
}
}
}

View File

@ -26,8 +26,8 @@
</div>
<div class="content flexflex">
<div class="content-left flex1">
<!-- <div class="pitch-title">QS世界综合排名2025</div> -->
<div class="major-pitch flexcenter">
<div v-if="classify == 'school'" class="pitch-title">{{ rankingKeyObj[comText.jg] || comText.jg }}世界综合排名({{ comText.year }}年)</div>
<div v-else class="major-pitch flexcenter">
<span class="organ">QS</span>
<img class="icon" src="/img/arrows-circle-green.svg" />
世界综合排名2025
@ -35,7 +35,7 @@
<div class="message flexacenter">
<div class="total flexacenter">
<div class="sum">{{ 000 }}</div>
<div class="sum">{{ comTotal }}</div>
个排名
</div>
<div class="only flexacenter" bind:tap="cutOnlyXg">
@ -45,24 +45,24 @@
</div>
</div>
<div class="list">
<div class="item flexflex" v-for="item in 2">
<div class="num">1</div>
<div class="item flexflex" v-for="(item,index) in classify == 'school' ? comList : majList">
<div class="num">{{ item.rank }}</div>
<div class="info flex1">
<div class="name">麻省理工学院MIT</div>
<div class="name-en">Massachusetts Institute of Technology (MIT)</div>
<div class="city">美国剑桥</div>
<div class="name">{{ item.name || item.subject }}</div>
<div class="name-en">{{ item.enname }} {{ item.simple ? `${ item.simple }` : ''}}</div>
<div class="city">{{ item.city || '' }}</div>
</div>
<div class="right">
<div class="grade"><span class="sum">100</span></div>
<div class="btn flexcenter">
<div class="right flexflex">
<div class="grade" v-if="item.total_score"><span class="sum">{{ item.total_score }}</span></div>
<a class="btn flexcenter" v-if="item.sid" target="_blank" :href="'/college/' + item.sid">
学校主页
<img class="icon" src="/img/arrows-circle.svg" />
</div>
</a>
</div>
</div>
</div>
<div class="upglide flexcenter">- End -</div>
<div class="upglide flexcenter" v-if="(classify == 'school' && comPage == 0) || (classify == 'subject' && majPage == 0)">- End -</div>
</div>
<div class="screen-box">
<div class="head">
@ -72,12 +72,77 @@
<div class="screen-item">
<div class="title flexacenter">
<div class="dot"></div>
学校类型
榜单分类
</div>
<div class="list">
<div class="item">学校排名</div>
<div class="list flexflex">
<div class="item" :class="{'pitch': classifySelect == 'school'}" @click="cutClassify('school')">学校排名</div>
<div class="item" :class="{'pitch': classifySelect == 'subject'}" @click="cutClassify('subject')">专业排名</div>
</div>
</div>
<template v-if="classifySelect == 'school'">
<div class="screen-item">
<div class="title flexacenter">
<div class="dot"></div>
评榜机构
</div>
<div class="list flexflex">
<div class="item" :class="{'pitch': comChoose.jg == item}" v-for="(item,key) in comJgList" @click="selectComJg(item)">{{ item }}</div>
</div>
</div>
<div class="screen-item">
<div class="title flexacenter">
<div class="dot"></div>
年份
</div>
<div class="list flexflex">
<div class="item" :class="{'pitch': comChoose.year == item}" v-for="item in comYearList" @click="selectComYear(item)">{{ item }}</div>
</div>
</div>
<div class="btn-box">
<div class="btn" @click="haveChosen('school')">确定</div>
</div>
</template>
<template v-else>
<div class="screen-item major">
<div class="title flexacenter">
<div class="dot"></div>
专业
</div>
<div class="major-box">
<div class="major-text flexacenter" :class="{'pitch': majorState,'have': majChoose.major}" @click="cutMajorState">
<div class="text one-line-display">{{ majChoose.major || '请选择' }}</div>
<img class="icon" src="/img/arrows-black.svg" />
</div>
<div class="major-list" v-if="majorState">
<div class="major-list-mask" @click="cutMajorState"></div>
<div class="major-list-list">
<div class="major-item" :class="{'pitch': item == majChoose.major}" v-for="(item, key) in majMajorList" @click="selectMajMajor(item)">{{ item }}</div>
</div>
</div>
</div>
</div>
<div class="screen-item">
<div class="title flexacenter">
<div class="dot"></div>
评榜机构
</div>
<div class="list flexflex">
<div class="item" :class="{'pitch': majChoose.jg == item}" v-for="(item,key) in majJgList" @click="selectMajJg(item)">{{ item }}</div>
</div>
</div>
<div class="screen-item">
<div class="title flexacenter">
<div class="dot"></div>
年份
</div>
<div class="list flexflex">
<div class="item" :class="{'pitch': majChoose.year == item}" v-for="item in majYearList" @click="selectMajYear(item)">{{ item }}</div>
</div>
</div>
<div class="btn-box">
<div class="btn" @click="haveChosen('subject')">确定</div>
</div>
</template>
</div>
</div>
</div>
@ -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");

View File

@ -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("")