diff --git a/packages/app/src/app.config.ts b/packages/app/src/app.config.ts
index f2e7ea962..b17f8b5fb 100644
--- a/packages/app/src/app.config.ts
+++ b/packages/app/src/app.config.ts
@@ -32,8 +32,10 @@ export default {
'pages/useAPICheck/index',
'pages/useUpdateManager/index',
'pages/useLaunchOptions/index',
+ 'pages/useUserInfo/index',
'pages/useAccountInfo/index',
'pages/useAuthorize/index',
+ 'pages/useLogin/index',
// network
'pages/useRequest/index',
'pages/useRequest/defaultRequest/index',
diff --git a/packages/app/src/components/DocPage/index.less b/packages/app/src/components/DocPage/index.less
index 43e38d6e0..3e5b706d8 100644
--- a/packages/app/src/components/DocPage/index.less
+++ b/packages/app/src/components/DocPage/index.less
@@ -1,5 +1,6 @@
@import '../../app.less';
@import '../../assets/style/icon.less';
+@import '../../style/mixins.less';
.demo {
background-color: #6190e8;
@@ -21,3 +22,7 @@
.margin {
margin-top: 30px;
}
+
+.ellipsis {
+ .ellipsis();
+}
diff --git a/packages/app/src/constant/index.ts b/packages/app/src/constant/index.ts
index acfbfe2b9..9880a824e 100644
--- a/packages/app/src/constant/index.ts
+++ b/packages/app/src/constant/index.ts
@@ -151,6 +151,10 @@ export const ChildrenList: { [_: string]: APIChildrenItem[] } = {
id: 'useLaunchOptions',
name: 'useLaunchOptions 启动参数',
},
+ {
+ id: 'useUserInfo',
+ name: 'useUserInfo 用户信息',
+ },
{
id: 'useAccountInfo',
name: 'useAccountInfo 账号信息',
diff --git a/packages/app/src/pages/useLogin/index.config.ts b/packages/app/src/pages/useLogin/index.config.ts
new file mode 100644
index 000000000..88999c7a3
--- /dev/null
+++ b/packages/app/src/pages/useLogin/index.config.ts
@@ -0,0 +1,3 @@
+export default {
+ navigationBarTitleText: 'useLogin',
+};
diff --git a/packages/app/src/pages/useLogin/index.tsx b/packages/app/src/pages/useLogin/index.tsx
new file mode 100644
index 000000000..b5fb04d4f
--- /dev/null
+++ b/packages/app/src/pages/useLogin/index.tsx
@@ -0,0 +1,42 @@
+import React, { useCallback } from 'react';
+import { AtNoticebar, AtButton } from 'taro-ui';
+
+import DocPage from '@components/DocPage';
+
+import { useLogin, useModal } from 'taro-hooks';
+
+export default () => {
+ const [login, checkSession] = useLogin();
+ const [show] = useModal({ mask: true, title: '登录信息', showCancel: false });
+
+ const handleSession = useCallback(async () => {
+ let message = '登录状态正常';
+ try {
+ await checkSession();
+ } catch (e) {
+ console.log(e);
+ message = '登录状态失效';
+ }
+ show({ content: message });
+ }, [checkSession, show]);
+
+ const handleLogin = useCallback(() => {
+ login(true)
+ .then((code: string) => show({ content: '获取凭证为: ' + code }))
+ .catch(() => {
+ show({ content: '获取凭证失败' });
+ });
+ }, [login, show]);
+
+ return (
+ <>
+ 该hook仅小程序可用
+
+ 获取凭证
+
+ 检测当前登录状态
+
+
+ >
+ );
+};
diff --git a/packages/app/src/pages/useUserInfo/index.config.ts b/packages/app/src/pages/useUserInfo/index.config.ts
new file mode 100644
index 000000000..d96aa8329
--- /dev/null
+++ b/packages/app/src/pages/useUserInfo/index.config.ts
@@ -0,0 +1,3 @@
+export default {
+ navigationBarTitleText: 'useUserInfo',
+};
diff --git a/packages/app/src/pages/useUserInfo/index.less b/packages/app/src/pages/useUserInfo/index.less
new file mode 100644
index 000000000..eb9c1c193
--- /dev/null
+++ b/packages/app/src/pages/useUserInfo/index.less
@@ -0,0 +1,3 @@
+.userinfo {
+ margin-bottom: 10px;
+}
diff --git a/packages/app/src/pages/useUserInfo/index.tsx b/packages/app/src/pages/useUserInfo/index.tsx
new file mode 100644
index 000000000..748656587
--- /dev/null
+++ b/packages/app/src/pages/useUserInfo/index.tsx
@@ -0,0 +1,70 @@
+import React, { useCallback } from 'react';
+import { AtRadio, AtAvatar, AtButton, AtNoticebar } from 'taro-ui';
+import DocPage from '@components/DocPage';
+
+import { useUserInfo, useLogin } from 'taro-hooks';
+import { UserInfo } from '@tarojs/taro';
+import { View, Text } from '@tarojs/components';
+
+import 'taro-ui/dist/style/components/flex.scss';
+import './index.less';
+
+const MOCK = '1';
+
+const transferOptions = (userInfo: UserInfo) =>
+ Object.entries(userInfo).map(([key, value]) => ({
+ label: key + ':',
+ value: key,
+ desc: JSON.stringify(value),
+ }));
+
+export default () => {
+ const [userInfo, { getUserInfo, getUserProfile }] = useUserInfo();
+ const [login] = useLogin();
+
+ const handleGetUserInfo = useCallback(() => {
+ login(true).then(() => {
+ getUserInfo({ lang: 'zh_CN', withCredentials: true });
+ });
+ }, [login, getUserInfo]);
+
+ return (
+ <>
+ 该hook仅在小程序中使用
+
+
+
+
+
+
+
+ 昵称: {userInfo.nickName}
+
+
+ 性别:{' '}
+ {!userInfo.gender
+ ? '未知'
+ : userInfo.gender === 1
+ ? '男性'
+ : '女性'}
+
+
+
+ 获取用户信息
+
+ getUserProfile({ lang: 'zh_CN', desc: '仅作为小程序展示使用' })
+ }
+ >
+ 获取用户信息(包含敏感)
+
+
+
+ >
+ );
+};
diff --git a/packages/hooks/src/index.ts b/packages/hooks/src/index.ts
index 3feb5dff6..af0808c1f 100644
--- a/packages/hooks/src/index.ts
+++ b/packages/hooks/src/index.ts
@@ -25,7 +25,9 @@ import useAPICheck from './useAPICheck';
import useUpdateManager from './useUpdateManager';
import useLaunchOptions from './useLaunchOptions';
import useAuthorize from './useAuthorize';
+import useUserInfo from './useUserInfo';
import useAccountInfo from './useAccountInfo';
+import useLogin from './useLogin';
// network
import useNetworkType from './useNetworkType';
@@ -87,4 +89,6 @@ export {
useMap,
useAuthorize,
useAccountInfo,
+ useUserInfo,
+ useLogin,
};
diff --git a/packages/hooks/src/useLogin/index.md b/packages/hooks/src/useLogin/index.md
new file mode 100644
index 000000000..7f0e3cf08
--- /dev/null
+++ b/packages/hooks/src/useLogin/index.md
@@ -0,0 +1,46 @@
+---
+title: useLogin
+nav:
+ title: Hooks
+ path: /hooks
+ order: 2
+group:
+ title: 小程序
+ path: /wechat
+---
+
+# useLogin
+
+获取登录凭证, 检查登录状态
+
+## 何时使用
+
+当需要获取登录凭证, 检查登录状态
+
+## API
+
+```jsx | pure
+const [login, checkSession] = useLogin();
+```
+
+## 返回值说明
+
+| 返回值 | 说明 | 类型 |
+| ------------ | ----------------------------------------------------------------------- | ----------------------------------------------------------- |
+| login | 获取登录凭证(若`needCheck`为`true`则自动检测当前登录状态来进行登录操作) | `(needCheck?: boolean) => Promise` |
+| checkSession | 检查登录状态 | `() => Promise` |
+
+## 代码演示
+
+
+
+## Hook 支持度
+
+| 微信小程序 | H5 | ReactNative |
+| :--------: | :-: | :---------: |
+| ✔️ | | |
+
+## FAQ
+
+- [login](https://developers.weixin.qq.com/miniprogram/dev/api/open-api/login/wx.login.html)
+- [checkSession](https://developers.weixin.qq.com/miniprogram/dev/api/open-api/login/wx.checkSession.html)
diff --git a/packages/hooks/src/useLogin/index.ts b/packages/hooks/src/useLogin/index.ts
new file mode 100644
index 000000000..e7542c53f
--- /dev/null
+++ b/packages/hooks/src/useLogin/index.ts
@@ -0,0 +1,59 @@
+import { login, checkSession, General } from '@tarojs/taro';
+import { useCallback } from 'react';
+import { ENV_TYPE } from '@tarojs/taro';
+import useEnv from '../useEnv';
+
+export type ILogin = (needCheck?: boolean) => Promise;
+export type IAction = () => Promise;
+
+function useLogin(): [ILogin, IAction] {
+ const env = useEnv();
+
+ const checkSessionSync = useCallback(() => {
+ return new Promise((resolve, reject) => {
+ if (env === ENV_TYPE.WEAPP) {
+ checkSession({
+ success: resolve,
+ fail: reject,
+ }).catch(reject);
+ } else {
+ reject({ errMsg: 'checkSession:fail' });
+ }
+ });
+ }, [env]);
+
+ const loginSync = useCallback(
+ (needCheck) => {
+ return new Promise((resolve, reject) => {
+ if (env === ENV_TYPE.WEAPP) {
+ const loginAction = () => {
+ login({
+ success: (res) => resolve(res.code),
+ fail: reject,
+ }).catch(reject);
+ };
+ try {
+ if (needCheck) {
+ checkSessionSync()
+ .then(() => {
+ loginAction();
+ })
+ .catch(reject);
+ } else {
+ loginAction();
+ }
+ } catch (e) {
+ reject({ errMsg: 'login:fail', data: e });
+ }
+ } else {
+ reject({ errMsg: 'login:fail' });
+ }
+ });
+ },
+ [env, checkSessionSync],
+ );
+
+ return [loginSync, checkSessionSync];
+}
+
+export default useLogin;
diff --git a/packages/hooks/src/useUserInfo/index.md b/packages/hooks/src/useUserInfo/index.md
new file mode 100644
index 000000000..a4266100e
--- /dev/null
+++ b/packages/hooks/src/useUserInfo/index.md
@@ -0,0 +1,101 @@
+---
+title: useUserInfo
+nav:
+ title: Hooks
+ path: /hooks
+ order: 2
+group:
+ title: 小程序
+ path: /wechat
+---
+
+# useUserInfo
+
+获取用户信息
+
+## 何时使用
+
+当需要获取用户信息展示时
+
+## API
+
+```jsx | pure
+const [userInfo, { getUserInfo, getUserProfile }] = useUserInfo();
+```
+
+## 参数说明
+
+无
+
+## 返回值说明
+
+| 参数 | 类型 | 说明 |
+| -------------- | ------------------------------------------------------------------------------ | ---------------------------------- |
+| userInfo | `UserInfo` | 用户信息对象 |
+| getUserProfile | `(option?: IOption) => Promise` | 获取用户信息(点击生效, 且每次弹窗) |
+| getUserInfo | `(option: IProfileOption) => Promise` | 获取用户信息 |
+
+### IOption
+
+| 参数 | 类型 | 说明 |
+| --------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| lang | `Language` | 显示用户信息的语言 |
+| withCredentials | `boolean` | 是否带上登录态信息。当 withCredentials 为 true 时,要求此前有调用过 wx.login 且登录态尚未过期,此时返回的数据会包含 encryptedData, iv 等敏感信息;当 withCredentials 为 false 时,不要求有登录态,返回的数据不包含 encryptedData, iv 等敏感信息 |
+
+### IProfileOption
+
+| 参数 | 类型 | 说明 |
+| ---- | ---------- | ---------------------------------------------- |
+| lang | `Language` | 显示用户信息的语言 |
+| desc | `string` | 声明获取用户个人信息后的用途,不超过 30 个字符 |
+
+### UserInfo
+
+| 参数 | 类型 | 说明 |
+| ------------- | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| avatarUrl | `string` | 用户头像图片的 URL。URL 最后一个数值代表正方形头像大小(有 0、46、64、96、132 数值可选,0 代表 640x640 的正方形头像,46 表示 46x46 的正方形头像,剩余数值以此类推。默认 132),用户没有头像时该项为空。若用户更换头像,原有头像 URL 将失效。 |
+| city | `string` | 用户所在城市 |
+| country | `string` | 用户所在国家 |
+| gender | `0 | 1 | 2` | 用户性别 |
+| language | `string` | 显示 country,province,city 所用的语言 |
+| nickName | `string` | 用户昵称 |
+| province | `string` | 用户所在省份 |
+| rawData | `string` | 不包括敏感信息的原始数据字符串,用于计算签名 |
+| signature | `string` | 使用 sha1( rawData + sessionkey ) 得到字符串,用于校验用户信息 |
+| encryptedData | `string` | 包括敏感数据在内的完整用户信息的加密数据 |
+| iv | `string` | 加密算法的初始向量 |
+| cloudID | `string` | 敏感数据对应的云 ID |
+
+### Gender
+
+| 参数 | 类型 |
+| ---- | ---- |
+| 0 | 未知 |
+| 1 | 男性 |
+| 2 | 女性 |
+
+### Language
+
+| 参数 | 类型 |
+| ----- | -------- |
+| en | 英文 |
+| zh_CN | 简体中文 |
+| zh_TW | 繁体中文 |
+
+## 代码演示
+
+
+
+## Hook 支持度
+
+| 微信小程序 | H5 | ReactNative |
+| :--------: | :-: | :---------: |
+| ✔️ | | |
+
+## FAQ
+
+### 1. 更多说明
+
+- [getUserInfo](https://developers.weixin.qq.com/miniprogram/dev/api/open-api/user-info/wx.getUserInfo.html)
+- [getUserProfile](https://developers.weixin.qq.com/miniprogram/dev/api/open-api/user-info/wx.getUserProfile.html)
+- [调整说明](https://developers.weixin.qq.com/community/develop/doc/000cacfa20ce88df04cb468bc52801)
diff --git a/packages/hooks/src/useUserInfo/index.ts b/packages/hooks/src/useUserInfo/index.ts
new file mode 100644
index 000000000..259def8d8
--- /dev/null
+++ b/packages/hooks/src/useUserInfo/index.ts
@@ -0,0 +1,129 @@
+import {
+ UserInfo,
+ getUserInfo,
+ getUserProfile,
+ General,
+ ENV_TYPE,
+} from '@tarojs/taro';
+import { useCallback, useState } from 'react';
+import useEnv from '../useEnv';
+
+export interface IUserInfo extends Partial {
+ rawData?: string;
+ signature?: string;
+ encryptedData?: string;
+ iv?: string;
+ cloudID?: string;
+}
+
+export type TLang = 'en' | 'zh_CN' | 'zh_TW';
+
+export interface IOption {
+ withCredentials?: boolean;
+ lang?: TLang;
+}
+
+export interface IProfileOption {
+ lang?: TLang;
+ desc: string;
+}
+
+export type INormalAction = (
+ option?: IOption,
+) => Promise;
+
+export type IProfileAction = (
+ option: IProfileOption,
+) => Promise;
+
+const INITOPTION: IOption = { withCredentials: false, lang: 'en' };
+
+function useUserInfo(): [
+ IUserInfo,
+ { getUserInfo: INormalAction; getUserProfile: IProfileAction },
+] {
+ const [userInfo, setUserInfo] = useState({});
+ const env = useEnv();
+
+ const combineUserInfo = useCallback(
+ (
+ info:
+ | getUserInfo.SuccessCallbackResult
+ | getUserProfile.SuccessCallbackResult,
+ ): IUserInfo => {
+ const { userInfo, rawData, signature, encryptedData, iv, cloudID } =
+ info || {};
+ const painInfo = Object.fromEntries(
+ Object.entries({
+ rawData,
+ signature,
+ encryptedData,
+ iv,
+ cloudID,
+ }).filter((v) => v[1]),
+ );
+ const finallyUserInfo = {
+ ...userInfo,
+ ...painInfo,
+ };
+ setUserInfo(finallyUserInfo);
+ return finallyUserInfo;
+ },
+ [],
+ );
+
+ const getUserInfoAsync = useCallback(
+ (option = INITOPTION) => {
+ return new Promise((resolve, reject) => {
+ if (env === ENV_TYPE.WEAPP) {
+ try {
+ getUserInfo({
+ ...option,
+ success: (res) => {
+ const info = combineUserInfo(res);
+ resolve(info);
+ },
+ fail: reject,
+ }).catch((e) => reject({ errMsg: 'getUserInfo: fail', data: e }));
+ } catch (e) {
+ reject({ errMsg: 'getUserInfo: fail', data: e });
+ }
+ } else {
+ reject({ errMsg: 'getUserInfo: fail' });
+ }
+ });
+ },
+ [combineUserInfo, env],
+ );
+
+ const getUserProfileAsync = useCallback(
+ (option) => {
+ return new Promise((resolve, reject) => {
+ if (env === ENV_TYPE.WEAPP) {
+ try {
+ getUserProfile({
+ ...option,
+ success: (res) => {
+ const info = combineUserInfo(res);
+ resolve(info);
+ },
+ fail: reject,
+ });
+ } catch (e) {
+ reject({ errMsg: 'getUserProfile: fail', data: e });
+ }
+ } else {
+ reject({ errMsg: 'getUserProfile: fail' });
+ }
+ });
+ },
+ [combineUserInfo, env],
+ );
+
+ return [
+ userInfo,
+ { getUserInfo: getUserInfoAsync, getUserProfile: getUserProfileAsync },
+ ];
+}
+
+export default useUserInfo;