min-project/pages/projectDetails/projectDetails.js
2025-01-21 19:05:54 +08:00

805 lines
24 KiB
JavaScript

// pages/projectDetails/projectDetails.js
var miucms = require('../../utils/miucms.js');
let app = getApp()
const util = require('../../utils/util')
const common = require('../../utils/commonMethod')
Page({
/**
* 页面的初始数据
*/
data: {
informationState: false, // 授权后可能需要弹出完成信息框 个人背景那些
islogin: false,
isloginBtnState: false,
isFirstPattern: true,
screen_data: {},
totalTopHeight: 82,
admissionCurrent: 0, // 招生官 轮播图的下标
admissionState: false, // 招生官 弹窗的状态
stateState: false, // 底部状态显示状态
headHeight: 0, // 头部 高度
rpx150: 150,
index: 0,
current: {},
headerObj: {
ranking: "专业排名",
brief: "简介",
},
info: {},
remark: "", // 备注
keyboardHeight: 0, // 键盘高度
remarkInput: "", // 备注输入文本
remarkFocus: false, // 备注输入框焦点状态
isadmission: 0, // 是否是招生官项目
urls: [], // 招生官项目
uniqid: "",
info: {},
side: {
pivotal: "关键信息",
basic: "基本信息",
apply: "申请信息",
attend: "就读信息",
graduate: "毕业&就业",
answers: "招生官问答",
consult: "录取参考",
issue: "常见问题",
links: "相关链接",
},
sideKey: "pivotal", // 侧边栏选中 key
course: {}, // 课程
scrollTop: 0,
contras: {}, //
stateObj: {
0: "待定",
1: "主申",
2: "冲刺",
3: "保底",
},
rankingsObj: {}, // 排名对象
studyMode: 'ft', // 学习模式显示状态 ft 全日制 pt 兼读制
moldObj: {
1: "直播",
2: "回放",
3: "答疑"
},
isInitFinish: false,
sideFixed: false,
rpx30: 30,
user: {},
offerList: [],
sideNum: {},
answerType: 0, // 招生管显示类型 0不显示 1长期答疑 2即将开始
countDown: {
days: 0,
hours: 0,
minutes: 0,
seconds: 0,
},
admissionofficerin: [],
answerquestions: [],
quickAnswerState: false,
},
/**
* 生命周期函数--监听页面加载
*/
options: {},
onLoad(options) {
this.options = options
miucms.pageStart(app).then(() => {
const screen_data = app.globalData.screen_data || {}
this.setData({
screen_data,
totalTopHeight: screen_data.totalTopHeight,
islogin: app.globalData.user.uid > 0 ? true : false,
uniqid: options.uniqid,
rpx150: util.rpxTopx(150),
rpx30: util.rpxTopx(30),
user: app.globalData.user,
})
this.windowHeight = screen_data.windowHeight || 812
common.xgBasicData(this, app).then(data => {
this.setData({
rankingsObj: data.rankings,
})
this.getData()
})
})
},
getData() {
wx.showLoading({
title: '加载中...',
})
util.wxpost("/api/project.detail", {
uniqid: this.data.uniqid,
query: {
...this.options
},
}).then(res => {
const data = res.data
let admissionofficerin = data.admissionofficerin || []
// console.log("data", data);
// admissionofficerin[0]['date'] = "2025-01-21 19:00"
if (admissionofficerin.length > 0) {
admissionofficerin.forEach(element => {
const date = new Date(element.date);
// let answerType = 1
if (!isNaN(date.getTime())) {
if (Date.now() < date.getTime()) {
this.startCountdown(date.getTime() - Date.now() || 0)
// answerType = 2
this.setData({
answerType: 2,
})
}
}
// this.setData({
// answerType,
// })
})
}
let answerquestions = data.answerquestions || []
common.decodeKey(data.info).then(res => {
// console.log("res", res);
data.info = res
let course = {
required: [],
requiredCount: 0,
elective: [],
electiveCount: 0,
}
const info = data.info || {}
const fields = ['tuition_fee', 'tuition_fee_per_credit', 'application_fee', 'admission_deposit'];
fields.forEach(field => {
const textKey = `${field}_text`;
info[textKey] = common.formatNumberWithSpaces(info[field] || '');
});
const curriculum = info.curriculum || []
if (info.language_of_instruction) {
let strOutput = info.language_of_instruction.join(',');
info['language_of_instruction_text'] = strOutput
}
curriculum.forEach(element => {
if (element.credit == 'N/A') element.credit = 0
element.type === '必修课' ? (course.required.push(element), course.requiredCount += element.credit) : (course.elective.push(element), course.electiveCount += element.credit);
})
let contras = data.contras
if (Array.isArray(contras)) contras = {}
const remark = contras.remarks || ''
// 算出最后申请时间
info['application_end'] = this.calculateApplicaDeadline(info.nonlocal_application_end || {})
// 算出面试轮时间
info['interviewRounds'] = this.calculateInterviewRound(info.nonlocal_application_end || {})
if (typeof info.mode_of_study == "string") info.mode_of_study = JSON.parse(info.mode_of_study)
let side = this.data.side
// 判断是否常见问题 ,没有则删除左侧
if (!info.faq || info.faq.length == 0) delete side.issue
// 判断 毕业就业 没有则删除左侧
if (!info.award_zh && !info.graduation_requirements && !info.domains && !info.employers && !info.positions) delete side.graduate
// 判断奖学金文案
if (info.scholarship) info['scholarshipText'] = this.JudgmentScholarshipText(info.scholarship)
if (info.leaflet_url) {
const leaflet_url = decodeURIComponent(info.leaflet_url)
const urlWithoutParams = leaflet_url.split('?')[0];
const urlParts = urlWithoutParams.split('/');
const fileName = urlParts[urlParts.length - 1];
info['leaflet_name'] = fileName
}
const isadmission = info.admissionsproject || 0
if (isadmission == 1) this.getAdmissionList()
const date = new Date()
const month = date.getMonth() + 1
const year = date.getFullYear()
const semester = info.semester || {}
// if (month > semester.month && year + 1 <= semester.year) info['semesterState'] = true
if ((year < semester.year) || (year === semester.year && month < semester.month)) info['semesterState'] = true
let scores = info.language_proficiency_scores || []
const scoresList = scores
.map(element => {
let text = ""
if (["GMAT", "GMAT Focus Edition"].includes(element.name_zh)) text = `Verbal Reasoning ${element.verbal_reasoning} 分以上`
else if (["IELTS Academic", "TOEFL-iBT", "TOEFL-pBT"].includes(element.name_en)) {
const fields = {
total: "总分",
reading: "阅读",
speaking: "口语",
writing: "写作",
listening: "听力",
}
let scores = [element.reading, element.speaking, element.writing, element.listening]
if (scores.length == 4 && scores.every(score => score !== undefined && score > 0 && score === scores[0])) text = `总分 ${element.total} 分以上,各项分数不低于 ${scores[0]}`
else {
for (const [key, label] of Object.entries(fields)) {
if (element[key]) text += `${label} ${element[key]} 分以上、`
}
if (text.endsWith("、")) text = text.slice(0, -1)
}
} else if (element.total && element.total == 'Pass') text = `等级 ${element.total}`
else if (element.total && /^[A-Za-z]+$/.test(element.total)) text = `等级 ${element.total} 以上`
else if (element.total) text = `总分 ${element.total} 分以上`
return text ? {
name: element.name_zh,
text
} : null
})
.filter(item => item !== null)
info["scoresList"] = scoresList
this.getOfferData(info.id)
// 判断相关链接
if (!info.leaflet_url && !info.program_url && !info.catalog_url) delete side.links
if (info.rankings.length == 0 && !info.intro && !info.accreditation) delete side.basic
if (answerquestions.length == 0) delete side.answers
this.setData({
info,
course,
contras,
remark,
side,
isadmission,
sideKey: "pivotal", // pivotal
isInitFinish: true,
admissionofficerin,
answerquestions,
answerintroduction: data.answerintroduction,
}, () => {
setTimeout(() => {
this.getHeadHeight()
this.getIndexHeight()
}, 500)
})
}).catch(err => common.toast("出错了,请联系管理员。"))
}).finally(() => {
wx.hideLoading()
})
},
countdownInterval: null,
startCountdown(duration) {
clearTimeout(this.countdownInterval);
let timer = duration;
const days = Math.floor(timer / (24 * 60 * 60 * 1000));
const hours = Math.floor((timer % (24 * 60 * 60 * 1000)) / (60 * 60 * 1000));
const minutes = Math.floor((timer % (60 * 60 * 1000)) / (60 * 1000));
const seconds = Math.floor(timer % (60 * 1000) / 1000);
this.setData({
countDown: {
days,
hours: hours.toString().padStart(2, '0'),
minutes: minutes.toString().padStart(2, '0'),
seconds: seconds.toString().padStart(2, '0'),
},
countDownState: true,
});
timer = timer - 1000
if (timer > 0) this.countdownInterval = setTimeout(() => this.startCountdown(timer), 1000)
},
// 判断奖学金文案
JudgmentScholarshipText(obj) {
let text = ""
if (obj.local && obj.nonlocal) text = '均有'
else if (!obj.local && !obj.nonlocal) text = '均无'
else if (obj.local && !obj.nonlocal) text = '非本地学生无'
else if (!obj.local && obj.nonlocal) text = '非本地学生有'
return text
},
// 计算出外地申请截止时间
calculateApplicaDeadline(obj) {
// 初始化变量来存储最大时间点的属性和日期
let maxDate = null;
// 遍历对象的属性
for (const item in obj) {
// 如果当前日期是最大日期或是第一个日期,则更新最大日期和属性
if (maxDate === null || obj[item] > maxDate) maxDate = obj[item];
}
return maxDate
},
// 计算出面试轮的数组
calculateInterviewRound(obj) {
let rounds = [];
const chineseNumbers = ["一", "二", "三", "四", "五", "六", "七", "八", "九", "十"];
const formatTime = (time, index) => {
return {
text: `${chineseNumbers[index]}`,
time,
};
};
Object.keys(obj).forEach((key, index) => {
rounds.push(formatTime(obj[key], index));
});
if (rounds.length == 0) rounds = [{}]
return rounds || [{}]
},
// 切换招生官 轮播图状态
cutAdmission() {
this.setData({
admissionState: !this.data.admissionState
})
},
// 招生官 轮播图 修改状态
admissionChange(e) {
this.setData({
admissionCurrent: e.detail.current
})
},
// 获取index的高度
getIndexHeight() {
const query = wx.createSelectorQuery();
query.select('.head-box').boundingClientRect(rect => {
this.setData({
headHeight: rect.height,
})
if (!this.indexSidebar) this.indexSidebar = this.selectComponent('#index-sidebar')
}).exec();
},
// 获取头部 高度
sideHeight: {},
getHeadHeight() {
const query = wx.createSelectorQuery();
query.selectAll('.details-box .side-item').boundingClientRect(rect => {
if (!rect) return
let sideHeight = {}
rect.forEach(element => {
const type = element.dataset.type
sideHeight[type] = element.top
})
this.sideHeight = sideHeight
}).exec();
},
onPageScroll(e) {
if (Math.random() > 0.5) return
const scrollTop = e.scrollTop
const sideHeight = this.sideHeight
const sideHeightList = Object.keys(sideHeight) || []
if (sideHeightList.length == 0) return
let closestValue = sideHeightList.reduce((a, b) => Math.abs(sideHeight[b] - scrollTop) < Math.abs(sideHeight[a] - scrollTop) ? b : a);
const sideKey = closestValue || 'pivotal'
if (sideKey != this.data.sideKey) {
this.setData({
sideKey,
})
}
const headHeight = this.data.headHeight
const totalTopHeight = this.data.totalTopHeight
let sideFixed = false
if (scrollTop > headHeight - totalTopHeight - 15) sideFixed = true
if (sideFixed != this.data.sideFixed) {
this.setData({
sideFixed,
})
}
let sidebarState = this.indexSidebar.data.sidebarState
if (scrollTop > this.windowHeight * 3 && sidebarState !== 3) sidebarState = 3
if (scrollTop < this.windowHeight * 3 && sidebarState == 3) sidebarState = 2
// 同一搜集 修改的 sidebarState
if (sidebarState !== this.indexSidebar.data.sidebarState) {
this.indexSidebar.setData({
sidebarState
})
}
this.indexSidebar.openSidebarTwoHide()
},
// 点击复制
copy(e) {
const text = e.currentTarget.dataset.text
util.copy(text, '复制成功,浏览器打开')
},
// 打开文件
openFile(e) {
const url = e.currentTarget.dataset.url
common.goPage("/pages/webview/webview?url=" + encodeURIComponent(url))
},
// 打开 授权按钮
openLoginBtnState() {
this.setData({
isloginBtnState: true,
})
},
// 关闭授权登录事件
popClose() {
this.setData({
isloginBtnState: !this.data.isloginBtnState
})
},
userClickLogin(e) {
let data = e.detail.data
this.setData({
islogin: true,
isloginBtnState: false,
informationState: data.regdatastep == 'success' ? false : true,
})
this.onLoad(this.options)
},
// 子组件传值 修改 完善信息组件的状态
revampInformationState() {
this.setData({
informationState: false
})
},
// 打开状态
cutState() {
if (!this.data.islogin) {
this.openLoginBtnState()
return
}
this.setData({
stateState: !this.data.stateState,
})
},
//监听键盘
bindkeyboardheightchange(e) {
let keyboardHeight = e.detail.height || 0;
this.setData({
keyboardHeight,
});
},
// 监听输入 失去焦点
bindblur() {
this.setData({
keyboardHeight: 0,
});
},
// 打开备注弹窗
openRemark() {
if (!this.data.islogin) {
this.openLoginBtnState()
return
}
this.setData({
remarkState: true,
remarkFocus: false,
remarkInput: this.data.remark,
}, () => {
this.setData({
remarkFocus: true,
})
})
},
// 关闭备注
closeRemark() {
this.setData({
remarkState: false,
})
},
// 确定备注
confirmRemark() {
const contras = this.data.contras
util.wxpost("/api/project.user/remarks", {
token: contras.token,
remarks: this.data.remarkInput,
}).then(res => {
common.toast(res.message)
this.setData({
remark: this.data.remarkInput,
})
this.closeRemark()
})
},
// 点击 跳转 公共方法
goPage(e) {
const url = e.currentTarget.dataset.url
common.goPage(url)
},
goMyProject() {
if (!this.data.islogin) {
this.openLoginBtnState()
return
}
common.goPage("/pages/projectMy/projectMy?classify=manage")
},
handSide(e) {
const sideKey = e.currentTarget.dataset.key
const sideHeight = this.sideHeight
wx.pageScrollTo({
scrollTop: sideHeight[sideKey] - this.data.totalTopHeight || 0,
})
},
indexSidebar: null,
windowHeight: 812, // 屏幕高度
// 修改 项目 状态
changeType(e) {
const typeid = e.currentTarget.dataset.typeid
const contras = this.data.contras
util.wxpost("/api/project.user/changeType", {
token: contras.token || '',
typeid,
}).then(res => {
if (res.code != 200) return
common.toast(res.message)
contras['typeid'] = typeid
this.cutState()
this.setData({
contras,
})
})
},
delete() {
const contras = this.data.contras
util.wxpost("/api/project.user/delete", {
token: contras.token,
}).then(res => {
if (res.code != 200) return
common.toast(res.message)
contras['ismanage'] = 0
this.setData({
contras,
stateState: false,
})
})
},
// 点击加入对比单
addComparison() {
if (!this.data.islogin) {
this.openLoginBtnState()
return
}
const contras = this.data.contras
const info = this.data.info
util.wxpost("/api/project.contrast/add", {
projectid: info.id,
}).then(res => {
if (res.code != 200) return
const data = res.data
common.toast(res.message)
contras['status'] = 1
contras['ismanage'] = 1
contras['typeid'] = 0 // 默认是待定
contras['token'] = data.token
this.setData({
contras,
})
app.globalData.basicData['contrastcount'] = data.count
}).catch(err => {
if (err.code == 401) this.openLoginBtnState()
})
},
cutStudyMode(e) {
const type = e.currentTarget.dataset.type
this.setData({
studyMode: type,
})
},
// 获取招生官
getAdmissionList() {
util.wxget('/miniprogramApi/offer/home').then(res => {
if (res.code != 200) return
const data = res.data
let admissionsOfficer = data.admissionsOfficer || []
let urls = []
admissionsOfficer.forEach(element => {
let mold = null
if (element.date == null || element.date.indexOf("答疑") >= 0) mold = 3
else mold = this.isToday(element.date) ? 1 : 2
element.urls.forEach(ele => {
urls.push({
...ele,
mold,
logo: element['logo'],
})
})
})
const urlsOne = urls[0] || {}
const chunkedArray = [];
for (let i = 0; i < urls.length; i += 4) {
chunkedArray.push(urls.slice(i, i + 4));
}
this.setData({
urls: chunkedArray,
urlsOne,
})
})
},
isToday(dateString) {
const now = new Date().getTime()
const date = new Date(dateString).getTime()
return now <= date
},
getOfferData(projectid) {
util.wxget("/api/project.other/offerList?limit=2000&projectid=" + projectid, ).then(res => {
const data = res.data
const list = data.list || []
let side = this.data.side
if (list.length == 0) delete side.consult
list.forEach(element => {
element["timestamp"] = util.strtimeago(element["timestamp"], 3)
})
let sideNum = this.data.sideNum
sideNum['consult'] = data.count
this.setData({
offerList: list,
side,
sideNum,
})
})
},
openPreview() {
wx.previewMedia({
sources: [{
url: '//framework.x-php.com/project//img/9dfc-c89f50deb0f906dd751540895bf0e303.jpg'
}],
showmenu: true,
})
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
wx.stopPullDownRefresh()
this.getData()
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
wx.stopPullDownRefresh()
this.getData()
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
return {
// title: "【寄托港校项目库】 - " + this.data.info.name_zh,
title: `${ this.data.info.schoolalias || this.data.info.schoolname || '' }】 - ${this.data.info.name_zh}`,
}
},
onShareTimeline() {
util.statistics({
name: "share-timeline"
})
return {
title: `${ this.data.info.schoolalias || this.data.info.schoolname || '' }】 - ${this.data.info.name_zh}`,
}
},
cutQuickAnswer() {
this.setData({
quickAnswerState: !this.data.quickAnswerState
})
},
})