diff --git a/.gitignore b/.gitignore index 14ea590..2ccbe46 100644 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1 @@ -# Windows -[Dd]esktop.ini -Thumbs.db -$RECYCLE.BIN/ - -# macOS -.DS_Store -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes - -# Node.js -node_modules/ +/node_modules/ diff --git a/miniprogram/app.json b/miniprogram/app.json index b754380..f6ed19f 100644 --- a/miniprogram/app.json +++ b/miniprogram/app.json @@ -1,5 +1,6 @@ { "pages": [ + "pages/user/login", "pages/personal/personal", "pages/index/index", "pages/logs/logs" diff --git a/miniprogram/app.ts b/miniprogram/app.ts index 8759e49..1f78daf 100644 --- a/miniprogram/app.ts +++ b/miniprogram/app.ts @@ -3,16 +3,9 @@ App({ globalData: {}, onLaunch() { // 展示本地存储能力 - const logs = wx.getStorageSync('logs') || [] - logs.unshift(Date.now()) - wx.setStorageSync('logs', logs) - - // 登录 - // wx.login({ - // success: res => { - // console.log(res.code) - // // 发送 res.code 到后台换取 openId, sessionKey, unionId - // }, - // }) + const token = wx.getStorageSync('user-token') + if (token) { + this.globalData.token = token; + } }, }) \ No newline at end of file diff --git a/miniprogram/app.wxss b/miniprogram/app.wxss new file mode 100644 index 0000000..06c6fc9 --- /dev/null +++ b/miniprogram/app.wxss @@ -0,0 +1,10 @@ +/**app.wxss**/ +.container { + height: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: space-between; + padding: 200rpx 0; + box-sizing: border-box; +} diff --git a/miniprogram/config.ts b/miniprogram/config.ts new file mode 100644 index 0000000..9002e14 --- /dev/null +++ b/miniprogram/config.ts @@ -0,0 +1,18 @@ +type ApplicationConfig = { + api_url: string + env: 'dev' | 'test' | 'prod' +} +const ConfigData = { + dev: { + api_url: 'http://localhost:8001', + env: 'dev' + }, + prod: { + api_url: 'http://localhost:8001', + env: 'prod' + } +} + +const config = ConfigData['dev'] as ApplicationConfig + +export default config \ No newline at end of file diff --git a/miniprogram/pages/index/index.ts b/miniprogram/pages/index/index.ts index 4f909c0..52d84cf 100644 --- a/miniprogram/pages/index/index.ts +++ b/miniprogram/pages/index/index.ts @@ -3,83 +3,47 @@ const app = getApp() Page({ - data: { - motto: 'Hello World', - userInfo: {}, - open_id: '', - hasUserInfo: false, - canIUse: wx.canIUse('button.open-type.getUserInfo'), - canIUseGetUserProfile: false, - canIUseOpenData: wx.canIUse('open-data.type.userAvatarUrl') && wx.canIUse('open-data.type.userNickName') // 如需尝试获取用户信息可改为false - }, - // 事件处理函数 - bindViewTap() { - wx.navigateTo({ - url: '../logs/logs', - }) - }, - onLoad() { - // @ts-ignore - if (wx.getUserProfile) { - this.setData({ - canIUseGetUserProfile: true - }) - } - }, - getUserProfile() { - // 推荐使用wx.getUserProfile获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认,开发者妥善保管用户快速填写的头像昵称,避免重复弹窗 - wx.getUserProfile({ - desc: '展示用户信息', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写 - success: (res) => { - console.log(res) - this.setData({ - userInfo: res.userInfo, - hasUserInfo: true - }) - } - }) - }, - getUserInfo(e: any) { - // 用户授权操作后的回调 - // 并直接返回匿名的用户个人信息 - console.log(e) - this.setData({ - userInfo: e.detail.userInfo, - hasUserInfo: true - }) - }, - user_login() { - wx.login({ - success: ({ code }) => { - if (!code) { - wx.showToast({ - title: '获取授权数据失败' - }) - return; - } - // 发起请求 - wx.request({ - url: 'http://localhost:8080/wechat/login', - data: { code }, - method: 'GET', - success: (result) => { - if (result.statusCode != 200) { - wx.showToast({ - title: '登录失败' - }) - return; - } - this.setData({ - open_id: result.data.toString() - }) - } - }) - }, - fail: () => { - wx.showToast({ - title: '授权失败' - }) - } - }) + data: { + motto: 'Hello World', + userInfo: {}, + hasUserInfo: false, + canIUse: wx.canIUse('button.open-type.getUserInfo'), + canIUseGetUserProfile: false, + canIUseOpenData: wx.canIUse('open-data.type.userAvatarUrl') && wx.canIUse('open-data.type.userNickName') // 如需尝试获取用户信息可改为false + }, + // 事件处理函数 + bindViewTap() { + wx.navigateTo({ + url: '../logs/logs', + }) + }, + onLoad() { + // @ts-ignore + if (wx.getUserProfile) { + this.setData({ + canIUseGetUserProfile: true + }) } + }, + getUserProfile() { + // 推荐使用wx.getUserProfile获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认,开发者妥善保管用户快速填写的头像昵称,避免重复弹窗 + wx.getUserProfile({ + desc: '展示用户信息', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写 + success: (res) => { + console.log(res) + this.setData({ + userInfo: res.userInfo, + hasUserInfo: true + }) + } + }) + }, + getUserInfo(e: any) { + // 不推荐使用getUserInfo获取用户信息,预计自2021年4月13日起,getUserInfo将不再弹出弹窗,并直接返回匿名的用户个人信息 + console.log(e) + this.setData({ + userInfo: e.detail.userInfo, + hasUserInfo: true + }) + } }) diff --git a/miniprogram/pages/index/index.wxml b/miniprogram/pages/index/index.wxml index e758b8f..f00d294 100644 --- a/miniprogram/pages/index/index.wxml +++ b/miniprogram/pages/index/index.wxml @@ -1,27 +1,21 @@ - - - - - {{open_id}} - + {{userInfo.nickName}} + {{motto}} diff --git a/miniprogram/pages/index/index.wxss b/miniprogram/pages/index/index.wxss new file mode 100644 index 0000000..eb64203 --- /dev/null +++ b/miniprogram/pages/index/index.wxss @@ -0,0 +1,19 @@ +/**index.wxss**/ +.userinfo { + display: flex; + flex-direction: column; + align-items: center; + color: #aaa; +} + +.userinfo-avatar { + overflow: hidden; + width: 128rpx; + height: 128rpx; + margin: 20rpx; + border-radius: 50%; +} + +.usermotto { + margin-top: 200px; +} \ No newline at end of file diff --git a/miniprogram/pages/user/login.json b/miniprogram/pages/user/login.json new file mode 100644 index 0000000..8835af0 --- /dev/null +++ b/miniprogram/pages/user/login.json @@ -0,0 +1,3 @@ +{ + "usingComponents": {} +} \ No newline at end of file diff --git a/miniprogram/pages/user/login.ts b/miniprogram/pages/user/login.ts new file mode 100644 index 0000000..be341a7 --- /dev/null +++ b/miniprogram/pages/user/login.ts @@ -0,0 +1,86 @@ +import message from "../../utils/message" +import { getUserProfile, login } from "../../utils/promise-hooks" +import request, { BizError } from "../../utils/request"; + +// pages/user/login.ts +Page({ + + /** + * 页面的初始数据 + */ + data: { + + }, + + /** + * 生命周期函数--监听页面加载 + */ + async onLoad() { + }, + async onGetUserInfo() { + message.showLoading() + try { + const data = await getUserProfile() + const res = await request('/wechat/login', data) + console.log(res) + } finally { + message.hideLoading() + } + }, + async getCode() { + try { + const code = await login() + console.log(res) + } catch (e) { + message.toast(e) + } + }, + /** + * 生命周期函数--监听页面初次渲染完成 + */ + onReady() { + + }, + + /** + * 生命周期函数--监听页面显示 + */ + onShow() { + + }, + + /** + * 生命周期函数--监听页面隐藏 + */ + onHide() { + + }, + + /** + * 生命周期函数--监听页面卸载 + */ + onUnload() { + + }, + + /** + * 页面相关事件处理函数--监听用户下拉动作 + */ + onPullDownRefresh() { + + }, + + /** + * 页面上拉触底事件的处理函数 + */ + onReachBottom() { + + }, + + /** + * 用户点击右上角分享 + */ + onShareAppMessage() { + + } +}) \ No newline at end of file diff --git a/miniprogram/pages/user/login.wxml b/miniprogram/pages/user/login.wxml new file mode 100644 index 0000000..c00e4a7 --- /dev/null +++ b/miniprogram/pages/user/login.wxml @@ -0,0 +1,9 @@ + + + + + + + {{motto}} + + diff --git a/miniprogram/pages/user/login.wxss b/miniprogram/pages/user/login.wxss new file mode 100644 index 0000000..5e8fc4f --- /dev/null +++ b/miniprogram/pages/user/login.wxss @@ -0,0 +1 @@ +/* pages/user/login.wxss */ \ No newline at end of file diff --git a/miniprogram/utils/log.ts b/miniprogram/utils/log.ts new file mode 100644 index 0000000..8a37efa --- /dev/null +++ b/miniprogram/utils/log.ts @@ -0,0 +1,7 @@ +import config from "../config"; + +export default function log(...args: any[]) { + if (config.env !== 'prod') { + console.log(...args) + } +} \ No newline at end of file diff --git a/miniprogram/utils/message.ts b/miniprogram/utils/message.ts new file mode 100644 index 0000000..ca73d54 --- /dev/null +++ b/miniprogram/utils/message.ts @@ -0,0 +1,28 @@ +import { BizError } from "./request"; + +type ToastIcon = 'success' | 'error' | 'none'; +export function toast(message: string|any, icon: ToastIcon = 'none') { + if(message instanceof BizError){ + message = message.message + } + return wx.showToast({ + title: message, + duration: 2000, + icon + }) +} +export function showLoading({ message = '加载中...', mask = true }) { + return wx.showLoading({ + title: message, + mask + }) +} +export function hideLoading(){ + wx.hideLoading() +} + +export default { + toast, + showLoading, + hideLoading, +} \ No newline at end of file diff --git a/miniprogram/utils/promise-hooks.ts b/miniprogram/utils/promise-hooks.ts new file mode 100644 index 0000000..1a7d1af --- /dev/null +++ b/miniprogram/utils/promise-hooks.ts @@ -0,0 +1,65 @@ +import message from "./message" + +/** + * 执行wx.login 返回 用户登录凭证 + * @param timeout + */ +export function login(timeout = 10000) { + return new Promise((resolve, reject) => { + wx.login({ + timeout, // 超时为10s + success: (res) => { + if (res.code) resolve(res.code) + else { + message.toast('登录失败,' + res.errMsg, 'error') + reject('登录失败,' + res.errMsg) + } + }, + fail: (e) => { + message.toast('登录失败,请重新登录', 'error') + reject('登录失败,' + e.errMsg) + } + }) + }) +} +type UserProfileData = { + /** + * 包括敏感数据在内的完整用户信息的加密数据(BASE64) + */ + encryptedData: string + /** + * 加密算法的初始向量(BASE64) + */ + iv: string + /** + * 用户登录凭证(有效期五分钟)。开发者需要在开发者服务器后台调用 auth.code2Session, + * 使用 code 换取 openid、unionid、session_key 等信息 + */ + code: string +} +export function getUserProfile(timeout = 10000) { + return new Promise((resolve, reject) => { + login(timeout).then(code => { + wx.getUserProfile({ + desc: '展示用户信息并参与相关活动', + success: (res) => { + console.log(res) + if (res.errMsg == 'getUserProfile:ok' && res.encryptedData) { + resolve({ + encryptedData: res.encryptedData, + iv: res.iv, + code + }) + } else { + message.toast('登录失败,请重新登录', 'error') + reject('登录失败,' + res.errMsg) + } + }, + fail: (e) => { + message.toast('登录失败,请重新登录', 'error') + reject('登录失败,' + e.errMsg) + } + }) + }).catch(reject) + }); +} \ No newline at end of file diff --git a/miniprogram/utils/request.ts b/miniprogram/utils/request.ts new file mode 100644 index 0000000..d0f9aa9 --- /dev/null +++ b/miniprogram/utils/request.ts @@ -0,0 +1,62 @@ +import config from "../config"; +import log from "./log"; + +export class BizError extends Error { + code: number; + constructor(message: string, code = 500) { + super(message); + this.code = code; + } +} +type ApiResponse = { + code: 0 | number + message: string + data: T +} +type HttpMethod = "GET" | "POST" | "PUT" | "DELETE" + +function request(api: string, data: any = null, method: HttpMethod = 'POST') { + const header: { + [key: string]: string + } = {} + // 判断是否有token + const token = getApp().globalData.token + if (token) { // 如果有token 添加token头 + header.token = token + } + // 对于post请求发送json数据 + if (method === 'POST') { + header['content-type'] = 'application/json' + } + return new Promise((resolve, reject) => { + wx.request>({ + url: config.api_url + api, + data, + header, + method, + success: (res) => { + // 验证http的响应码是否正常 + if (res.statusCode !== 200) { + reject(new BizError('请求数据失败(server)', -1)) + return; + } + if(!res.data){ + reject(new BizError('请求数据失败(empty)', -1)) + return; + } + const result = res.data + // 验证接口是否正确 + if (result.code !== 0) { + reject(new BizError(result.message, result.code)) + return; + } + resolve(result.data) + }, + fail: (e) => { + log(e) + reject(new BizError('请求数据失败(http)', -1)) + } + }) + }) +} +export default request \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..d8f0b16 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,42 @@ +{ + "name": "miniprogram-ts-less-quickstart", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "miniprogram-ts-less-quickstart", + "version": "1.0.0", + "devDependencies": { + "@types/node": "^18.11.9", + "miniprogram-api-typings": "^2.12.0" + } + }, + "node_modules/@types/node": { + "version": "18.11.9", + "resolved": "https://registry.npmmirror.com/@types/node/-/node-18.11.9.tgz", + "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==", + "dev": true + }, + "node_modules/miniprogram-api-typings": { + "version": "2.12.0", + "resolved": "https://registry.npmmirror.com/miniprogram-api-typings/-/miniprogram-api-typings-2.12.0.tgz", + "integrity": "sha512-ibvbqeslVFur0IAvTxLMvsbtvVcMo6gwvOnj0YZHV7aeDLu091VQRrETT2QuiG9P6aZWRcxeNGJChRKVPCp9VQ==", + "dev": true + } + }, + "dependencies": { + "@types/node": { + "version": "18.11.9", + "resolved": "https://registry.npmmirror.com/@types/node/-/node-18.11.9.tgz", + "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==", + "dev": true + }, + "miniprogram-api-typings": { + "version": "2.12.0", + "resolved": "https://registry.npmmirror.com/miniprogram-api-typings/-/miniprogram-api-typings-2.12.0.tgz", + "integrity": "sha512-ibvbqeslVFur0IAvTxLMvsbtvVcMo6gwvOnj0YZHV7aeDLu091VQRrETT2QuiG9P6aZWRcxeNGJChRKVPCp9VQ==", + "dev": true + } + } +} diff --git a/package.json b/package.json index 5f731b7..1255a81 100644 --- a/package.json +++ b/package.json @@ -2,14 +2,12 @@ "name": "miniprogram-ts-less-quickstart", "version": "1.0.0", "description": "", - "scripts": { - }, + "scripts": {}, "keywords": [], "author": "", "license": "", - "dependencies": { - }, "devDependencies": { - "miniprogram-api-typings": "^2.8.3-1" + "@types/node": "^18.11.9", + "miniprogram-api-typings": "^3.6.0" } } diff --git a/project.config.json b/project.config.json index b9be3a4..3f9aa19 100644 --- a/project.config.json +++ b/project.config.json @@ -1,31 +1,30 @@ { - "description": "项目配置文件", - "packOptions": { - "ignore": [], - "include": [] - }, - "miniprogramRoot": "miniprogram/", - "compileType": "miniprogram", - "projectname": "ts-less-demo", - "setting": { - "useCompilerPlugins": [ - "typescript", - "less" - ], - "babelSetting": { - "ignore": [], - "disablePlugins": [], - "outputPath": "" - } - }, - "simulatorType": "wechat", - "simulatorPluginLibVersion": {}, - "condition": {}, - "srcMiniprogramRoot": "miniprogram/", - "appid": "wxafc2a812fe936d88", - "libVersion": "2.27.2", - "editorSetting": { - "tabIndent": "insertSpaces", - "tabSize": 4 + "description": "项目配置文件", + "packOptions": { + "ignore": [], + "include": [] + }, + "miniprogramRoot": "miniprogram/", + "compileType": "miniprogram", + "projectname": "ts-demo", + "setting": { + "useCompilerPlugins": [ + "typescript" + ], + "babelSetting": { + "ignore": [], + "disablePlugins": [], + "outputPath": "" } + }, + "simulatorType": "wechat", + "simulatorPluginLibVersion": {}, + "condition": {}, + "srcMiniprogramRoot": "miniprogram/", + "appid": "wxafc2a812fe936d88", + "libVersion": "2.27.3", + "editorSetting": { + "tabIndent": "insertSpaces", + "tabSize": 2 + } } \ No newline at end of file diff --git a/project.private.config.json b/project.private.config.json index 93eac2c..f7e3777 100644 --- a/project.private.config.json +++ b/project.private.config.json @@ -1,9 +1,8 @@ { - "description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html", - "projectname": "PointApp", - "setting": { - "compileHotReLoad": true, - "urlCheck": false - }, - "libVersion": "2.23.0" + "description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html", + "projectname": "userlogin-demo", + "setting": { + "compileHotReLoad": true, + "urlCheck": false + } } \ No newline at end of file diff --git a/typings/index.d.ts b/typings/index.d.ts index 3ee60c8..a21af67 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -3,6 +3,7 @@ interface IAppOption { globalData: { userInfo?: WechatMiniprogram.UserInfo, + token?: string } userInfoReadyCallback?: WechatMiniprogram.GetUserInfoSuccessCallback, } \ No newline at end of file