Convenient SZU

【使用前先看介绍/有问题可反馈】便捷深大 (Convenient SZU):适配深圳大学内部网多个网页的辅助脚本。内部网首页左上角增加 `宿舍用电查询/校园网络续费/登录(不可用)Dr.com/体育场馆预订/百度文库/下载专区/师资队伍` 入口以及增加绑定个人信息窗口,免去进入 `Blackboard/学业完成查询/办事大厅卡片` 的繁琐步骤,自动登录(不可用) `Blackboard/办事大厅/Dr.com/校园网络续费` 等页面,自动填写需要登陆的页面的账号密码,【办事大厅】页面增加【修读课程统计下载】,【网上评教】页面增加【一键五星+评价】,【成长记录】页面增加【学期专业排名】,【转专业】页面增加【转专业成绩显示】,【学业完成查询】页面增加【彩虹地毯】,【宿舍用电查询】可自动记忆填写的宿舍信息并可自动显示近 20 天用电记录。

当前为 2021-04-24 提交的版本,查看 最新版本

// ==UserScript==
// @name            Convenient SZU
// @namespace       http://tampermonkey.net/
// @version         1.1.4
// @description     【使用前先看介绍/有问题可反馈】便捷深大 (Convenient SZU):适配深圳大学内部网多个网页的辅助脚本。内部网首页左上角增加 `宿舍用电查询/校园网络续费/登录(不可用)Dr.com/体育场馆预订/百度文库/下载专区/师资队伍` 入口以及增加绑定个人信息窗口,免去进入 `Blackboard/学业完成查询/办事大厅卡片` 的繁琐步骤,自动登录(不可用) `Blackboard/办事大厅/Dr.com/校园网络续费` 等页面,自动填写需要登陆的页面的账号密码,【办事大厅】页面增加【修读课程统计下载】,【网上评教】页面增加【一键五星+评价】,【成长记录】页面增加【学期专业排名】,【转专业】页面增加【转专业成绩显示】,【学业完成查询】页面增加【彩虹地毯】,【宿舍用电查询】可自动记忆填写的宿舍信息并可自动显示近 20 天用电记录。
// @author          cc
// @match           https://elearning.szu.edu.cn/*
// @match           https://authserver.szu.edu.cn/*
// @match           https://drcom.szu.edu.cn/*
// @match           https://self.szu.edu.cn/*
// @match           https://www1.szu.edu.cn/*
// @match           http://ehall.szu.edu.cn/*
// @match           http://bkxk.szu.edu.cn/*
// @match           https://*.webvpn.szu.edu.cn/*
// @match           172.30.255.2/*
// @match           http://192.168.84.3:9090/cgcSims/*
// @grant           GM_setValue
// @grant           GM_getValue
// @require         https://cdn.bootcss.com/jquery/3.4.1/jquery.js
// @require         https://gf.qytechs.cn/scripts/418193-coder-utils.js
// @require         https://gf.qytechs.cn/scripts/422854-bubble-message.js
// @noframes
// ==/UserScript==

