From 4a3b12f59d15d571b2549ee3a6c4e6b5ed0decce Mon Sep 17 00:00:00 2001 From: kola-web Date: Mon, 22 Jun 2026 14:40:58 +0800 Subject: [PATCH] stash --- README.md | 7 + project.private.config.json | 11 +- src/api/request.ts | 5 +- src/app.json | 3 +- src/app.ts | 172 +++++++++++++------- src/components/svg-icon/README.md | 91 ++--------- src/components/svg-icon/index.ts | 224 ++++++++++++++++++--------- src/components/svg-icon/index.wxml | 3 +- src/images/icon69.png | Bin 0 -> 1974 bytes src/images/icon70.png | Bin 0 -> 1965 bytes src/images/icon71.png | Bin 0 -> 1031 bytes src/images/icon72.png | Bin 0 -> 1186 bytes src/images/icon73.png | Bin 0 -> 1167 bytes src/images/icon74.png | Bin 0 -> 1233 bytes src/images/icon75.png | Bin 0 -> 1154 bytes src/images/svg2.svg | 5 + src/pages/login/index.json | 1 + src/pages/login/index.scss | 26 +++- src/pages/login/index.ts | 148 +++++++++++++++++- src/pages/login/index.wxml | 24 ++- src/pages/schedule/index.json | 7 + src/pages/schedule/index.scss | 310 +++++++++++++++++++++++++++++++++++++ src/pages/schedule/index.ts | 149 ++++++++++++++++++ src/pages/schedule/index.wxml | 142 +++++++++++++++++ typings/index.d.ts | 29 +++- 25 files changed, 1133 insertions(+), 224 deletions(-) create mode 100644 src/images/icon69.png create mode 100644 src/images/icon70.png create mode 100644 src/images/icon71.png create mode 100644 src/images/icon72.png create mode 100644 src/images/icon73.png create mode 100644 src/images/icon74.png create mode 100644 src/images/icon75.png create mode 100644 src/images/svg2.svg create mode 100644 src/pages/schedule/index.json create mode 100644 src/pages/schedule/index.scss create mode 100644 src/pages/schedule/index.ts create mode 100644 src/pages/schedule/index.wxml diff --git a/README.md b/README.md index a41ed9d..7d80a85 100644 --- a/README.md +++ b/README.md @@ -6,3 +6,10 @@ powershell 软链形式 ``` New-Item -ItemType Junction -Path "src/images" -Target C:\Users\kola\project\school-system\web_dist\images ``` + +测试账号 + +``` +账号 2026050122123 +密码 123456 +``` diff --git a/project.private.config.json b/project.private.config.json index 3a43ce0..81dd7a9 100644 --- a/project.private.config.json +++ b/project.private.config.json @@ -4,13 +4,20 @@ "miniprogram": { "list": [ { - "name": "登录", - "pathName": "pages/login/index", + "name": "课表", + "pathName": "pages/schedule/index", "query": "", "scene": null, "launchMode": "default" }, { + "name": "登录", + "pathName": "pages/login/index", + "query": "", + "launchMode": "default", + "scene": null + }, + { "name": "我的评论", "pathName": "pages/myComment/index", "query": "", diff --git a/src/api/request.ts b/src/api/request.ts index f0d3379..8251f2c 100644 --- a/src/api/request.ts +++ b/src/api/request.ts @@ -24,15 +24,16 @@ export const request = function ( mask: true, }) } + + const app = getApp() + wx.request({ header: { - loginState: getApp().globalData.loginState, ...header, }, url: gUrl + url, method, data: { - loginState: getApp().globalData.loginState, ...(data as object), }, ...options, diff --git a/src/app.json b/src/app.json index 3278354..5fc9f82 100644 --- a/src/app.json +++ b/src/app.json @@ -18,7 +18,8 @@ "pages/agentEva/index", "pages/myAct/index", "pages/myAgent/index", - "pages/myComment/index" + "pages/myComment/index", + "pages/schedule/index" ], "window": { "backgroundTextStyle": "light", diff --git a/src/app.ts b/src/app.ts index 4f656b1..236f6f4 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,9 +1,9 @@ +/* eslint-disable perfectionist/sort-imports */ +import dayjs from 'dayjs' import page from '@/utils/page' - import { request } from './api/request' - import { parseScene } from './utils/util' -const dayjs = require('dayjs') + const licia = require('miniprogram-licia') require('/utils/dayjs/day-zh-cn.js') const relativeTime = require('/utils/dayjs/relativeTime.js') @@ -12,8 +12,8 @@ dayjs.extend(relativeTime) App({ globalData: { - url: '', - upFileUrl: '', + url: 'https://app.gohighedu.cn', + upFileUrl: 'https://app.gohighedu.cn', imageUrl: 'https://app.gohighedu.cn/images', Timestamp: new Date().getTime(), @@ -21,9 +21,7 @@ App({ waitBindDoctorId: '', scene: {}, - loginState: '', initLoginInfo: {}, - userInfo: {}, }, onLaunch() { @@ -42,19 +40,44 @@ App({ startLogin(callback?: () => void) { wx.login({ success: (res) => { - wx.ajax({ - method: 'GET', - url: '?r=wtx/user/init-login', - data: { - code: res.code, - }, - }).then((res: any) => { - this.globalData.loginState = res.loginState - this.globalData.initLoginInfo = res - if (callback) { - callback() - } - }) + console.log("DEBUGPRINT[244]: app.ts:42: res=", res) + // // 调用静默登录接口 + // wx.ajax({ + // method: 'POST', + // url: '/auth/silent-login', + // showMsg: false, // 隐藏错误提示 + // data: { + // code: res.code, + // }, + // }) + // .then((response: any) => { + // const { accessToken, user } = response + // + // // 存储 accessToken + // this.globalData.accessToken = accessToken + // + // // 存储用户信息 + // if (user) { + // this.globalData.userInfo = user + // } + // + // // 更新 initLoginInfo + // this.globalData.initLoginInfo = { + // user, + // } + // + // if (callback) { + // callback() + // } + // }) + // .catch((err: any) => { + // // 静默失败,不提示用户 + // console.error('静默登录请求失败:', err) + // }) + }, + fail: (err) => { + // 静默失败,不提示用户 + console.error('wx.login 失败:', err) }, }) }, @@ -71,54 +94,92 @@ App({ }) }, waitLogin({ type }: { type?: 0 | 1 | 2 | 'any' } = { type: 'any' }) { - return new Promise((resolve) => { + return new Promise((resolve, reject) => { const checkLogin = () => { - if (this.globalData.loginState) { - if (this.checkLoginType(type ?? 'any')) { + // type = 0:不需要登录即可访问 + if (type === 0) { + resolve() + return + } + + // type = 'any':不检查登录状态 + if (type === 'any') { + resolve() + return + } + + // type = 1 或 2:需要登录 + if (type === 1 || type === 2) { + // 检查是否有 accessToken + if (this.globalData.accessToken) { + // 已登录,检查是否需要绑定 + if (this.globalData.initLoginInfo?.needBind) { + // 需要绑定,跳转到登录页 + this.redirectToLogin(type) + reject(new Error('need_bind')) + return + } resolve() + return } + + // 未登录,跳转到登录页 + this.redirectToLogin(type) + reject(new Error('not_logged_in')) return } - setTimeout(() => { - checkLogin() - }, 500) + + resolve() } checkLogin() }) }, + + /** + * 重定向到登录页,并记录当前页面路径 + */ + redirectToLogin(_type: 1 | 2) { + // 获取当前页面路径 + const pages = getCurrentPages() + const currentPage = pages[pages.length - 1] + const currentUrl = currentPage ? currentPage.route : '' + + // 记录来源页面,登录后返回 + if (currentUrl && currentUrl !== 'pages/login/index') { + this.globalData.loginRedirectUrl = currentUrl + } + + wx.reLaunch({ + url: '/pages/login/index', + }) + }, + checkLoginType(type: 0 | 1 | 2 | 'any') { - const { loginType, isLogin, isReg } = this.globalData.initLoginInfo + // type = 0:不需要登录 + if (type === 0) { + return true + } + // type = 'any':不检查 if (type === 'any') { return true } - if (isLogin !== 1) { - if (type === 0) { - return true + // type = 1 或 2:需要登录 + if (type === 1 || type === 2) { + // 检查是否有 accessToken + if (!this.globalData.accessToken) { + this.redirectToLogin(type as 1 | 2) + return false } - wx.reLaunch({ - url: '/pages/index/index', - }) - return false - } - if (isReg !== 1) { - const typePageUrl: Record = { - 1: '/pages/login/index', - 2: '/pages/login/index', + // 检查是否需要绑定 + if (this.globalData.initLoginInfo?.needBind) { + this.redirectToLogin(type as 1 | 2) + return false } - wx.reLaunch({ - url: typePageUrl[loginType as 1 | 2], - }) - return false - } - if (loginType !== type) { - wx.reLaunch({ - url: '/pages/index/index', - }) - return false + return true } return true @@ -135,7 +196,6 @@ App({ }) }, autoUpdate() { - const self = this if (wx.canIUse('getUpdateManager')) { const updateManager = wx.getUpdateManager() updateManager.onCheckForUpdate((res) => { @@ -143,18 +203,18 @@ App({ wx.showModal({ title: '更新提示', content: '检测到新版本,是否下载新版本并重启小程序?', - success(res) { + success: (res) => { if (res.confirm) { - self.downLoadAndUpdate(updateManager) + this.downLoadAndUpdate(updateManager) } else if (res.cancel) { wx.showModal({ title: '温馨提示~', content: '本次版本更新涉及到新的功能添加,旧版本无法正常访问的哦~', showCancel: false, confirmText: '确定更新', - success(res) { + success: (res) => { if (res.confirm) { - self.downLoadAndUpdate(updateManager) + this.downLoadAndUpdate(updateManager) } }, }) @@ -165,8 +225,8 @@ App({ }) } else { wx.showModal({ - title: '提示', - content: '当前微信版本过低,无法使用该功能,请升级到最新微信版本后重试。', + title: '温馨提示', + content: '当前微信版本过低,无法使用版本更新功能,请升级到最新微信版本后重试。', }) } }, diff --git a/src/components/svg-icon/README.md b/src/components/svg-icon/README.md index a41ac4b..4cb7ac8 100644 --- a/src/components/svg-icon/README.md +++ b/src/components/svg-icon/README.md @@ -26,96 +26,32 @@ + + + + ``` -## 单颜色重新着色 +## 重新着色 传入 `color` 时,会对 SVG 中所有声明 `fill/stroke` 的元素统一着色: ```xml + ``` -## 多颜色重新着色 - -### 数组形式(按顺序替换) - -以数组形式传入 `colors` 时,依照数组中的颜色顺序,对 SVG 中所有声明 `fill/stroke` 的元素按顺序重新着色: - -```ts -// page.ts -Page({ - data: { - colorsArray: ['#ff0000', '#00ff00', '#0000ff'], - }, -}) -``` - -```xml - - -``` - -### 对象形式(按键值关系替换) - -以对象形式传入 `colors` 时,依照对象中的键值关系,对 SVG 中所有声明 `fill/stroke` 的元素按对应关系重新着色: - -```ts -// page.ts -Page({ - data: { - colorsObject: { - black: '#ff0000', - '#fff': '#00ff00', - '#808080': '#cdcdcd', - }, - }, -}) -``` - -```xml - - -``` - -## 组合重新着色 - -同时传入 `color` 和 `colors` 组合搭配,既能为指定元素重新着色,也能为其余未指定元素统一着色: - -```ts -// page.ts -Page({ - data: { - colorsArray: ['#ff0000', '#00ff00'], - }, -}) -``` - -```xml - - -``` - -## 网络资源 - -支持传入网络 SVG 资源地址: - -```xml - -``` - -**注意**:当 src 传入网络资源并重新着色时,请将网络资源的域名配置于小程序的 `downloadFile` 合法域名中。 - ## API ### Properties | 参数 | 说明 | 类型 | 默认值 | 必填 | | --- | --- | --- | --- | --- | -| `src` | SVG 资源地址(支持本地路径、临时路径、网络资源) | `string` | `''` | 是 | -| `color` | SVG 单一颜色(对所有 fill/stroke 元素统一着色) | `string` | `''` | 否 | -| `colors` | SVG 多颜色配置(支持数组或对象) | `array \| object` | `null` | 否 | +| `src` | SVG 资源地址(支持本地路径) | `string` | `''` | 是 | +| `color` | SVG 颜色(对所有 fill/stroke 元素统一着色) | `string` | `''` | 否 | | `mode` | SVG 裁剪、缩放模式(与 `image` 标签相同) | `string` | `''` | 否 | +| `width` | SVG 宽度(支持 rpx/px 单位) | `string` | `'48rpx'` | 否 | +| `height` | SVG 高度(支持 rpx/px 单位) | `string` | `'48rpx'` | 否 | ### Events @@ -153,12 +89,15 @@ Page({ ## 实现原理 -1. 读取 SVG 文件内容(本地文件或网络文件) +1. 读取 SVG 文件内容 2. 通过正则替换 SVG 中的 `fill/stroke` 属性值来实现改色 3. 将修改后的 SVG 内容转为 base64 格式,作为 `image` 的 `src` ## 注意事项 - SVG 文件必须包含 `fill` 或 `stroke` 属性才能被改色 +- 支持本地路径、网络地址(HTTP/HTTPS)、临时路径(wxfile://) +- **开发工具限制**:由于开发工具的文件系统权限限制,本地路径的 SVG 可能无法改色,此时会直接显示原 SVG(不改色) +- **正式环境建议**:使用网络地址,这样可以正常改色 - 网络资源需要配置 `downloadFile` 合法域名 -- 组件会缓存下载的网络资源,避免重复下载 \ No newline at end of file +- 组件会缓存下载的网络资源,避免重复下载 diff --git a/src/components/svg-icon/index.ts b/src/components/svg-icon/index.ts index a86ea35..08c478d 100644 --- a/src/components/svg-icon/index.ts +++ b/src/components/svg-icon/index.ts @@ -7,22 +7,90 @@ import { encode } from './base64' const fs = wx.getFileSystemManager() -// 临时文件缓存(网络资源下载后缓存) -const tempFileMap = new Map() +// 网络资源缓存(避免重复下载) +const networkCache = new Map() /** - * 同步下载网络文件 - * @param url 网络资源地址 - * @returns 下载结果 + * 判断是否为网络资源 */ -function downloadFileSync(url: string): Promise { - return new Promise((resolve, reject) => { +function isNetworkUrl(src: string): boolean { + return /^https?:\/\//.test(src) +} + +/** + * 判断是否为本地资源路径 + */ +function isLocalPath(src: string): boolean { + return !isNetworkUrl(src) && !src.startsWith('wxfile:') +} + +/** + * 获取本地文件的完整路径 + * 开发工具中需要使用完整的绝对路径 + */ +function getLocalFullPath(src: string): string { + // 如果已经是完整路径,直接返回 + if (src.startsWith('/') && !src.startsWith('/images')) { + return src + } + + // 微信小程序中,/images/xxx 是相对于项目根目录的 + // 开发工具中需要转换为完整路径 + // 注意:这里假设项目根目录为 src/ + if (src.startsWith('/images/')) { + // 尝试多种路径格式 + const paths = [ + src, // 原始路径 + `/src${src}`, // 添加 src 前缀 + `${wx.env.USER_DATA_PATH}${src}`, // 用户数据路径 + ] + + // 尝试读取文件,找到正确的路径 + for (const path of paths) { + try { + fs.accessSync(path) + return path + } catch { + continue + } + } + + // 如果都找不到,返回原始路径 + return src + } + + return src +} + +/** + * 下载网络资源 + */ +async function downloadNetworkResource(url: string): Promise { + // 检查缓存 + const cachedPath = networkCache.get(url) + if (cachedPath) { + try { + fs.accessSync(cachedPath) + return cachedPath + } catch { + // 缓存失效,重新下载 + networkCache.delete(url) + } + } + + // 下载文件 + const downloadResult = await new Promise((resolve, reject) => { wx.downloadFile({ url, success: resolve, fail: reject, }) }) + + // 缓存临时文件路径 + networkCache.set(url, downloadResult.tempFilePath) + + return downloadResult.tempFilePath } Component({ @@ -33,100 +101,102 @@ Component({ externalClasses: ['image-class'], properties: { - /** SVG 资源地址(支持本地路径、临时路径、网络资源) */ + /** SVG 资源地址(支持本地路径、网络地址、临时路径) */ src: { type: String, value: '', }, - /** SVG 单一颜色(对所有 fill/stroke 元素统一着色) */ + /** SVG 颜色(对所有 fill/stroke 元素统一着色) */ color: { type: String, value: '', }, - /** SVG 多颜色配置(支持数组或对象) */ - colors: { - type: null, - value: null, - }, /** SVG 裁剪、缩放模式(与 image 标签相同) */ mode: { type: String, value: '', }, + /** SVG 宽度(支持 rpx/px 单位,默认 48rpx) */ + width: { + type: String, + value: '48rpx', + }, + /** SVG 高度(支持 rpx/px 单位,默认 48rpx) */ + height: { + type: String, + value: '48rpx', + }, }, observers: { - 'src, color, colors': async function (src: string, color: string, colors: unknown) { + 'src, color': async function (src: string, color: string) { try { - // 如果需要改色 - if (color || (colors && ((Array.isArray(colors) && colors.length > 0) || (typeof colors === 'object' && Object.keys(colors).length > 0)))) { - let svgData: string - - // 判断是否为网络资源(排除开发工具临时路径 http://tmp/) - if (/^https?:\/\//.test(src) && !/^http:\/\/tmp\//.test(src)) { - // 网络资源需要先下载 - let tempFilePath = tempFileMap.get(src) - try { - if (!tempFilePath) throw new Error('未缓存') - // 检查临时文件是否存在 - fs.accessSync(tempFilePath) - } catch { - // 下载文件 - const downloadResult = await downloadFileSync(src) - tempFilePath = downloadResult.tempFilePath - // 缓存临时文件路径 - tempFileMap.set(src, tempFilePath) - } - // 读取文件内容 - svgData = fs.readFileSync(tempFilePath, 'utf8') as string - } else { - // 本地资源直接读取 - svgData = fs.readFileSync(src, 'utf8') as string - } + if (!src) { + this.setData({ base64: '' }) + return + } - // 处理颜色配置 - const colorsConfig = colors || {} - - // 替换 SVG 中的 fill/stroke 属性 - if (/(?:fill|stroke)=".*?"/.test(svgData)) { - let colorIndex = 0 - svgData = svgData.replace(/(?:fill|stroke)=".*?"/g, (matched) => { - // 获取原本颜色值 - const originalColor = matched.slice(matched.indexOf('"') + 1, -1) - - // 计算替换颜色 - let replaceColor: string - if (Array.isArray(colorsConfig)) { - // 数组形式:按顺序替换 - replaceColor = colorsConfig[colorIndex++] || color || originalColor - } else { - // 对象形式:按键值关系替换 - replaceColor = colorsConfig[originalColor] || colorsConfig[colorIndex++] || color || originalColor - } - - // 返回替换后的属性 - if (/fill/.test(matched)) return `fill="${replaceColor}"` - if (/stroke/.test(matched)) return `stroke="${replaceColor}"` - return `fill="${replaceColor}"` - }) - } + // 如果不需要改色,直接使用原路径 + if (!color) { + this.setData({ base64: src }) + return + } - // 设置默认底色(SVG 根元素) - const defaultColor = (typeof colorsConfig === 'object' && !Array.isArray(colorsConfig) && (colorsConfig['#000'] || colorsConfig['#000000'] || colorsConfig.black)) || color - if (defaultColor && !/fill=".*?"/.test(svgData.slice(0, svgData.indexOf('>')))) { - svgData = svgData.replace(/ { + if (/fill/.test(matched)) return `fill="${color}"` + if (/stroke/.test(matched)) return `stroke="${color}"` + return `fill="${color}"` }) - } else { - // 不需要改色,直接使用原路径 - this.setData({ base64: src }) } + + // 设置 SVG 根元素的默认底色 + if (!/fill="[^"]*"/.test(modifiedSvg.slice(0, modifiedSvg.indexOf('>')))) { + modifiedSvg = modifiedSvg.replace(/ \ No newline at end of file +/> diff --git a/src/images/icon69.png b/src/images/icon69.png new file mode 100644 index 0000000000000000000000000000000000000000..17d41d32a0493b3e45c019c0e6b3f2a2cdae1e45 GIT binary patch literal 1974 zcmV;n2TAyeP)ZJ?B3B_tG-q+qm__6HJ#Rf=w(?>ck2Ugyl5nS0KencJ1$Z-3{`obx-s-{+p0 zIrHa^*F?jAP6-TwI>i}9L!d*TjtMw+Hyok=2cYeCdjuUvZ$-DD)3K_&iSiga;+TaK zca@J&z^p}BjH=uc#}h<=A2Ll2^Jv8wzzqYD6h<7I_!-HAf6zqd~xW1xgZkf=rwBJJ>O_ zD%nWK30TF%M?c~PC?Hp&ZxixiRLNj!A&TWNv3-E9_7PDTpqX(H=oTt`gU+CJl?Msz zII-S^bt+DQ65tC&w;$Ef7gK9djPr5K&H{A;l%O;b-$zulXs81YT{)3i;@^ilIZ@~v zD1odXElq`{uuT=kE~v_-uN?XU+68lSUs2Sfu}&&9Z|w_eX`sN=Ch{>XG?B&(geVt{ zROMDGq0&I7s&8*{q%WsF=X~?c!zc@1OP#btcaJa)&v8t%2fa z7g|=FwCjbLI1K=G7f`heNV+syE1)>jwM1R1F*@?S!UQwv*E3Y*zCGzH%US`&L8V9a zJeb6{B=f&S(S^U;OJxs#bkxrsrMW<>9-X!@uU{TTJ11JvifdaQST4EL1#(Rpmp+*O zgBl~1<>%3sT2~`v@$7otO;me_oh4EdkO;tQ<)V4s(q*$Inx1yDK4puOe0hc+^fyS zAa9p5-KUf0(F8QUW0TgT+%iphbiGnjpezr%SEL(o8qrzxYOpSqkqPKuJ^~&vs29>0 znSh>8tL$Rg06{&Q#=`{kOj>1E%LWAMLK=@$psiL*`-jv9X->*4YdF*e7+p%sI~8bL z@%z|%GajINi?zq`w>_UbAG3fS@6_^GXeiVZCL~!v^*c*bJEzMV2-V0Y3+Pd({euRP z20;}Q{r)PIixDV0-9Kny9NMgNE)9Wt3^vATr4eXMYfo&0nFc`B@34$K@_>FsOAT>M zIH@_*_p(T20%d6bNaJ}lxYQKtL74oU1tSw^+_19eLTd>1jhKni*Z}>M)_P$8CcG4d z>hW>L6O7Kic?5ES;zawTyG0?a&61!f_^HH*>|IT&+i^8WaM`H>F!6kI_&EDok}EsMDj~_L)gy zGvs_O`CwxU0hL2KIE1bAs89bPWR_etWh~TJVW9F(M=xSC&uLlTWZ56*SU87LK;vE> zz#1=Ov9772Q*Gr|D*nX#WY&9 zwQY@aDxFAQK-Fs;fJSPQ_o<4#ZP1vgtL+sbo8I(vnIrtT8m%SG8*SYUik7_qO@bikBk0Y1z86(8m`X$!$o00! z3F6U39&bT-1)4-n5_{1Etln~)tnz7fz)@fNHT6yO|1PR!KKl&&KP`}uz-r#Qo~-mD zYE7hFCMjQfgy8s5PQ=^kuP? zsIE>b)K)&$;8UCRYgpZ}wQ}OJQYApU6Cm;Ps7CH{SdA!+Ui~0IucXk+{j|vnmQ-c^ z&QiZ*8=?Ms^lnsb<>Sw8-&K?=2bz{NfG(k@(W9s~S;3O3`~}NSqN&ekruhrGtd>AS z0&v~e5U69KL!d*TjtMw+_a6WN0RR6ri5D3F000I_L_t&o0ESoE$7yuHIsgCw07*qo IM6N<$f|(eiHUIzs literal 0 HcmV?d00001 diff --git a/src/images/icon70.png b/src/images/icon70.png new file mode 100644 index 0000000000000000000000000000000000000000..aa4fa020f99403d5146cd9ab664d17e22906cf51 GIT binary patch literal 1965 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 Ab^rhX literal 0 HcmV?d00001 diff --git a/src/images/icon73.png b/src/images/icon73.png new file mode 100644 index 0000000000000000000000000000000000000000..92299a59f4e7d892b342a1b7025bb984e3ab095b GIT binary patch literal 1167 zcmV;A1aSL_P)rujY+@dY+U%D0$qI@i3dX01nJtAr z#AXtxG?Id8clJRnR4{^q{sSdKzn}@UPhHyWoSw6!bU$XAnY}|pVJ>snd(S=hcRnY% za};ci6HB#MU9WnU>89)Xlz{HI3)M>%uU6QC+V)3e=e+8luIJxjN}#LCwpGub^!%3} zDrq_}=lSp54`>Yl$C&yd*{}d{9|0}83;wT;vm8|74FW7OMJg7-ScGuk**$x|iIRQJeEu_N1_^YBIDST|Mm2!GU^HRykN$&* zM`!0(p6Nsq9!LIoC%@OV1Uv|W<5EJyy@EuXvrNO<+qu_g?tYzlsRfF$3)6a@2+oxX`~Pp{$$X3F z%RDVYR43JRF~0;r%c+18tfP@T3K1iy2ZuQA>k-?D*c_-;v0dH)*l64+5}?E2I(6zB zl-suz=}=>i9D3^t7`b6qelAoX4wLJih-Nk~uZ@a$(*b8{s$MSUzs_0405c8%;z!*S z&cgr?g31k+iuTB-ll!j<=;KHnEKk^@_ZsvP0*=SkBXVrGEjE@XMn30n@8%y0w)c^` zk3V?x_Ti#^zBHac9hasj^FNDGk5J;xj!liMPjmUk)gy9jcuj0<)zIcZ>&DYE&m7q4 zOtob|S|4)@)x5h{n~Y1+WpkH)doigaa;9>+_F-H-B6q^`-xeENbu}H}H`-*wzss4s zuXA6=2cnJ0PhEI{+lf^C|)Ih5+8p8Th*xD8OMP4yrzNiT7-#ULT8t=JcBbo&YHJrU2X$ zl!Z1S3@S^Vio)4t;wXGy|x8Jj+062%jL6Ge8?@>*E_@7banFBW*q0&)Q%I;3A|sxNl?h z8C|W##@+f~nti7*D=#V@Gknc$_E^FslJW>~rnB`S7{{hYZ}Er74>C`MI8c0@{#G3> zZbqz002ovPDHLkV1kwkD--|# literal 0 HcmV?d00001 diff --git a/src/images/icon74.png b/src/images/icon74.png new file mode 100644 index 0000000000000000000000000000000000000000..1e8e433c2342be321b4d0882b396ec537d79a9a8 GIT binary patch literal 1233 zcmV;?1TOoDP)BO-nFAlZ%17#JRt`k(! zG&3mE=4A-+Cls2O^$$f?#nQh-QE?L)RhSN))k)j*-Q&5j?b4SYwSx2pa=Bk}-#IVI z_ucbec;CGOxW!^4^^rt695Joa3}SpcU|J7xs%*P>J!!4U)Y#)dWUz|$4ArkBfL9_3 z>jQOA2(j4EI?e}p5+2hAaVro$GTfl}im7RQoClj2`JJFGGHA6q6NlsJt1~sLCV(Kq zxr5pOAtF|Z6VbucwR|a37ZWSqk#;+iHbR19s~I}>IvepB76xlevO`guo4+TSbOF$o z+?Z}-1iDlJ;&&y9A)U(2zun$$1K>NLaYG@-gp~AB368TQu+N#78M~WfUobT6_-WO- znH~}HdtT!|fg=SN&Pbw1j1KoV&Rpnz?=L~0XNwS3jRbV7E|`_BjX|G#>z+M#{v{OY zpx?6_Y`g^lkMX5B3J4~Wt10Af4Xb=Hbno2$z2R&9NEhtuYFW5#vceSc;4b&a0iW9) z>~wDnc6$5)f6Lb@UanXWR;~hy@vaCfHZ!`$D;3kVGR1gRDpqV}bd4`mESwmw>oLu&9}SP>}c?LA=jT9WSFx9-l-y&0?>D5-l7aUd@KH zX2lR2TJ+nB&pcfbHm%zwP;5Ss^Ipqh#}3Al^*OQZu(!8YaAhOnA~D5=^PDbFd?IG* zkm4t}WY1;U7JjL%sjfTVOf_8I+{cv-IOBR8V^2e4^V6&gnxI4zh?|{hwla@8V_F>( zwh3OeIa`txQ6?{s_-9^BqX27~5^PnhB{&T^^Cacn&X`WeMDkf7(e7b!Q-z3e{7ben zQD@HZx2BcJk*N8=OpHa z%4D){Yc_nIjk_(X4kj~H%OHAO!}sf*E>C@qwLoqAYb6Yb&R}DAUmQ*t?kal;iJ|K8N`zu zf09eS_x}a_>+dJP-i)xv`x7JA2tq7jK~V7inc53u4d=Tp;SJu5uRb5qYSe&aA5wm7 zU1?fk(VB-^PvG-QF7j}GMLYYE4SW^BS4*&1w3W-kBmum&#AjACErE`+FjDteK_Ub+ z!$vf3u_rSPK|mZW_@R{!5`5@#+51J%=SdPl-b$1b#k-sxY+)vxTpxbG^Xr)tpWUq7 zDxuh>4cI;N?(A*KesRSB48P|oDWL@sUL}P007RdlswQKX=Uu0$`D1lSDz|EM?ikeu vbM9Gj{2KrO|Np}`Ofmof00v1!K~w_(!9Oj0EDoc+ literal 0 HcmV?d00001 diff --git a/src/images/icon75.png b/src/images/icon75.png new file mode 100644 index 0000000000000000000000000000000000000000..d7b99b7b49a4239f46cefeb291c10b765e4348ce GIT binary patch literal 1154 zcmV-|1bzF7P)n*32p~MPwVCIN0A)lxh-^*;aqp|z68l+twUb=dBq! z#MTi4)2WNkiXn}L0Z+*y)3EI2`XJ!k)PD0$7I^Am^O{Xk79gh_0m=k;`e`{eM9gof zAgAs~7$cmht^T(s4OAI3ET-k#U5n4YXOcxi;M#UchUp1$xm><~e|qn}%ANzdmzKM% z5wIqe81KwOoWkoGit4z+jAtjo$p9Nz@=id;VAFkBF&W!qi6v2MQQcp$Z=ubL_<})L z5^{&t%W z2KlAIIb6obN1;SB&4wR*dkFu85kmuv8Z>YC>ORq3$ZN;q*fi9~X5hWvUPAk#umNn@ z{Q4>Uf4zb0aoztc@02orp-ydaxi7xS$8*l)`Qpxv}EO5Uqf9{8Kl#LVW? zyc4A(2iMGobmdkX(dJA)`LO$-dHInrHj(Ox?&-XCEDp??`WUv7w`wPUEi1nbV5-Wi zfUt8X_@mH@iCH;+J^xqI2d4nlK}h)ny^&rJK4K?_^SwDr;Arqps0Xj28tu( zm_TRsq5?xxco&5P4F68r9^hUWoNn)11P2#~oYq8tC^n>nA))u#k z$Xgat+hdWd^GE)vUrx6^l(aqn;3Z`E5GFJ<83w&~X}yj=$uFd%Dsbgy=Cj?uet$oU z=W?0eWWcqo{k(Dp=ppbztAW4Rw~%zzzBE8p)*dOt!1=u$G0Tx* + + + + diff --git a/src/pages/login/index.json b/src/pages/login/index.json index a97367d..40df7ae 100644 --- a/src/pages/login/index.json +++ b/src/pages/login/index.json @@ -1,3 +1,4 @@ { + "navigationBarTitleText": "登录", "usingComponents": {} } diff --git a/src/pages/login/index.scss b/src/pages/login/index.scss index d440f29..97d0e5d 100644 --- a/src/pages/login/index.scss +++ b/src/pages/login/index.scss @@ -53,6 +53,7 @@ } } .input { + flex: 1; height: 84rpx; font-size: 32rpx; color: rgba(71, 85, 105, 1); @@ -61,18 +62,37 @@ color: rgba(203, 213, 225, 1); } } - .tip2{ + .tip2 { margin-top: 28rpx; font-size: 28rpx; color: rgba(148, 163, 184, 1); line-height: 42rpx; } - .agreement{ + .agreement { + margin-top: 42rpx; font-size: 28rpx; color: rgba(71, 85, 105, 1); - .high{ + .wx-checkbox-input { + margin-top: -10rpx; + width: 29rpx; + height: 29rpx; + border-radius: 50%; + } + .high { color: rgba(74, 184, 253, 1); } } + .btn { + margin-top: 47rpx; + height: 96rpx; + font-size: 32rpx; + color: rgba(255, 255, 255, 1); + display: flex; + align-items: center; + justify-content: center; + background: linear-gradient(90deg, #9ddffd 0%, #4ab8fd 100%); + box-shadow: 0rpx 15rpx 30rpx -6rpx rgba(74, 172, 219, 0.4); + border-radius: 16rpx 16rpx 16rpx 16rpx; + } } } diff --git a/src/pages/login/index.ts b/src/pages/login/index.ts index ded2028..fbf1963 100644 --- a/src/pages/login/index.ts +++ b/src/pages/login/index.ts @@ -1,8 +1,152 @@ const app = getApp() Page({ - data: {}, - onLoad() {}, + data: { + checked: false, + account: '', + password: '', + loading: false, + }, + + onLoad() { + app.waitLogin({ type: 0 }).then(() => {}) + }, + + /** + * 账号输入 + */ + onAccountInput(e: WechatMiniprogram.Input) { + this.setData({ + account: e.detail.value, + }) + }, + + /** + * 密码输入 + */ + onPasswordInput(e: WechatMiniprogram.Input) { + this.setData({ + password: e.detail.value, + }) + }, + + /** + * 协议勾选 + */ + onCheckboxChange() { + this.setData({ + checked: !this.data.checked, + }) + }, + + /** + * 提交绑定 + */ + onSubmit() { + const { account, password, checked, loading } = this.data + + // 防止重复提交 + if (loading) { + return + } + + // 表单验证 + if (!account) { + wx.showToast({ + title: '请输入学工号', + icon: 'none', + }) + return + } + + if (!password) { + wx.showToast({ + title: '请输入密码', + icon: 'none', + }) + return + } + + if (!checked) { + wx.showToast({ + title: '请阅读并接受隐私保护指引', + icon: 'none', + }) + return + } + + // 开始提交 + this.setData({ loading: true }) + wx.showLoading({ + title: '正在绑定...', + mask: true, + }) + + // 调用 CAS 账号绑定登录接口 + wx.ajax({ + method: 'POST', + url: '/auth/cas-bind', + data: { + account, + password, + openidSession: app.globalData.accessToken, + protocolVersion: 'v1.0', + }, + }) + .then((response: any) => { + wx.hideLoading() + this.setData({ loading: false }) + + const { accessToken, expireIn, needBind, user } = response + + // 存储 accessToken + app.globalData.accessToken = accessToken + app.globalData.tokenExpireIn = expireIn + + // 存储用户信息 + if (user) { + app.globalData.userInfo = user + } + + // 更新 initLoginInfo + app.globalData.initLoginInfo = { + needBind, + user, + } + + // 绑定成功,跳转到目标页面 + wx.showToast({ + title: '绑定成功', + icon: 'success', + }) + + setTimeout(() => { + // 检查是否有来源页面,如果有则返回,否则跳转到首页 + const redirectUrl = app.globalData.loginRedirectUrl + if (redirectUrl) { + // 清除记录的来源页面 + app.globalData.loginRedirectUrl = '' + wx.reLaunch({ + url: `/${redirectUrl}`, + }) + } else { + wx.reLaunch({ + url: '/pages/index/index', + }) + } + }, 1500) + }) + .catch((err: any) => { + wx.hideLoading() + this.setData({ loading: false }) + console.error('CAS 绑定登录请求失败:', err) + wx.showToast({ + title: '网络请求失败', + icon: 'none', + }) + }) + }, + handleGetUserInfo() { wx.getUserProfile({ desc: '用于完善用户资料', diff --git a/src/pages/login/index.wxml b/src/pages/login/index.wxml index fe35598..a88f0e7 100644 --- a/src/pages/login/index.wxml +++ b/src/pages/login/index.wxml @@ -16,15 +16,33 @@ 账号 - + 密码 - + SIC+仅将您的密码用于单次身份验证,不会保存您的密码 - 我已阅读并接受《深职大SIC+小程序隐私保护指引》 + + 我已阅读并接受 + 《深职大SIC+小程序隐私保护指引》 + + 提交 diff --git a/src/pages/schedule/index.json b/src/pages/schedule/index.json new file mode 100644 index 0000000..52e5e1d --- /dev/null +++ b/src/pages/schedule/index.json @@ -0,0 +1,7 @@ +{ + "navigationBarTitleText": "课表", + "usingComponents": { + "van-icon": "@vant/weapp/icon/index", + "svg-icon": "/components/svg-icon/index" + } +} diff --git a/src/pages/schedule/index.scss b/src/pages/schedule/index.scss new file mode 100644 index 0000000..a34ae29 --- /dev/null +++ b/src/pages/schedule/index.scss @@ -0,0 +1,310 @@ +.page-title { + font-size: 32rpx; + color: rgba(30, 41, 59, 1); +} +.page-back { + font-size: 32rpx; + color: rgba(30, 41, 59, 1); +} + +.page { + padding: 0 30rpx; + .page-header { + padding-top: 30rpx; + display: flex; + align-items: center; + gap: 46rpx; + .week-wrap { + flex: 1; + .week { + display: flex; + align-items: center; + gap: 16rpx; + .icon { + width: 42rpx; + height: 42rpx; + } + .content { + font-size: 38rpx; + color: rgba(17, 24, 39, 1); + font-weight: bold; + } + } + .school-year { + margin-top: 8rpx; + font-size: 28rpx; + color: rgba(107, 114, 128, 1); + } + } + .notify { + flex-shrink: 0; + padding: 5rpx 15rpx 5rpx 5rpx; + display: flex; + align-items: center; + gap: 6rpx; + background-color: rgba(247, 248, 250, 1); + border: 1px solid #fff; + border-radius: 50rpx; + font-size: 28rpx; + color: rgba(74, 184, 253, 1); + .icon-wrap { + flex-shrink: 0; + width: 42rpx; + height: 42rpx; + display: flex; + align-items: center; + justify-content: center; + background-color: #fff; + border-radius: 50rpx; + .icon { + width: 22rpx; + height: 28rpx; + } + } + } + .switch-format { + flex-shrink: 0; + padding: 8rpx; + background-color: rgba(247, 248, 250, 1); + border: 1px solid #fff; + border-radius: 16rpx; + display: flex; + .icon-wrap { + width: 50rpx; + height: 50rpx; + display: flex; + align-items: center; + justify-content: center; + border-radius: 12rpx; + .icon { + width: 26rpx; + height: 21rpx; + } + .icon:last-of-type { + display: none; + } + &.active { + background-color: #fff; + .icon { + display: none; + } + .icon:last-of-type { + display: block; + } + } + } + } + } + .calender { + margin-top: 30rpx; + padding: 16rpx; + background: #ffffff; + box-shadow: 0rpx 15rpx 30rpx 0rpx rgba(74, 172, 219, 0.09); + border-radius: 24rpx 24rpx 24rpx 24rpx; + border: 1rpx solid #f7f8fa; + display: flex; + align-content: inherit; + .item { + flex: 1; + padding: 16rpx; + text-align: center; + border-radius: 24rpx; + .week-name { + font-size: 18rpx; + color: rgba(148, 163, 184, 1); + } + .day { + margin-top: 8rpx; + font-size: 28rpx; + color: rgba(17, 24, 39, 1); + line-height: 38rpx; + } + &.active { + background-color: rgba(74, 184, 253, 1); + box-shadow: + 0rpx 8rpx 12rpx -8rpx rgba(74, 184, 253, 0.2), + 0rpx 19rpx 29rpx -6rpx rgba(74, 184, 253, 0.2); + .week-name { + color: #fff; + } + .day { + color: #fff; + } + } + } + } + + .format1 { + margin-top: 18rpx; + .card { + margin-top: 30rpx; + display: flex; + gap: 30rpx; + .aside { + flex-shrink: 0; + padding-top: 15rpx; + display: flex; + flex-direction: column; + align-items: flex-end; + .start { + font-size: 28rpx; + color: rgba(17, 24, 39, 1); + font-weight: bold; + } + .line { + flex-shrink: 0; + width: 1px; + height: 29rpx; + background-color: rgba(148, 163, 184, 1); + } + .end { + font-size: 24rpx; + color: rgba(148, 163, 184, 1); + } + } + .container { + flex: 1; + padding: 33rpx; + border-radius: 24rpx; + border-left: 10rpx solid rgba(171, 89, 248, 1); + background-color: rgba(171, 89, 248, 0.1); + .c-header { + display: flex; + align-items: center; + .title { + font-size: 32rpx; + color: rgba(74, 21, 124, 1); + font-weight: bold; + .step { + padding: 6rpx 16rpx; + font-size: 22rpx; + color: rgba(103, 31, 171, 1); + background-color: rgba(171, 89, 248, 0.2); + border-radius: 242rpx; + } + } + } + .site, + .teacher { + padding-top: 16rpx; + display: flex; + gap: 12rpx; + .icon { + margin-top: 14rpx; + } + .content { + color: rgba(103, 31, 171, 1); + } + } + } + } + } + + .format2 { + } + + .format3 { + /* 外层容器 */ + .schedule-wrap { + border: 1rpx solid #eee; + border-radius: 16rpx; + overflow: hidden; + } + /* 表头横向滚动 */ + .header-scroll { + width: 100%; + } + .header-row { + display: flex; + } + /* 左侧时间列统一宽度 */ + .time-col { + width: 160rpx; + flex-shrink: 0; + } + .header-time { + display: flex; + align-items: center; + justify-content: center; + border-right: 1rpx solid #eee; + border-bottom: 1rpx solid #eee; + color: #999; + } + /* 每一天的列宽统一 */ + .day-col { + width: 120rpx; + flex-shrink: 0; + } + .header-day { + text-align: center; + padding: 16rpx 0; + border-right: 1rpx solid #eee; + border-bottom: 1rpx solid #eee; + } + .header-day .week { + font-size: 26rpx; + color: #666; + } + .header-day .date { + font-size: 30rpx; + margin-top: 4rpx; + } + /* 主体横向+纵向滚动 */ + .body-scroll { + height: calc(100vh - 160rpx); + } + .body-row { + display: flex; + } + /* 左侧课时列表 */ + .body-time { + border-right: 1rpx solid #eee; + } + .time-item { + height: 140rpx; /* 单节高度,和课程网格行高一致 */ + padding: 10rpx; + border-bottom: 1rpx solid #eee; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + color: #666; + font-size: 24rpx; + } + .section-name { + font-weight: 500; + } + .time-range { + font-size: 22rpx; + color: #999; + margin-top: 6rpx; + } + /* 课程网格核心:CSS Grid */ + .grid-container { + display: flex; + } + .grid-day { + display: grid; + grid-template-rows: repeat(9, 140rpx); /* 9节课,每行140rpx */ + border-right: 1rpx solid #eee; + position: relative; + } + /* 课程卡片样式 */ + .course-card { + margin: 8rpx; + border-radius: 8rpx; + padding: 10rpx; + color: #333; + font-size: 24rpx; + } + .course-name { + font-weight: 500; + line-height: 1.4; + } + .course-loc { + margin-top: 8rpx; + font-size: 22rpx; + opacity: 0.8; + } + } +} diff --git a/src/pages/schedule/index.ts b/src/pages/schedule/index.ts new file mode 100644 index 0000000..89d1a56 --- /dev/null +++ b/src/pages/schedule/index.ts @@ -0,0 +1,149 @@ +const _app = getApp() + +Page({ + data: { + scrollX: 0, // 横向滚动偏移量 + isSyncing: false, // 同步锁,防止双向滚动死循环 + todayIndex: 1, // 今天对应的索引(周二=1) + // 左侧9节课时间段 + sectionList: [ + { index: 1, time: '08:30\n09:15' }, + { index: 2, time: '09:20\n10:05' }, + { index: 3, time: '10:25\n11:10' }, + { index: 4, time: '11:15\n12:00' }, + { index: 5, time: '14:00\n14:45' }, + { index: 6, time: '14:50\n15:35' }, + { index: 7, time: '15:45\n16:30' }, + { index: 8, time: '16:35\n17:20' }, + { index: 9, time: '17:30\n18:10' }, + ], + // 周一到周日 日期+课程数据 + weekList: [ + { + week: '一', + date: '6/1', + courseList: [ + { + id: 1, + name: '公共外语II(英语综合)', + loc: '日新楼北401', + start: 1, + rowNum: 2, + bgColor: '#e6f7ff', + borderColor: '#4ab8fd', + textColor: '#4ab8fd', + locColor: '#4ab8fd', + }, + ], + }, + { + week: '二', + date: '6/2', + courseList: [ + { + id: 2, + name: '数据库SQL入门', + loc: '日新楼北401', + start: 3, + rowNum: 2, + bgColor: '#fff0f0', + borderColor: '#ff6b6b', + textColor: '#ff6b6b', + locColor: '#ff6b6b', + }, + { + id: 3, + name: 'BIM建筑信息模型', + loc: '信息楼507机房', + start: 5, + rowNum: 2, + bgColor: '#fff8e6', + borderColor: '#ffa500', + textColor: '#ffa500', + locColor: '#ffa500', + }, + ], + }, + { week: '三', date: '6/3', courseList: [] }, + { + week: '四', + date: '6/4', + courseList: [ + { + id: 4, + name: '(美育课)人工智能伦理与哲学', + loc: '日新楼北301', + start: 3, + rowNum: 2, + bgColor: '#e6fff0', + borderColor: '#52c41a', + textColor: '#52c41a', + locColor: '#52c41a', + }, + ], + }, + { week: '五', date: '6/5', courseList: [] }, + { + week: '六', + date: '6/6', + courseList: [ + { + id: 5, + name: '体育与健康2(健美操)', + loc: 'D6体育馆三', + start: 7, + rowNum: 2, + bgColor: '#e6f0ff', + borderColor: '#4ab8fd', + textColor: '#4ab8fd', + locColor: '#4ab8fd', + }, + ], + }, + { + week: '日', + date: '6/7', + courseList: [ + { + id: 6, + name: '创新思维', + loc: '日新楼北301', + start: 1, + rowNum: 2, + bgColor: '#f5e6ff', + borderColor: '#b37feb', + textColor: '#b37feb', + locColor: '#b37feb', + }, + ], + }, + ], + }, + onLoad() {}, + // 滑动表头时,同步主体滚动 + onHeaderScroll(e) { + if (this.data.isSyncing) return + this.setData({ + isSyncing: true, + scrollX: e.detail.scrollLeft, + }) + // 延迟解锁同步锁,避免双向触发循环 + setTimeout(() => { + this.setData({ isSyncing: false }) + }, 80) + }, + + // 滑动课程主体时,同步表头滚动 + onBodyScroll(e) { + if (this.data.isSyncing) return + this.setData({ + isSyncing: true, + scrollX: e.detail.scrollLeft, + }) + setTimeout(() => { + this.setData({ isSyncing: false }) + }, 80) + }, +}) + +export {} diff --git a/src/pages/schedule/index.wxml b/src/pages/schedule/index.wxml new file mode 100644 index 0000000..a418e39 --- /dev/null +++ b/src/pages/schedule/index.wxml @@ -0,0 +1,142 @@ + + + SIC+ 课表 + + + + + + + 第14周 + + + 2025-2026 第1学期 + + + + + + 提醒我 + + + + + + + + + + + + + + + + 6/1 + + + + + + + 08:00 + + 09:35 + + + + 微积分 (Calculus) + 第1-2节 + + + + 博学楼4教室 + + + + 李老师 + + + + + + + + + 时间 + + + {{item.week}} + {{item.date}} + + + + + + + + + + + 第{{item.index}}节 + {{item.time}} + + + + + + + {{item.name}} + {{item.loc}} + + + + + + + + + + + + + 时间 + + {{item.week}} + {{item.date}} + + + + + + + + + + 第{{item.index}}节 + {{item.time}} + + + + + + {{item.name}} + {{item.loc}} + + + + + + + + diff --git a/typings/index.d.ts b/typings/index.d.ts index fb2a2b0..ee96425 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -1,3 +1,22 @@ +/** + * 用户信息类型定义 + */ +interface UserInfo { + id: number + nickname?: string + avatarUrl?: string + realName?: string + studentNo?: string + role?: 'student' | 'teacher' | 'staff' + collegeId?: number + collegeName?: string + majorId?: number + majorName?: string + classId?: number + className?: string + grade?: string +} + interface IAppOption { globalData: { url?: string @@ -7,13 +26,21 @@ interface IAppOption { Timestamp: number waitBindDoctorId: string - loginState: string initLoginInfo: Partial<{ isLogin: 0 | 1 isReg: 0 | 1 loginType: 1 | 2 + needBind?: boolean + user?: UserInfo }> + // JWT 令牌相关 + accessToken?: string + tokenExpireIn?: number + openidSession?: string // 临时凭证(用于绑定) + userInfo?: UserInfo + loginRedirectUrl?: string // 登录后返回的页面路径 + [propName: string]: any } getUserInfo: (type?: 0 | 1 | 2) => Promise