(function() {
  'use strict'
  var account = GM_getValue('account')
  var hasUpdatedInfo = false
  if (!account) {
    account = { cid: '', uid: '', pwd: '' }
    GM_setValue('account', account)
  } else {
    hasUpdatedInfo = account.cid && account.uid && account.pwd
  }
  function inform (msg) { console.log(`%c${msg}`, 'background-color: yellow; font-size: 16px;') }
  $(document).ready(function () {
    inform('Convenient SZU version 1.1.4')
		var bm = new BubbleMessage()
		if (location.host.match(/www1.*?\.szu\.edu\.cn/)) {
			if (location.href.endsWith('.szu.edu.cn/')) {
				let td = document.querySelector('table table table tbody tr td')
				let a_drcom_dom = document.getElementById('drcom_dom')
				if (td && !a_drcom_dom) {
					function setExtension () {
						td.querySelectorAll('img').forEach(e => e.remove())
						td.firstChild.remove()
						td.firstChild.remove()
						td.style.padding = '5px 20px'
						td.innerHTML += '<br>'
						let a
						a = HTMLElement.$mkel('a', {
							href: 'http://192.168.84.3:9090/cgcSims/',
							id: 'electricity',
						}, { innerHTML: '宿舍用电查询' })
						td.appendChild(a)
						a = HTMLElement.$mkel('a', {
							href: 'https://self.szu.edu.cn/self/',
							id: 'network',
						}, { innerHTML: '|校园网络续费' })
						td.appendChild(a)
						a = HTMLElement.$mkel('a', {
							href: 'http://172.30.255.2/0.htm',
							id: 'drcom_dom',
						}, { innerHTML: '|登录(不可用) Dr.com' })
						td.appendChild(a)
						td.innerHTML += '<br>'
						a = HTMLElement.$mkel('a', {
							href: 'http://ehall.szu.edu.cn/publicapp/sys/tycgyyxt/index.do',
							id: 'venue_yh',
						}, { innerHTML: '体育场馆预订(粤海校区)' })
						td.appendChild(a)
						a = HTMLElement.$mkel('a', {
							href: 'http://ehall.szu.edu.cn/publicappxl/sys/xlxqtycgyy/index.do',
							id: 'venue_lh',
						}, { innerHTML: '|体育场馆预订(丽湖校区)' })
						td.appendChild(a)
						td.innerHTML += '<br>'
						a = HTMLElement.$mkel('a', {
							href: 'http://www.lib.szu.edu.cn/zh-hans/er/baidu-wenku',
							id: 'wenku',
						}, { innerHTML: '百度文库' })
						td.appendChild(a)
						a = HTMLElement.$mkel('a', {
							href: 'https://www1.szu.edu.cn/nc/view.asp?id=64',
							id: 'download',
						}, { innerHTML: '|下载专区' })
						td.appendChild(a)
						a = HTMLElement.$mkel('a', {
							href: 'http://xzfc.szu.edu.cn/rsfw/sys/szdxxzfc/login/index.do',
							id: 'teacher',
						}, { innerHTML: '|师资队伍' })
						td.appendChild(a)
					}
					function setInput () {
						let next_tr = document.querySelector('tbody tbody tbody tr:nth-child(2)')
						let tr = HTMLElement.$mkel('tr', { id: 'inform' }, {}, {
							'display': 'flex',
							'flex-wrap': 'wrap',
							'justify-content': 'flex-start',
						})
						let uid = HTMLElement.$mkel('input', {
							id: 'uid',
							type: 'number',
							minlength: '10',
							maxlength: '10',
							placeholder: '请输入 10 位数学号',
						})
						let cid = HTMLElement.$mkel('input', {
							id: 'cid',
							type: 'number',
							minlength: '6',
							maxlength: '6',
							placeholder: '请输入 6 位数校园卡号',
						})
						let pwd = HTMLElement.$mkel('input', {
							id: 'pwd',
							type: 'password',
							placeholder: '请输入统一认证登录(不可用)密码',
						})
						let btn = HTMLElement.$mkel('button', {}, { innerHTML: '更新信息' }, {
							'cursor': 'pointer',
						}, {
							click: function () {
								let uid_val = document.getElementById('uid').value
								let cid_val = document.getElementById('cid').value
								let pwd_val = document.getElementById('pwd').value
								if (!uid_val.match(/^\d{10}$/)) {
									bm.message({
										type: 'warning',
										message: '学号必须为 10 位数',
										duration: 2000,
									})
									return false
								}
								if (!cid_val.match(/^\d{6}$/)) {
									bm.message({
										type: 'warning',
										message: '校园卡号必须为 6 位数',
										duration: 2000,
									})
									return false
								}
								if (!pwd_val) {
									bm.message({
										type: 'warning',
										message: '密码不能为空',
										duration: 2000,
									})
									return false
								}
								account.uid = uid_val
								account.cid = cid_val
								account.pwd = pwd_val
								GM_setValue('account', account)
								bm.message({
									type: 'success',
									message: '信息更新成功',
									duration: 2000,
								})
							}
						})
						if (account.uid) uid.value = account.uid
						if (account.cid) cid.value = account.cid
						if (account.pwd) pwd.value = account.pwd
						tr.appendChild(uid)
						tr.appendChild(cid)
						tr.appendChild(pwd)
						tr.appendChild(btn)
						next_tr.parentElement.insertBefore(tr, next_tr)
						let style = HTMLElement.$mkel('style', {}, {
							innerText: `
								#inform > * {
									width: 160px;
									margin-left: 20px;
									margin-top: 5px;
									padding: 0 5px;
								}
								input::-webkit-outer-spin-button,
								input::-webkit-inner-spin-button {
									-webkit-appearance: none !important;
									margin: 0;
								}
							`
						})
						document.head.appendChild(style)
						let win = document.querySelector('tbody tbody tr:last-child')
						let lst = win.lastElementChild.querySelector('tr:nth-child(2)')
						lst.style.height = `${lst.offsetHeight + tr.offsetHeight + 55}px`
					}
					setExtension()
					setInput()
				}
			} else if (location.href.indexOf('/board/infolist') >= 0) {
        function meets (_ct, _in) {
          for (let it of _in) if (_ct.match(it)) return true
          return false
        }
        function generateCheckbox (_id, _ct, _fn) {
          var checkbox = document.createElement('input')
          checkbox.id = _id
          checkbox.type = 'checkbox'
          checkbox.checked = false
          checkbox.style = 'margin-right: 5px;'
          checkbox.onchange = _fn
          var label = document.createElement('label')
          label.innerHTML = _ct
          label.setAttribute('for', _id)
          var container = document.createElement('span')
          container.style = 'font-size: 13px; display: inline-flex; align-items: center; margin-right: 10px; position: relative; top: 2px;'
          container.appendChild(checkbox)
          container.appendChild((label))
          return container
        }
        function setCheckbox () {
          var checkbox_show_only_college = generateCheckbox('show-only-college', '只看学院学部', function (event) {
            var _in = [/.*?学院.*/, /.*?学部.*/]
            var title_container = document.querySelectorAll('[valign=top]')[3]
            var articles = [...title_container.querySelectorAll('table>tbody>tr')].slice(2)
            var not_meets_articles = articles.filter(el => !meets(el.querySelector('td:nth-child(3)>a').innerText, _in))
            var not_meets_depts = [...document.querySelectorAll('select[name=from_username]>option')].filter(el => !meets(el.value, _in))
            if (event.target.checked) {
              not_meets_depts.forEach(el => el.style.display = 'none')
              not_meets_articles.forEach(el => el.style.display = 'none')
            } else {
              not_meets_depts.forEach(el => el.style.display = '')
              not_meets_articles.forEach(el => el.style.display = '')
            }
          })
          var next_el = document.querySelector('select[name=dayy]')
          var td = next_el.parentElement
          td.style.width = '500px'
          td.insertBefore(checkbox_show_only_college, next_el)
        }
				function updateSelect () {
					// 只看学院学部
					var show_el = document.querySelector('input#show-only-college')
					show_el.addEventListener('change', (event) => {
						account.boardShowOnlyChecked = event.target.checked
						GM_setValue('account', account)
					})
					if (typeof account.boardShowOnlyChecked !== 'boolean') {
						account.boardShowOnlyChecked = show_el.checked
						GM_setValue('account', account)
					} else if (show_el.checked !== account.boardShowOnlyChecked) {
						show_el.click()
					}
					// 发文时间
					var dayy_el = document.querySelector('select[name=dayy]')
					dayy_el.addEventListener('change', (event) => {
						account.boardDayySelectedIndex = event.target.selectedIndex
						GM_setValue('account', account)
					})
					if (typeof account.boardDayySelectedIndex !== 'number') {
						account.boardDayySelectedIndex = dayy_el.selectedIndex
						GM_setValue('account', account)
					} else if (dayy_el.selectedIndex !== account.boardDayySelectedIndex) {
						dayy_el.selectedIndex = account.boardDayySelectedIndex
					}
					// 发文单位
					var dept_el = document.querySelector('select[name=from_username]')
					dept_el.addEventListener('change', (event) => {
						account.boardDeptSelectedIndex = event.target.selectedIndex
						GM_setValue('account', account)
					})
					if (typeof account.boardDeptSelectedIndex !== 'number') {
						account.boardDeptSelectedIndex = dept_el.selectedIndex
						GM_setValue('account', account)
					} else if (dept_el.selectedIndex !== account.boardDeptSelectedIndex) {
						dept_el.selectedIndex = account.boardDeptSelectedIndex
					}
					// 搜索关键词
					var kw_el = document.querySelector('input[name=keyword]')
					kw_el.addEventListener('input', (event) => {
						account.boardKeywordValue = event.target.value
						GM_setValue('account', account)
					})
					if (typeof account.boardKeywordValue !== 'string') {
						account.boardKeywordValue = kw_el.getAttribute('value')
						GM_setValue('account', account)
					} else if (kw_el.getAttribute('value') !== account.boardKeywordValue) {
						kw_el.setAttribute('value', account.boardKeywordValue)
						kw_el.value = account.boardKeywordValue
					}
				}
        setCheckbox()
				updateSelect()
      }
		} else if (!hasUpdatedInfo) {
			return
		}
		if (location.host.indexOf('elearning') >= 0) {
			if (location.href.endsWith('.szu.edu.cn/') || location.href.endsWith('.szu.edu.cn/webapps/login/')) {
				let span = document.querySelector('table table table tr td a span')
				if (span) span.click()
			} else if (location.href.includes('webapps/portal/')) {
				let observer = document.body.$monitor({ childList: true }, (events) => {
					for (let event of events) {
						for (let node of event.addedNodes) {
							if (node instanceof HTMLElement && node.classList.contains('lb-wrapper')) {
								(function rec () {
									let agree_button = node.querySelector('#agree_button')
									if (agree_button) {
										agree_button.click()
										observer.disconnect()
									} else {
										setTimeout(rec, 50)
									}
								})()
							}
						}
					}
				})
			}
		} else if (location.host == 'authserver.szu.edu.cn') {
			let username_el = document.getElementById('username')
			let password_el = document.getElementById('password')
			let helper_el = $('.iCheck-helper')
			let captchaResponse_el = document.querySelector('p#cpatchaDiv #captchaResponse')
			let button_el = document.querySelector('button')
			if (username_el && password_el && button_el && helper_el.length && !captchaResponse_el) {
				username_el.value = account.cid
				password_el.value = account.pwd
				helper_el.click()
				button_el.click()
			}
		} else if (location.host == 'ehall.szu.edu.cn') {
			function setfunc () {
				function courseClassSorted (courses) {
					function getCourseClassPriority (course) {
						let priority = ['基本通识', '专业核心', '专业限选', '专业选修', '扩展通识', '自然科学', '生命科学', '社会科学', '中华文化', '人文艺术', '创新创业', '个性课程', '基本实践']
						for (let i = 0; i < priority.length; i++)
							if (course.indexOf(priority[i]) >= 0)
								return i
						return priority.length
					}
					let courseInfo = courses.map(c => {
						return {
							course: c,
							priority: getCourseClassPriority(c),
						}
					})
					return courseInfo.$sorted('priority').$map('course')
				}
				function exec () {
					$.ajax({
						method: 'POST',
						url: 'http://ehall.szu.edu.cn/jwapp/sys/xywccx/modules/xywccx/cxscfakz.do',
						data: {
							BYNJDM: '-',
						},
					}).then(res => {
						let extendedCourseClasses = ['自然科学类', '生命科学类', '社会科学类', '中华文化类', '人文艺术类', '创新创业类']
						let numChinese = ['一', '二', '三', '四', '五', '六', '七', '八']
						let courseClassesObj = res.datas.cxscfakz.rows
						courseClassesObj = courseClassesObj.$map(['KZM', 'KZH', 'PYFADM', 'YQXF', 'WCXF', 'YQMS', 'WCMS', 'YQWCKZS', 'WCKZS', 'FKZH'])
						let allCourseClasses = courseClassesObj.filter(c => c.FKZH !== '-1').$map('KZM')
						let statCourseClasses = allCourseClasses.filter(c => !extendedCourseClasses.includes(c))
						let extendedCourseClass = allCourseClasses.$where(c => c.indexOf('扩展通识') >= 0, true)
						let cmap = Object.fromEntries([[extendedCourseClass, courseClassesObj.$where(c => c.KZM.indexOf('扩展通识') >= 0, true)]])
						allCourseClasses = allCourseClasses.filter(c => c.indexOf('扩展通识') < 0)
						let recommendClasses = statCourseClasses.filter(c => c.indexOf('个性课程') < 0)
						courseClassesObj = courseClassesObj.filter(c => allCourseClasses.includes(c.KZM) && c.FKZH !== '-1')
						cmap = Object.assign(cmap, Object.fromEntries(courseClassesObj.map(c => [c.KZM, c])))
						let keys = ['课程类型', '要求学分', '已修学分', '要求门数', '已修门数', '要求类别数', '已修类别数']
						let progressContent = '课程类型,要求学分,已修学分,要求门数,已修门数,要求类别数,已修类别数\n'
						statCourseClasses = courseClassSorted(statCourseClasses)
						statCourseClasses.map(statCourseClass => {
							let c = cmap[statCourseClass]
							return {
								'课程类型': statCourseClass,
								'要求学分': c.YQXF,
								'已修学分': c.WCXF,
								'要求门数': c.YQMS === null ? '0' : String(c.YQMS),
								'已修门数': c.WCMS === null ? '0' : String(c.WCMS),
								'要求类别数': c.YQWCKZS === null ? '0' : String(c.YQWCKZS),
								'已修类别数': c.WCKZS === null ? '0' : String(c.WCKZS),
							}
						}).forEach(p => progressContent += keys.map(k => p[k]).join(',') + '\n')
						let reqs = []
						courseClassesObj.forEach(courseClass => {
							reqs.push($.ajax({
								method: 'POST',
								url: 'http://ehall.szu.edu.cn/jwapp/sys/xywccx/modules/xywccx/cxscfakzkc.do',
								data: {
									BYNJDM: '-',
									KZH: courseClass.KZH,
									PYFADM: courseClass.PYFADM,
									pageSize: 999,
									pageNumber: 1,
								},
							}).then(res => {
								let courseList = res.datas.cxscfakzkc.rows
								courseList = courseList.$map(['KCM', 'CJ', 'XF', 'SFTG_DISPLAY', 'XNXQDM', 'KCXZDM_DISPLAY', 'KCH', 'BZ'])
								courseList = courseList.$reindex({'KCM': '课程名', 'CJ': '成绩', 'XF': '学分', 'SFTG_DISPLAY': '是否通过', 'XNXQDM': '学年学期', 'KCXZDM_DISPLAY': '课程性质', 'BZ': '备注'})
								let clreqs = []
								courseList.forEach(course => {
									course['课程类型'] = courseClass.KZM
									course['成绩'] = course['成绩'] || ''
									course['学年学期'] = course['学年学期'] || ''
									course['备注'] = course['备注'] || ''
									if (course['备注'].match(/[^\d\s\:a-zA-z\-]/g) === null || course['备注'].match(/见.*?备注.*/) !== null) course['备注'] = ''
									clreqs.push($.ajax({
										method: 'POST',
										url: 'http://ehall.szu.edu.cn/jwapp/sys/qxfacx/modules/pyfacxepg/kzkccx.do',
										data: {
											KZH: courseClass.KZH,
											PYFADM: courseClass.PYFADM,
											KCH: course.KCH,
											pageSize: 1,
											pageNumber: 1,
										},
									}).then(res => {
										let recSem = ''
										if (res.datas.kzkccx.rows.length > 0) {
											let remark = res.datas.kzkccx.rows[0].BZ
											recSem = res.datas.kzkccx.rows[0].XDXQ || ''
											if (typeof remark === 'string' && remark.length > 0 && !course['备注'].length && (remark.match(/[^\d\s\:a-zA-z\-]/g) !== null && remark.match(/见.*?备注.*/) === null))  course['备注'] = remark
										}
										course['建议修读学期'] = recSem
										delete course.KCH
										return course
									}).fail(err => {
										console.error(err)
									}))
								})
								return Promise.all(clreqs).then(res => {
									return [courseClass.KZM, res]
								})
							}).fail(err => {
								console.error(err)
							}))
						})
						Promise.all(reqs).then(res => {
							let courses = Object.fromEntries(res)
							let gradeMap = {'A+': 4.5, 'A': 4.0, 'B+': 3.5, 'B': 3.0, 'C+': 2.5, 'C': 2.0, 'D': 1.0, 'F': 0.0}
							let semester = Array.from(new Set(Object.values(courses).$merge().$map('学年学期'))).filter(s => Boolean(s))
							semester.sort()
							let earliestYear = new Date().getFullYear()
							if (semester.length) earliestYear = Number(semester[0].slice(0, 4))
							let semesterIndexToDate = (year, semesterIndex) => {
								semesterIndex = Number(semesterIndex)
								let reccommendYear = year + ((semesterIndex - 1) >> 1)
								let reccommendGrade = 2 - (semesterIndex & 1)
								return `${reccommendYear}-${reccommendYear + 1}-${reccommendGrade}`
							}
							let semesterDateToChinese = (year, semesterDate) => {
								let allNums = semesterDate.match(/\d+/g)
								let yearIndex = Number(allNums[0]) - year
								let gradeChinese = `大${numChinese[yearIndex]}${['上', '下'][Number(allNums[2]) - 1]}`
								return `${semesterDate} (${gradeChinese})`
							}
							let semesterGrade = {}
							semester.forEach(s => semesterGrade[s] = {semester: s, allScore: 0, getScore: 0, avgScore: 0, acaScore: 0, acgScore: 0, acvScore: 0})
							Object.values(courses).$merge().forEach(course => {
								if (course['学年学期']) {
									semesterGrade[course['学年学期']]['allScore'] += course['学分']
									semesterGrade[course['学年学期']]['getScore'] += course['学分'] * gradeMap[course['成绩']]
								}
							})
							semesterGrade = Object.values(semesterGrade).$sorted('semester')
							for (let i = 0; i < semesterGrade.length; i++) {
								semesterGrade[i].avgScore = parseFloat(semesterGrade[i].getScore / semesterGrade[i].allScore).toFixed(2)
								semesterGrade[i].acaScore = semesterGrade[i].allScore
								semesterGrade[i].acgScore = semesterGrade[i].getScore
								for (let j = 0; j < i; j++) {
									semesterGrade[i].acaScore += semesterGrade[j].allScore
									semesterGrade[i].acgScore += semesterGrade[j].getScore
								}
								semesterGrade[i].acvScore = parseFloat(semesterGrade[i].acgScore / semesterGrade[i].acaScore).toFixed(2)
								semesterGrade[i].semester = semesterDateToChinese(earliestYear, semesterGrade[i].semester)
							}
							recommendClasses.forEach(recommendClass => {
								let courseClassesIndex = courseClassesObj.$where(x => x.KZM === recommendClass)
								if (courseClassesIndex >= 0) {
									courses[recommendClass].forEach(c => {
										let reccommendSemester = ''
										if (c['建议修读学期']) reccommendSemester = semesterIndexToDate(earliestYear, c['建议修读学期'])
										c['建议修读学期'] = reccommendSemester
									})
								}
							})
							let orderedCourses = []
							allCourseClasses = courseClassSorted(allCourseClasses)
							allCourseClasses.forEach(courseClass => {
								if (ut.type.$isIterable(courses[courseClass]) && courses[courseClass].length > 0) {
									courses[courseClass] = courses[courseClass].$sorted(['课程名', '建议修读学期', '学年学期', '是否通过'], [false, false, false, true])
									orderedCourses = orderedCourses.concat(courses[courseClass])
								}
							})
							let suggestCourses = []
							let notPassCourses = orderedCourses.filter(c => c['是否通过'] !== '通过')
							recommendClasses = courseClassSorted(recommendClasses)
							recommendClasses.forEach(recommendClass => {
								let tmpCourseList = notPassCourses.filter(c => c['课程类型'] === recommendClass)
								tmpCourseList = tmpCourseList.$sorted('建议修读学期')
								if (recommendClass === '专业选修课') {
									let tmpLimitCourseList = tmpCourseList.filter(c => c['备注'].indexOf('限选') >= 0)
									let tmpFreeCourseList = tmpCourseList.filter(c => c['备注'].indexOf('限选') < 0)
									tmpCourseList = tmpLimitCourseList.concat(tmpFreeCourseList)
								}
								suggestCourses = suggestCourses.concat(tmpCourseList)
							})
							orderedCourses.forEach(c => {
								if (c['学年学期']) c['学年学期'] = semesterDateToChinese(earliestYear, c['学年学期'])
								if (c['建议修读学期']) c['建议修读学期'] = semesterDateToChinese(earliestYear, c['建议修读学期'])
							})
							let gradeContent = '学年学期,学期学分,学期GPA,累计学分,累计GPA\n'
							for (let grade of semesterGrade) gradeContent += `${grade.semester},${grade.allScore},${grade.avgScore},${grade.acaScore},${grade.acvScore}\n`
							let courseContent = '课程名,学分,成绩,是否通过,学年学期,建议修读学期,课程类型,课程性质,备注\n'
							for (let course of orderedCourses) courseContent += `${course['课程名']},${course['学分']},${course['成绩']},${course['是否通过']},${course['学年学期']},${course['建议修读学期']},${course['课程类型']},${course['课程性质']},${course['备注']}\n`
							let suggestContent = '以下是根据数据自动生成的推荐修读课程,仅供参考\n'
							suggestContent += '课程名,学分,成绩,是否通过,学年学期,建议修读学期,课程类型,课程性质,备注\n'
							for (let course of suggestCourses) suggestContent += `${course['课程名']},${course['学分']},${course['成绩']},${course['是否通过']},${course['学年学期']},${course['建议修读学期']},${course['课程类型']},${course['课程性质']},${course['备注']}\n`
							let csvContent = `${courseContent}\n${gradeContent}\n${progressContent}\n${suggestContent}\n`
							csvContent.$toCsv('修读课程统计.csv')
							bm.message({
								type: 'success',
								message: '修读课程统计表格生成成功',
								duration: 2000,
							})
						})
					}).fail(err => {
						console.error(err)
					})
				}
				(function () {
					let ampDesktopNav = $('#ampDesktopNav')[0]
					if (!ampDesktopNav) return 
					function setTab () {
						let stuServeCenter = ampDesktopNav.firstElementChild
						let div = HTMLElement.$mkel('div', {
							id: 'download-training-program',
							class: stuServeCenter.className.replace(/\s?amp\-active/, ''),
							title: '修读课程统计下载',
						}, {innerHTML: '修读课程统计下载'}, {}, {
							click: function () {
								$.ajax({
									method: 'POST',
									url: 'http://ehall.szu.edu.cn/jwapp/sys/xywccx/modules/xywccx/cxscfakz.do',
									data: {
										BYNJDM: '-',
									},
								}).then(res => {
									let rows = res.datas.cxscfakz.rows
									let i = 0
									while (rows[i].KZM.indexOf('基本通识课') < 0 && i < rows.length) i++
									let courseClass = rows[i]
									$.ajax({
										method: 'POST',
										url: 'http://ehall.szu.edu.cn/jwapp/sys/qxfacx/modules/pyfacxepg/kzkccx.do',
										data: {
											KZH: courseClass.KZH,
											PYFADM: courseClass.PYFADM,
											pageSize: 10,
											pageNumber: 1,
											BYNJDM: '-',
										},
									}).then(res => {
										setTimeout(() => {
											$('#ampDesktopNav')[0].firstElementChild.click()
										}, 50)
										exec()
									}).fail(err => {
										console.log('fail (code: -2)')
										bm.message({
											type: 'info',
											message: `将跳转至"全校培养方案查询",跳转后请手动关闭打开的页面`,
											duration: 2000,
										})
										setTimeout(() => {
											let btn = $('[amp-title=全校培养方案查询]')[0]
											btn.setAttribute('amp-unviewabledescription', 'true')
											btn.click()
											setTimeout(() => {
												div.click()
											}, 50)
										}, 3000)
									})
								}).fail(err => {
									console.log('fail (code: -1)')
									bm.message({
										type: 'info',
										message: `将跳转至"学业完成查询",跳转后请手动关闭打开的页面`,
										duration: 2000,
									})
									setTimeout(() => {
										let btn = $('.card-bus-content [amp-title=学业完成查询]')[0]
										btn.setAttribute('amp-unviewabledescription', 'true')
										btn.click()
										setTimeout(() => {
											div.click()
										}, 50)
									}, 3000)
								})
							},
						})
						stuServeCenter.$before(div, -1)
					}
					(function rec () {
						let child = ampDesktopNav.firstElementChild
						if (child) {
							setTab()
						} else {
							setTimeout(rec, 50)
						}
					})()
				})()
			}
			let ampHasNoLogin = document.getElementById('ampHasNoLogin')
			if (ampHasNoLogin && sessionStorage.ampUserId === 'guest') {
				ampHasNoLogin.click()
				setfunc()
			}
			if (sessionStorage.ampUserId !== 'guest') {
				if (location.href.indexOf('/jwapp/sys/czjl') >= 0) {
					(function rec () {
						let el = document.getElementsByClassName('czjl-sixItem-container')[0]
						if (el) {
							el.innerHTML = el.innerHTML.replace(/<!\-\-\s*/g, '').replace(/\s*\-\->+/g, '')
						} else {
							setTimeout(rec, 50)
						}
					})()
				}
				if (location.href.indexOf('/jwapp/sys/jwwspj') >= 0) {
					if (document.getElementsByClassName('timu-title')[0] && !document.getElementById('quick-set')) {
						let title = document.getElementsByClassName('timu-title')[0]
						let btn = HTMLElement.$mkel('button', {id: 'quick-set'}, {innerHTML: '一键五星+评价'}, {
							'border': '0',
							'width': '300px',
							'height': '40px',
							'margin-left': '10px',
							'fontW-weight': 'bold',
							'font-size': '16px',
							'color': 'white',
							'background-color': '#d22e2e',
						}, {
							'click': function () {
								let rotate = document.getElementsByClassName('rotate')[0]
								if (rotate && rotate.innerHTML.indexOf('已') >= 0) {
									bm.message({
										type: 'warning',
										message: '你已经评教过了',
										duration: 2000,
									})
									return false
								}
								$('[data-x-bl=100]').toArray().forEach(s => s.firstElementChild.click())
								$('textarea').val(prompt('请提供一个默认的教师评价'))
								return false
							},
						})
						title.$before(btn)
					}
				}
				if (location.href.indexOf('/new/index.html') >= 0) {
					if ($('#ampDesktopNav')[0] && !$('#download-training-program')[0]) {
						setfunc()
					}
				}
				if (location.href.indexOf('/jwapp/sys/xywccx') >= 0) {
					function setFunc () {
						let scoreMaps = {
							'A+': 4.5,
							'A': 4.0,
							'B+': 3.5,
							'B': 3.0,
							'C+': 2.5,
							'C': 2.0,
							'D': 1.0,
							'F': 0.0,
						}
						function setList (courses, sem) {
							function createInfo () {
								let studentName = sessionStorage.ampUserName
								let weightedScore = courses.map(c => parseFloat(c['学分']) * scoreMaps[c['成绩']])
								let score = courses.map(c => parseFloat(c['学分']))
								let avgScore = parseFloat(weightedScore.$sum() / score.$sum()).toFixed(2)
								let info = HTMLElement.$mkel(
									'div', {}, {
										innerHTML: `
											<p style="margin-top: 40px">${studentName} 同学,你本学期的绩点是:</p>
											<p style="font-size: 120px color: #fff306 height: 120px margin-top: 30px"><i>${avgScore}</i></p>
											<p style="margin: 70px 0 60px">下学期再接再厉哦!</p>
										`,
									}, {
										'font-size': '30px',
										'background-color': '#fe5f5e',
										'margin-bottom': '20px',
										'width': '640px',
										'display': 'flex',
										'flex-direction': 'column',
										'align-items': 'center',
										'color': 'white',
									}
								)
								return info
							}
							function createTable () {
								let table = HTMLElement.$mkel(
									'div', { id: 'rainbow-grade-table' }, {}, {
										'width': 'max-content',
										'height': 'max-content',
										'display': 'flex',
										'flex-direction': 'column',
										'margin': '200px',
										'align-items': 'center',
										'background-color': 'white',
										'line-height': 'normal',
									}
								)
								return table
							}
							function createItem (course) {
								function createText () {
									let text = HTMLElement.$mkel(
										'div', {}, {
												innerHTML: `
													<p style="
														overflow: hidden;
														text-overflow: ellipsis;
														white-space: nowrap;
													">课程名:${course['课程名']}</p>
													<p>课程号:${course['课程号']}</p>
													<p>学分:${course['学分']}</p>
													<p>课程类别:${course['课程类别']}</p>
												`,
										}, {
											'width': '400px',
											'font-size': '28px',
											'padding-left': '20px',
										}
									)
									let container = HTMLElement.$mkel(
											'div', {}, {}, {
													'display': 'flex',
													'flex-direction': 'column',
													'align-items': 'center',
													'justify-content': 'center',
											}
									)
									container.appendChild(text)
									return container
								}
								function createScore () {
									let text = HTMLElement.$mkel(
										'div', {}, { innerHTML: course['成绩'] }, {
											'font-size': '90px',
										},
									)
									let container = HTMLElement.$mkel(
										'div', {}, {}, {
											'display': 'flex',
											'flex-direction': 'column',
											'align-items': 'center',
											'justify-content': 'center',
											'width': '160px',
										}
									)
									container.appendChild(text)
									return container
								}
								function createCard () {
									let cmap = {
										'A+': '#e7322f',
										'A': '#fe5f5e',
										'B+': '#7648d9',
										'B': '#4d89d7',
										'C+': '#ff7905',
										'C': '#ffa303',
										'D': '#219b3e',
										'F': '#4e4e4e',
									}
									let bgc = cmap[course['成绩']]
									let card = HTMLElement.$mkel('div', {}, {}, {
										'display': 'flex',
										'flex-direction': 'row',
										'align-items': 'center',
										'justify-content': 'space-between',
										'width': '600px',
										'height': '200px',
										'margin-bottom': '20px',
										'border-radius': '12px',
										'background-color': bgc,
										'color': 'white',
									})
									let text = createText()
									let score = createScore()
									card.appendChild(text)
									card.appendChild(score)
									return card
								}
								let item = createCard()
								return item
							}
							let semList = Array.from(new Set(courses.map(c => c['学年学期']).filter(c => c)))
							if (sem && !semList.includes(sem)) {
								bm.message({
									type: 'warning',
									message: '输入不合法,默认生成最新学期的彩虹地毯',
									duration: 2000,
								})
								sem = semList.$max()
							}
							if (!sem) sem = semList.$max()
							courses = courses.filter(c => c['学年学期'] === sem)
							courses = courses.$sorted([c => parseFloat(c['学分']), c => scoreMaps[c['成绩']]], [true, true])
							let table = createTable()
							table.appendChild(createInfo())
							courses.forEach(course => table.appendChild(createItem(course)))
							$('.bh-paper-pile-body')[0].$before(table)
						}
						function downloadList () {
							function callbackfn (canvas) {
									let a = HTMLElement.$mkel(
										'a', {
											href: canvas.toDataURL('image/png', 1.0),
											download: '彩虹地毯.png',
										}
									)
									document.body.appendChild(a)
									a.click()
									document.body.removeChild(a)
							}
							if (typeof html2canvas !== 'undefined') {
								$.getScript('https://html2canvas.hertzen.com/dist/html2canvas.js', function () {
									html2canvas($('#rainbow-grade-table')[0], { dpi: window.devicePixelRatio }).then(callbackfn)
								})
							} else {
								html2canvas($('#rainbow-grade-table')[0], { dpi: window.devicePixelRatio }).then(callbackfn)
							}
						}
						function setBar (courses) {
							if (!$('#xywcqk_chdt')[0]) {
								let bar = $('.bh-paper-pile-dialog-container')[0]
								if (bar) {
									bar.style.width = `${bar.offsetWidth + 100}px`
									let a = HTMLElement.$mkel(
										'a', {
											id: 'xywcqk_chdt'
										}, {
											innerHTML: '彩虹地毯',
										}, {
											'font-size': '14px',
											'margin-left': '20px',
											'cursor': 'pointer',
										}
									)
									let last = $('#xywcqk_bxqxk')[0]
									last.$before(a)
									a.$before(last)
									a.addEventListener('click', function (event) {
										if (!$('#rainbow-grade-table')[0]) {
											let semester = prompt(`请输入需要生成彩虹地毯的学年学期,格式为 'yyyy-yyyy-n' ,如 '2020-2021-2' 表示2020学年至2021学年第2学期,若输入不合法或不输入,将默认生成最新学期的彩虹地毯`)
											while (semester && !/^\d{4}-\d{4}-\d$/.exec(semester)) semester = prompt(`输入格式必须为 'yyyy-yyyy-n'`)
											if (!semester) {
												bm.message({
													type: 'warning',
													message: '未输入,默认生成最新学期的彩虹地毯',
													duration: 2000,
												})
												semester = undefined
											} else {
												let [yearStart, yearEnd, num] = semester.match(/\d+/g)
												if (parseInt(yearEnd) - parseInt(yearStart) !== 1 || !parseInt(num).$in(1, 2, true)) {
													bm.message({
														type: 'warning',
														message: '输入不合法,默认生成最新学期的彩虹地毯',
														duration: 2000,
													})
													semester = undefined
												}
											}
											setList(courses, semester)
											downloadList()
										}
									})
								}
							}
						}
						if (!window.courseClasses) {
							$.ajax({
								method: 'POST',
								url: 'http://ehall.szu.edu.cn/jwapp/sys/xywccx/modules/xywccx/cxscfakz.do',
								data: {
									BYNJDM: '-',
								},
							}).then(res => {
								let courseClasses = res.datas.cxscfakz.rows
								window.courseClasses = courseClasses
								courseClasses = courseClasses.$map(['KZM', 'KZH', 'PYFADM', 'YQXF', 'WCXF', 'YQMS', 'WCMS', 'YQWCKZS', 'WCKZS', 'FKZH']).filter(c => c.FKZH !== '-1')
								Promise.all(courseClasses.map(courseClass => {
									return $.ajax({
										method: 'POST',
										url: 'http://ehall.szu.edu.cn/jwapp/sys/xywccx/modules/xywccx/cxscfakzkc.do',
										data: {
											BYNJDM: '-',
											KZH: courseClass.KZH,
											PYFADM: courseClass.PYFADM,
											pageSize: 999,
											pageNumber: 1,
										},
									}).then(res => {
										let courseList = res.datas.cxscfakzkc.rows
										courseList = courseList.$map(['KCM', 'KCH', 'CJ', 'XF', 'KCLBDM_DISPLAY', 'SFTG_DISPLAY', 'XNXQDM'])
										courseList = courseList.$reindex({'KCM': '课程名', 'KCH': '课程号','CJ': '成绩', 'XF': '学分', 'KCLBDM_DISPLAY': '课程类别', 'SFTG_DISPLAY': '是否通过', 'XNXQDM': '学年学期'})
										courseList.forEach(c => {
											c['成绩'] = c['成绩'] || 'F'
											c['学分'] = parseFloat(c['学分']).toFixed(2)
										})
										return courseList
									})
								})).then(res => {
									setBar(res.$merge())
									window.courseClasses = undefined
								})
							})
						}
					}
					let fn = (events) => {
						for (let event of events) {
							for (let node of event.addedNodes) {
								if (node.classList.contains('bh-paper-pile-dialog')) {
									setFunc()
								}
							}
						}
					}
					document.body.$monitor({ childList: true }, fn)
				}
        if (location.href.indexOf('/jwapp/sys/zzy') >= 0) {
          function addCJ () {
            if (document.getElementById('convenient-szu-zzy-cj')) return
            $.ajax({
              url: 'http://ehall.szu.edu.cn/jwapp/sys/zzy/modules/xszzysq/cxxszzybmsq.do',
              type: 'POST',
              data: { XH: localStorage.ampUserId },
            }).then(res => {
              var info = res.datas.cxxszzybmsq.rows[0]
              if (info) {
                var bar = document.createElement('span')
                bar.id = 'convenient-szu-zzy-cj'
                if (info.GGKCJ) bar.innerHTML += `公共课成绩:${info.GGKCJ}&emsp;`
                if (info.ZYKCJ) bar.innerHTML += `专业课成绩:${info.ZYKCJ}&emsp;`
                if (info.ZCJ) bar.innerHTML += `总成绩:${info.ZCJ}&emsp;`
                if (bar.innerHTML.length) {
                  (function rec () {
                    var text_center = document.querySelector('.bh-text-center.bh-pull-left')
                    if (text_center) text_center.appendChild(bar)
                    else setTimeout(rec, 250)
                  })()
                }
              }
            })
          }
          if (location.href.endsWith('#/xszzysq')) addCJ()
          window.onhashchange = () => {
            if (location.href.endsWith('#/xszzysq')) addCJ()
          }
        }
			}
			(function rec () {
				if ($('#ampTabContentItem0')[0]) {
					$('#ampTabContentItem0')[0].$monitor({ childList: true, subtree: true }, (events) => {
						$('.appFlag.widget-app-item').attr('amp-unviewabledescription', 'true')
						$('.appFlag.amp-app-card-hover-big').attr('amp-unviewabledescription', 'true')
					})
				} else {
					setTimeout(rec, 50)
				}
			})()
      var ampServiceCenterSearchApps = $('#ampServiceCenterSearchApps')[0]
      if (ampServiceCenterSearchApps) {
        ampServiceCenterSearchApps.$monitor({ childList: true, subtree: true }, (events) => {
          $('.appFlag.widget-app-item').attr('amp-unviewabledescription', 'true')
          $('.appFlag.amp-app-card-hover-big').attr('amp-unviewabledescription', 'true')
        })
      }
		} else if (location.host == '172.30.255.2') {
			if (location.href.includes('.htm')) {
				let username = document.getElementById('username')
				let password = document.getElementById('password')
				let submit = document.querySelector('#submit[type=submit]')
				if (username && password && submit) {
					username.value = account.cid
					password.value = account.pwd
					submit.click()
				}
			}
		} else if (location.host == '192.168.84.3:9090') {
			let client = document.querySelector('[name=client]')
			let buildingName = document.querySelector('[name=buildingId]')
			let roomName = document.querySelector('[name=roomName]')
			let beginTime = document.querySelector('#beginTime')
			let endTime = document.querySelector('#endTime')
			if (client && buildingName && roomName) {
				if (account.clientSelectedIndex !== client.selectedIndex) {
					account.clientSelectedIndex = client.selectedIndex
					account.client = client.value
					delete account.buildingNameSelectedIndex
					delete account.buildingId
					delete account.roomName
					GM_setValue('account', account)
				}
				if (account.buildingId) buildingName.selectedIndex = account.buildingNameSelectedIndex
				buildingName.removeAttribute('onchange')
				buildingName.onchange = function () {
					account.buildingNameSelectedIndex = this.selectedIndex
					account.buildingId = this.options[this.selectedIndex].value
					GM_setValue('account', account)
				}
				if (account.roomName) roomName.value = account.roomName
				roomName.oninput = function () {
					account.roomName = this.value
					GM_setValue('account', account)
				}
			} else if (beginTime && endTime && location.href.indexOf('login.do') >= 0) {
				let toDate = (date) => date.toLocaleDateString().replace(/\//g, '-')
				let now = new Date()
				let twentyDaysAgo = new Date(now.getTime() - 1000 * 86400 * 19)
				beginTime.value = toDate(twentyDaysAgo)
				endTime.value = toDate(now)
				document.querySelector('[name=type]').selectedIndex = 1
				document.querySelector('[type=submit]').click()
			}
		} else if (location.host == 'drcom.szu.edu.cn') {
			if (location.href.includes('.htm')) {
				let username_el = document.querySelector('input[name=DDDDD]')
				let password_el = document.querySelector('input[name=upass]')
				let submit_el = document.querySelector('input[type=submit]')
				if (username_el && password_el && submit_el) {
					username_el.value = account.cid
					password_el.value = account.pwd
					submit_el.click()
				}
			}
		} else if (location.host.match(/bkxk.*?\.szu\.edu\.cn/)) {
			let loginName_el = document.getElementById('loginName')
			let loginPwd_el = document.getElementById('loginPwd')
			if (loginName_el && loginPwd_el) {
				loginName_el.value = account.uid
				loginPwd_el.value = account.pwd
			}
		} else if (location.host == 'self.szu.edu.cn') {
			let account_el = document.getElementById('account')
			let pass_el = document.getElementById('pass')
			let submit_el = document.querySelector('input[type=submit]')
			if (account_el && pass_el && submit_el) {
				account_el.value = account.cid
				pass_el.value = account.pwd
				submit_el.click()
			}
		} else if (location.host.match(/authserver.*?\.webvpn\.szu\.edu\.cn/)) {
      var username_el = document.getElementById('username')
      var password_el = document.getElementById('password')
      var helper_el = document.querySelector('.iCheck-helper')
      var button_submit = document.querySelector('button[type=submit]')
      if (username_el && password_el && helper_el && button_submit) {
        helper_el.click()
        username_el.setAttribute('value', account.cid)
        password_el.setAttribute('value', account.pwd)
        button_submit.click()
      }
    } else if (1) {}
  })
})()

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址