diff --git a/.eslintrc.cjs b/.eslintrc.cjs index a7419e37827f20c94a15129b8d308bac6426f151..72fd5637df5474bc26f26ba072b3a29b75e06097 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -74,8 +74,9 @@ module.exports = { '@typescript-eslint/no-unused-vars': 'off', // 避免 `eslint` 对于 `typescript` 函数重载的误报 'no-redeclare': 'off', - '@typescript-eslint/no-redeclare': 'error', - 'prettier/prettier': 'off' // Andy:默认关闭 prettier 的 ESLint 校验,因为我们使用的是 IDE 的 Prettier 插件 + '@typescript-eslint/no-redeclare': 'off', + 'prettier/prettier': 'off', // Andy:默认关闭 prettier 的 ESLint 校验,因为我们使用的是 IDE 的 Prettier 插件 + 'no-async-promise-executor': 'off' }, // eslint-import-resolver-typescript 插件,@see https://www.npmjs.com/package/eslint-import-resolver-typescript settings: { diff --git a/src/interceptors/request.ts b/src/interceptors/request.ts index 9a4ab280ed139c20ec00243ae13d02fd862b614e..fd3c5c3e378dcb5280ffb4e283736f70a82668f4 100644 --- a/src/interceptors/request.ts +++ b/src/interceptors/request.ts @@ -5,12 +5,18 @@ import { getEvnBaseUrl } from '@/utils' import { getAccessToken, getTenantId } from '@/utils/auth' const tenantEnable = import.meta.env.VITE_APP_TENANT_ENABLE -const whiteList: string[] = ['/login', '/refresh-token', '/system/tenant/get-id-by-name'] +const whiteList: string[] = [ + '/login', + '/refresh-token', + '/system/tenant/get-id-by-name' +] export type CustomRequestOptions = UniApp.RequestOptions & { query?: Record + params?: Record /** 出错时是否隐藏错误提示 */ hideErrorToast?: boolean + custom?: Recordable } & IUniUploadFileOptions // 添加uni.uploadFile参数类型 // 请求基准地址 @@ -50,7 +56,7 @@ const httpInterceptor = { // 2. (可选)添加小程序端请求头标识 options.header = { platform, // 可选,与 uniapp 定义的平台一致,告诉后台来源 - ...options.header, + ...options.header } // 3. 添加 token 请求头标识 // const userStore = useUserStore() @@ -60,7 +66,9 @@ const httpInterceptor = { // } let isToken = (options!.header || {}).isToken === false - isToken = whiteList.some((allowUrl) => options!.url.indexOf(allowUrl) !== -1) + isToken = whiteList.some( + (allowUrl) => options!.url.indexOf(allowUrl) !== -1 + ) if (!isToken && getAccessToken()) { // 能够获取的到accessToken并且不是白名单 @@ -72,7 +80,7 @@ const httpInterceptor = { const tenantId = getTenantId() if (tenantId) options.header['tenant-id'] = tenantId } - }, + } } export const requestInterceptor = { @@ -81,5 +89,5 @@ export const requestInterceptor = { uni.addInterceptor('request', httpInterceptor) // 拦截 uploadFile 文件上传 uni.addInterceptor('uploadFile', httpInterceptor) - }, + } } diff --git a/src/service/member/auth.ts b/src/service/member/auth.ts new file mode 100644 index 0000000000000000000000000000000000000000..3d7919937b814b34ef8f3c2b3f7fe75a1e758071 --- /dev/null +++ b/src/service/member/auth.ts @@ -0,0 +1,132 @@ +import { http } from '@/utils/http' + +const AuthUtil = { + // 使用手机 + 密码登录 + login: (data) => { + return http({ + url: '/member/auth/login', + method: 'POST', + data, + custom: { + showSuccess: true, + loadingMsg: '登录中', + successMsg: '登录成功' + } + }) + }, + // 使用手机 + 验证码登录 + smsLogin: (data) => { + return http({ + url: '/member/auth/sms-login', + method: 'POST', + data, + custom: { + showSuccess: true, + loadingMsg: '登录中', + successMsg: '登录成功' + } + }) + }, + // 发送手机验证码 + sendSmsCode: (mobile, scene) => { + return http({ + url: '/member/auth/send-sms-code', + method: 'POST', + data: { + mobile, + scene + }, + custom: { + loadingMsg: '发送中', + showSuccess: true, + successMsg: '发送成功' + } + }) + }, + // 登出系统 + logout: () => { + return http({ + url: '/member/auth/logout', + method: 'POST' + }) + }, + // 刷新令牌 + refreshToken: (refreshToken) => { + return http({ + url: '/member/auth/refresh-token', + method: 'POST', + params: { + refreshToken + }, + custom: { + loading: false, // 不用加载中 + showError: false // 不展示错误提示 + } + }) + }, + // 社交授权的跳转 + socialAuthRedirect: (type, redirectUri) => { + return http({ + url: '/member/auth/social-auth-redirect', + method: 'GET', + params: { + type, + redirectUri + }, + custom: { + showSuccess: true, + loadingMsg: '登陆中' + } + }) + }, + // 社交快捷登录 + socialLogin: (type, code, state) => { + return http({ + url: '/member/auth/social-login', + method: 'POST', + data: { + type, + code, + state + }, + custom: { + showSuccess: true, + loadingMsg: '登陆中' + } + }) + }, + // 微信小程序的一键登录 + weixinMiniAppLogin: (phoneCode, loginCode, state) => { + return http({ + url: '/member/auth/weixin-mini-app-login', + method: 'POST', + data: { + phoneCode, + loginCode, + state + }, + custom: { + showSuccess: true, + loadingMsg: '登陆中', + successMsg: '登录成功' + } + }) + }, + // 创建微信 JS SDK 初始化所需的签名 + createWeixinMpJsapiSignature: (url) => { + return http({ + url: '/member/auth/create-weixin-jsapi-signature', + method: 'POST', + params: { + url + }, + custom: { + showError: false, + showLoading: false + } + }) + } + // +} + +export default AuthUtil diff --git a/src/service/member/social.ts b/src/service/member/social.ts new file mode 100644 index 0000000000000000000000000000000000000000..810d1999de1d4bd7f75c587fababce87bc58cdb3 --- /dev/null +++ b/src/service/member/social.ts @@ -0,0 +1,76 @@ +import { http } from '@/utils/http' + +const SocialApi = { + // 获得社交用户 + getSocialUser: (type) => { + return http({ + url: '/member/social-user/get', + method: 'GET', + params: { + type + }, + custom: { + showLoading: false + } + }) + }, + // 社交绑定 + socialBind: (type, code, state) => { + return http({ + url: '/member/social-user/bind', + method: 'POST', + data: { + type, + code, + state + }, + custom: { + custom: { + showSuccess: true, + loadingMsg: '绑定中', + successMsg: '绑定成功' + } + } + }) + }, + // 社交绑定 + socialUnbind: (type, openid) => { + return http({ + url: '/member/social-user/unbind', + method: 'DELETE', + data: { + type, + openid + }, + custom: { + showLoading: false, + loadingMsg: '解除绑定', + successMsg: '解绑成功' + } + }) + }, + // 获取订阅消息模板列表 + getSubscribeTemplateList: () => + http({ + url: '/member/social-user/get-subscribe-template-list', + method: 'GET', + custom: { + showError: false, + showLoading: false + } + }), + // 获取微信小程序码 + getWxaQrcode: async (path, query) => { + return await http({ + url: '/member/social-user/wxa-qrcode', + method: 'POST', + data: { + scene: query, + path, + checkPath: false // TODO 开发环境暂不检查 path 是否存在 + } + }) + } +} + +export default SocialApi diff --git a/src/service/member/user.ts b/src/service/member/user.ts new file mode 100644 index 0000000000000000000000000000000000000000..9f348e4ab2146f3ae82c5706f02784c0e26ac15d --- /dev/null +++ b/src/service/member/user.ts @@ -0,0 +1,84 @@ +import { http } from '@/utils/http' + +const UserApi = { + // 获得基本信息 + getUserInfo: () => { + return http({ + url: '/member/user/get', + method: 'GET', + custom: { + showLoading: false, + auth: true + } + }) + }, + // 修改基本信息 + updateUser: (data) => { + return http({ + url: '/member/user/update', + method: 'PUT', + data, + custom: { + auth: true, + showSuccess: true, + successMsg: '更新成功' + } + }) + }, + // 修改用户手机 + updateUserMobile: (data) => { + return http({ + url: '/member/user/update-mobile', + method: 'PUT', + data, + custom: { + loadingMsg: '验证中', + showSuccess: true, + successMsg: '修改成功' + } + }) + }, + // 基于微信小程序的授权码,修改用户手机 + updateUserMobileByWeixin: (code) => { + return http({ + url: '/member/user/update-mobile-by-weixin', + method: 'PUT', + data: { + code + }, + custom: { + showSuccess: true, + loadingMsg: '获取中', + successMsg: '修改成功' + } + }) + }, + // 修改密码 + updateUserPassword: (data) => { + return http({ + url: '/member/user/update-password', + method: 'PUT', + data, + custom: { + loadingMsg: '验证中', + showSuccess: true, + successMsg: '修改成功' + } + }) + }, + // 重置密码 + resetUserPassword: (data) => { + return http({ + url: '/member/user/reset-password', + method: 'PUT', + data, + custom: { + loadingMsg: '验证中', + showSuccess: true, + successMsg: '修改成功' + } + }) + } +} + +export default UserApi diff --git a/src/types/auto-import.d.ts b/src/types/auto-import.d.ts index 255cb5bea51b9d225f751de2359909feda8eeb3b..b5c1bff877f014e494ae747f7294a66d162849e9 100644 --- a/src/types/auto-import.d.ts +++ b/src/types/auto-import.d.ts @@ -94,7 +94,7 @@ declare global { // for type re-export declare global { // @ts-ignore - export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue' + export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue' import('vue') } // for vue template auto import @@ -156,6 +156,7 @@ declare module 'vue' { readonly onUnload: UnwrapRef readonly onUnmounted: UnwrapRef readonly onUpdated: UnwrapRef + readonly onWatcherCleanup: UnwrapRef readonly provide: UnwrapRef readonly reactive: UnwrapRef readonly readonly: UnwrapRef @@ -173,91 +174,11 @@ declare module 'vue' { readonly useAttrs: UnwrapRef readonly useCssModule: UnwrapRef readonly useCssVars: UnwrapRef + readonly useId: UnwrapRef + readonly useModel: UnwrapRef readonly useRequest: UnwrapRef readonly useSlots: UnwrapRef - readonly useUpload: UnwrapRef - readonly watch: UnwrapRef - readonly watchEffect: UnwrapRef - readonly watchPostEffect: UnwrapRef - readonly watchSyncEffect: UnwrapRef - } -} -declare module '@vue/runtime-core' { - interface GlobalComponents {} - interface ComponentCustomProperties { - readonly EffectScope: UnwrapRef - readonly computed: UnwrapRef - readonly createApp: UnwrapRef - readonly customRef: UnwrapRef - readonly defineAsyncComponent: UnwrapRef - readonly defineComponent: UnwrapRef - readonly effectScope: UnwrapRef - readonly getCurrentInstance: UnwrapRef - readonly getCurrentScope: UnwrapRef - readonly h: UnwrapRef - readonly inject: UnwrapRef - readonly isProxy: UnwrapRef - readonly isReactive: UnwrapRef - readonly isReadonly: UnwrapRef - readonly isRef: UnwrapRef - readonly markRaw: UnwrapRef - readonly nextTick: UnwrapRef - readonly onActivated: UnwrapRef - readonly onAddToFavorites: UnwrapRef - readonly onBackPress: UnwrapRef - readonly onBeforeMount: UnwrapRef - readonly onBeforeUnmount: UnwrapRef - readonly onBeforeUpdate: UnwrapRef - readonly onDeactivated: UnwrapRef - readonly onError: UnwrapRef - readonly onErrorCaptured: UnwrapRef - readonly onHide: UnwrapRef - readonly onLaunch: UnwrapRef - readonly onLoad: UnwrapRef - readonly onMounted: UnwrapRef - readonly onNavigationBarButtonTap: UnwrapRef - readonly onNavigationBarSearchInputChanged: UnwrapRef - readonly onNavigationBarSearchInputClicked: UnwrapRef - readonly onNavigationBarSearchInputConfirmed: UnwrapRef - readonly onNavigationBarSearchInputFocusChanged: UnwrapRef - readonly onPageNotFound: UnwrapRef - readonly onPageScroll: UnwrapRef - readonly onPullDownRefresh: UnwrapRef - readonly onReachBottom: UnwrapRef - readonly onReady: UnwrapRef - readonly onRenderTracked: UnwrapRef - readonly onRenderTriggered: UnwrapRef - readonly onResize: UnwrapRef - readonly onScopeDispose: UnwrapRef - readonly onServerPrefetch: UnwrapRef - readonly onShareAppMessage: UnwrapRef - readonly onShareTimeline: UnwrapRef - readonly onShow: UnwrapRef - readonly onTabItemTap: UnwrapRef - readonly onThemeChange: UnwrapRef - readonly onUnhandledRejection: UnwrapRef - readonly onUnload: UnwrapRef - readonly onUnmounted: UnwrapRef - readonly onUpdated: UnwrapRef - readonly provide: UnwrapRef - readonly reactive: UnwrapRef - readonly readonly: UnwrapRef - readonly ref: UnwrapRef - readonly resolveComponent: UnwrapRef - readonly shallowReactive: UnwrapRef - readonly shallowReadonly: UnwrapRef - readonly shallowRef: UnwrapRef - readonly toRaw: UnwrapRef - readonly toRef: UnwrapRef - readonly toRefs: UnwrapRef - readonly toValue: UnwrapRef - readonly triggerRef: UnwrapRef - readonly unref: UnwrapRef - readonly useAttrs: UnwrapRef - readonly useCssModule: UnwrapRef - readonly useCssVars: UnwrapRef - readonly useRequest: UnwrapRef - readonly useSlots: UnwrapRef + readonly useTemplateRef: UnwrapRef readonly useUpload: UnwrapRef readonly watch: UnwrapRef readonly watchEffect: UnwrapRef diff --git a/src/utils/is.ts b/src/utils/is.ts index 0384c4762560271f8048b7b0f83d61f5ee60ebcd..146497ff670b6611afbbf5b5be19bdcc38de101a 100644 --- a/src/utils/is.ts +++ b/src/utils/is.ts @@ -123,3 +123,12 @@ export const isImgPath = (path: string): boolean => { export const isEmptyVal = (val: any): boolean => { return val === '' || val === null || val === undefined } + +export function isWxBrowser() { + const ua = navigator.userAgent.toLowerCase() + if (ua.match(/MicroMessenger/i).includes('micromessenger')) { + return true + } else { + return false + } +} diff --git a/src/utils/libs/sdk-h5-weixin.ts b/src/utils/libs/sdk-h5-weixin.ts new file mode 100644 index 0000000000000000000000000000000000000000..11de4007a70cf5e2714e5db7c0b9dc422bfa8a03 --- /dev/null +++ b/src/utils/libs/sdk-h5-weixin.ts @@ -0,0 +1,199 @@ +/** + * 本模块封装微信浏览器下的一些方法。 + * 更多微信网页开发sdk方法,详见:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html + * 有 the permission value is offline verifying 报错请参考 @see https://segmentfault.com/a/1190000042289419 解决 + */ + +import jweixin from 'weixin-js-sdk' +import * as $helper from '@/utils/common' +import AuthUtil from '@/service/member/auth' + +let configSuccess = false + +export default { + // 判断是否在微信中 + isWechat() { + const ua = window.navigator.userAgent.toLowerCase() + // noinspection EqualityComparisonWithCoercionJS + return ua.match(/micromessenger/i).includes('micromessenger') + }, + + isReady(api) { + jweixin.ready(api) + }, + + // 初始化 JSSDK + async init(callback) { + if (!this.isWechat()) { + $helper.toast('请使用微信网页浏览器打开') + return + } + + // 调用后端接口,获得 JSSDK 初始化所需的签名 + const url = location.href.split('#')[0] + const { code, data } = await AuthUtil.createWeixinMpJsapiSignature(url) + if (code === 0) { + jweixin.config({ + debug: false, + appId: data.appId, + timestamp: data.timestamp, + nonceStr: data.nonceStr, + signature: data.signature, + jsApiList: [ + 'chooseWXPay', + 'openLocation', + 'getLocation', + 'updateTimelineShareData', + 'scanQRCode' + ], // TODO 芋艿:后续可以设置更多权限; + openTagList: data.openTagList + }) + } + + // 监听结果 + configSuccess = true + jweixin.error((err) => { + configSuccess = false + console.error('微信 JSSDK 初始化失败', err) + // $helper.toast('微信JSSDK:' + err.errMsg); + }) + jweixin.ready(() => { + if (configSuccess) { + console.log('微信 JSSDK 初始化成功') + } + }) + + // 回调 + if (callback) { + callback(data) + } + }, + + // 在需要定位页面调用 TODO 芋艿:未测试 + getLocation(callback) { + this.isReady(() => { + jweixin.getLocation({ + type: 'gcj02', // 默认为wgs84的gps坐标,如果要返回直接给openLocation用的火星坐标,可传入'gcj02' + success: function (res) { + callback(res) + }, + fail: function (res) { + console.log( + '%c微信H5sdk,getLocation失败:', + 'color:green;background:yellow' + ) + } + }) + }) + }, + + // 获取微信收货地址 + openAddress(callback) { + this.isReady(() => { + jweixin.openAddress({ + success: function (res) { + callback.success && callback.success(res) + }, + fail: function (err) { + callback.error && callback.error(err) + console.log( + '%c微信H5sdk,openAddress失败:', + 'color:green;background:yellow' + ) + }, + complete: function (res) {} + }) + }) + }, + + // 微信扫码 TODO 芋艿:未测试 + scanQRCode(callback) { + this.isReady(() => { + jweixin.scanQRCode({ + needResult: 1, // 默认为0,扫描结果由微信处理,1则直接返回扫描结果, + scanType: ['qrCode', 'barCode'], // 可以指定扫二维码还是一维码,默认二者都有 + success: function (res) { + callback(res) + }, + fail: function (res) { + console.log( + '%c微信H5sdk,scanQRCode失败:', + 'color:green;background:yellow' + ) + } + }) + }) + }, + + // 更新微信分享信息 TODO 芋艿:未测试 + updateShareInfo(data, callback = null) { + this.isReady(() => { + const shareData = { + title: data.title, + desc: data.desc, + link: data.link, + imgUrl: data.image, + success: function (res) { + if (callback) { + callback(res) + } + // 分享后的一些操作,比如分享统计等等 + }, + cancel: function (res) {} + } + + // 新版 分享聊天api + jweixin.updateAppMessageShareData(shareData) + // 新版 分享到朋友圈api + jweixin.updateTimelineShareData(shareData) + }) + }, + + // 打开坐标位置 TODO 芋艿:未测试 + openLocation(data, callback) { + this.isReady(() => { + jweixin.openLocation({ + ...data, + success: function (res) { + console.log(res) + } + }) + }) + }, + + // 选择图片 TODO 芋艿:未测试 + chooseImage(callback) { + this.isReady(() => { + jweixin.chooseImage({ + count: 1, + sizeType: ['compressed'], + sourceType: ['album'], + success: function (rs) { + callback(rs) + } + }) + }) + }, + + // 微信支付 + wxpay(data, callback) { + this.isReady(() => { + jweixin.chooseWXPay({ + timestamp: data.timeStamp, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符 + nonceStr: data.nonceStr, // 支付签名随机串,不长于 32 位 + package: data.packageValue, // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=\*\*\*) + signType: data.signType, // 签名方式,默认为'SHA1',使用新版支付需传入'MD5' + paySign: data.paySign, // 支付签名 + success: function (res) { + callback.success && callback.success(res) + }, + fail: function (err) { + callback.fail && callback.fail(err) + }, + cancel: function (err) { + callback.cancel && callback.cancel(err) + } + }) + }) + } +} diff --git a/src/utils/platform/index.ts b/src/utils/platform/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..0d35fa755e7e73b93137ad8433de4ba7dfd854a3 --- /dev/null +++ b/src/utils/platform/index.ts @@ -0,0 +1,133 @@ +/** + * Shopro 第三方平台功能聚合 + * @version 1.0.3 + * @author xiaohong + * @param {String} name - 厂商+平台名称 + * @param {String} provider - 厂商 + * @param {String} platform - 平台名称 + * @param {String} os - 系统型号 + * @param {Object} device - 设备信息 + */ + +import { isEmpty } from 'lodash-es' +// #ifdef H5 +import { isWxBrowser } from '@/utils/is' +// #endif +import wechat from './provider/wechat/index.js' +import apple from './provider/apple' + +const device = uni.getSystemInfoSync() + +const os = device.platform + +let name = '' +let provider = '' +let platform = '' +let isWechatInstalled = true + +// #ifdef H5 +if (isWxBrowser()) { + name = 'WechatOfficialAccount' + provider = 'wechat' + platform = 'officialAccount' +} else { + name = 'H5' + platform = 'h5' +} +// #endif + +// #ifdef APP-PLUS +name = 'App' +platform = 'openPlatform' +// 检查微信客户端是否安装,否则AppleStore会因此拒绝上架 +if (os === 'ios') { + isWechatInstalled = plus.ios.import('WXApi').isWXAppInstalled() +} +// #endif + +// #ifdef MP-WEIXIN +name = 'WechatMiniProgram' +platform = 'miniProgram' +provider = 'wechat' +// #endif + +if (isEmpty(name)) { + uni.showToast({ + title: '暂不支持该平台', + icon: 'none' + }) +} + +// 加载当前平台前置行为 +const load = () => { + if (provider === 'wechat') { + wechat.load() + } +} + +// 使用厂商独占sdk name = 'wechat' | 'alipay' | 'apple' +const useProvider = (_provider = '') => { + if (_provider === '') _provider = provider + if (_provider === 'wechat') return wechat + if (_provider === 'apple') return apple +} + +/** + * 检查更新 (只检查小程序和App) + * @param {Boolean} silence - 静默检查 + */ +const checkUpdate = (silence = false) => { + // #ifdef MP-WEIXIN + useProvider().checkUpdate(silence) + // #endif + + // #ifdef APP-PLUS + // TODO: 热更新 + // #endif +} + +/** + * 检查网络 + * @param {Boolean} silence - 静默检查 + */ +async function checkNetwork() { + const networkStatus = await uni.getNetworkType() + if (networkStatus.networkType === 'none') { + return Promise.resolve(false) + } + return Promise.resolve(true) +} + +// 标题栏高度 +const getNavBar = () => { + return device.statusBarHeight + 44 +} +const navbar = getNavBar() + +function getLandingPage() { + let page = '' + // #ifdef H5 + page = location.href.split('?')[0] + // #endif + return page +} + +// 设置ios+公众号网页落地页 解决微信sdk签名问题 +const landingPage = getLandingPage() + +const _platform = { + name, + device, + os, + provider, + platform, + useProvider, + checkUpdate, + checkNetwork, + load, + navbar, + landingPage, + isWechatInstalled +} + +export default _platform diff --git a/src/utils/platform/provider/apple/app.ts b/src/utils/platform/provider/apple/app.ts new file mode 100644 index 0000000000000000000000000000000000000000..00a7d09f0dfb151aefa562151b31612bf216fd03 --- /dev/null +++ b/src/utils/platform/provider/apple/app.ts @@ -0,0 +1,36 @@ +// import third from '@/sheep/api/third'; +// TODO 芋艿:等后面搞 App 再弄 + +const login = () => { + return new Promise(async (resolve, reject) => { + const loginRes = await uni.login({ + provider: 'apple', + success: () => { + uni.getUserInfo({ + provider: 'apple', + success: async (res) => { + if (res.errMsg === 'getUserInfo:ok') { + const payload = res.userInfo + // const { error } = await third.apple.login({ + // payload, + // shareInfo: uni.getStorageSync('shareLog') || {} + // }) + // if (error === 0) { + // resolve(true) + // } else { + // resolve(false) + // } + } + } + }) + } + // fail: (err) => { + // resolve(false) + // } + }) + }) +} + +export default { + login +} diff --git a/src/utils/platform/provider/apple/index.ts b/src/utils/platform/provider/apple/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..805edde6f4814cef14940b5a74dd1a791c96cc0c --- /dev/null +++ b/src/utils/platform/provider/apple/index.ts @@ -0,0 +1,9 @@ +// #ifdef APP-PLUS +import service from './app' +// #endif + +let apple = {} +if (typeof service !== 'undefined') { + apple = service +} +export default apple diff --git a/src/utils/platform/provider/wechat/index.ts b/src/utils/platform/provider/wechat/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..f34405b5a99b090004091acbd1260a01f90bd11a --- /dev/null +++ b/src/utils/platform/provider/wechat/index.ts @@ -0,0 +1,15 @@ +// #ifdef H5 +import service from './officialAccount' +// #endif + +// #ifdef MP-WEIXIN +import service from './miniProgram' +// #endif + +// #ifdef APP-PLUS +import service from './openPlatform' +// #endif + +const wechat = service + +export default wechat diff --git a/src/utils/platform/provider/wechat/miniProgram.ts b/src/utils/platform/provider/wechat/miniProgram.ts new file mode 100644 index 0000000000000000000000000000000000000000..d13b11f2fa2dcb273fc75c36da989d0875ff1cea --- /dev/null +++ b/src/utils/platform/provider/wechat/miniProgram.ts @@ -0,0 +1,225 @@ +import AuthUtil from '@/service/member/auth' +import SocialApi from '@/service/member/social' +import UserApi from '@/service/member/user' + +const socialType = 34 // 社交类型 - 微信小程序 + +let subscribeEventList = [] + +// 加载微信小程序 +function load() { + checkUpdate() + getSubscribeTemplate() +} + +// 微信小程序静默授权登陆 +const login = async () => { + return new Promise(async (resolve, reject) => { + // 1. 获得微信 code + const codeResult = await uni.login() + if (codeResult.errMsg !== 'login:ok') { + return resolve(false) + } + + // 2. 社交登录 + const loginResult = await AuthUtil.socialLogin( + socialType, + codeResult.code, + 'default' + ) + if (loginResult.code === 0) { + setOpenid(loginResult.data.openid) + return resolve(true) + } else { + return resolve(false) + } + }) +} + +// 微信小程序手机号授权登陆 +const mobileLogin = async (e) => { + return new Promise(async (resolve, reject) => { + if (e.errMsg !== 'getPhoneNumber:ok') { + return resolve(false) + } + + // 1. 获得微信 code + const codeResult = await uni.login() + if (codeResult.errMsg !== 'login:ok') { + return resolve(false) + } + + // 2. 一键登录 + const loginResult = await AuthUtil.weixinMiniAppLogin( + e.code, + codeResult.code, + 'default' + ) + if (loginResult.code === 0) { + setOpenid(loginResult.data.openid) + return resolve(true) + } else { + return resolve(false) + } + }) +} + +// 微信小程序绑定 +const bind = () => { + return new Promise(async (resolve, reject) => { + // 1. 获得微信 code + const codeResult = await uni.login() + if (codeResult.errMsg !== 'login:ok') { + return resolve(false) + } + + // 2. 绑定账号 + const bindResult = await SocialApi.socialBind( + socialType, + codeResult.code, + 'default' + ) + if (bindResult.code === 0) { + setOpenid(bindResult.data) + return resolve(true) + } else { + return resolve(false) + } + }) +} + +// 微信小程序解除绑定 +const unbind = async (openid) => { + const { code } = await SocialApi.socialUnbind(socialType, openid) + return code === 0 +} + +// 绑定用户手机号 +const bindUserPhoneNumber = (e) => { + return new Promise(async (resolve, reject) => { + const { code } = await UserApi.updateUserMobileByWeixin(e.code) + if (code === 0) { + resolve(true) + } + resolve(false) + }) +} + +// 设置 openid 到本地存储,目前只有 pay 支付时会使用 +function setOpenid(openid) { + uni.setStorageSync('openid', openid) +} + +// 获得 openid +async function getOpenid(force = false) { + let openid = uni.getStorageSync('openid') + if (!openid && force) { + const info = await getInfo() + if (info && info.openid) { + openid = info.openid + setOpenid(openid) + } + } + return openid +} + +// 获得社交信息 +async function getInfo() { + const { code, data } = await SocialApi.getSocialUser(socialType) + if (code !== 0) { + return undefined + } + return data +} + +// ========== 非登录相关的逻辑 ========== + +// 小程序更新 +const checkUpdate = async (silence = true) => { + if (uni.canIUse('getUpdateManager')) { + const updateManager = uni.getUpdateManager() + updateManager.onCheckForUpdate(function (res) { + // 请求完新版本信息的回调 + if (res.hasUpdate) { + updateManager.onUpdateReady(function () { + uni.showModal({ + title: '更新提示', + content: '新版本已经准备好,是否重启应用?', + success: function (res) { + if (res.confirm) { + // 新的版本已经下载好,调用 applyUpdate 应用新版本并重启 + updateManager.applyUpdate() + } + } + }) + }) + updateManager.onUpdateFailed(function () { + // 新的版本下载失败 + uni.showModal({ + title: '已经有新版本了哟~', + content: '新版本已经上线啦,请您删除当前小程序,重新搜索打开~' + }) + }) + } else { + if (!silence) { + uni.showModal({ + title: '当前为最新版本', + showCancel: false + }) + } + } + }) + } +} + +// 获取订阅消息模板 +async function getSubscribeTemplate() { + const { code, data } = await SocialApi.getSubscribeTemplateList() + if (code === 0) { + subscribeEventList = data + } +} + +// 订阅消息 +function subscribeMessage(event, callback = undefined) { + const tmplIds = [] + if (typeof event === 'string') { + const temp = subscribeEventList.find((item) => item.title.includes(event)) + if (temp) { + tmplIds.push(temp.id) + } + } + if (typeof event === 'object') { + event.forEach((e) => { + const temp = subscribeEventList.find((item) => item.title.includes(e)) + if (temp) { + tmplIds.push(temp.id) + } + }) + } + if (tmplIds.length === 0) return + + uni.requestSubscribeMessage({ + tmplIds, + success: () => { + // 不管是拒绝还是同意都触发 + callback && callback() + }, + fail: (err) => { + console.log(err) + } + }) +} + +export default { + load, + login, + bind, + unbind, + bindUserPhoneNumber, + mobileLogin, + getInfo, + getOpenid, + subscribeMessage, + checkUpdate +} diff --git a/src/utils/platform/provider/wechat/officialAccount.ts b/src/utils/platform/provider/wechat/officialAccount.ts new file mode 100644 index 0000000000000000000000000000000000000000..5a4cacc8b0293d26606d82efa84d2783f85fc3d8 --- /dev/null +++ b/src/utils/platform/provider/wechat/officialAccount.ts @@ -0,0 +1,106 @@ +import $wxsdk from '@/utils/libs/sdk-h5-weixin' +// import { getRootUrl } from '@/sheep/helper' +import AuthUtil from '@/service/member/auth' +import SocialApi from '@/service/member/social' + +const socialType = 31 // 社交类型 - 微信公众号 + +// 加载微信公众号JSSDK +async function load() { + $wxsdk.init() +} + +// 微信公众号登陆 +async function login(code = '', state = '') { + // 情况一:没有 code 时,去获取 code + if (!code) { + const loginUrl = await getLoginUrl() + if (loginUrl) { + uni.setStorageSync('returnUrl', location.href) + window.location = loginUrl + } + // 情况二:有 code 时,使用 code 去自动登录 + } else { + // 解密 code 发起登陆 + const loginResult = await AuthUtil.socialLogin(socialType, code, state) + if (loginResult.code === 0) { + setOpenid(loginResult.data.openid) + return loginResult + } + } + return false +} + +// 微信公众号绑定 +async function bind(code = '', state = '') { + // 情况一:没有 code 时,去获取 code + if (code === '') { + const loginUrl = await getLoginUrl('bind') + if (loginUrl) { + uni.setStorageSync('returnUrl', location.href) + window.location = loginUrl + } + } else { + // 情况二:有 code 时,使用 code 去自动绑定 + const loginResult = await SocialApi.socialBind(socialType, code, state) + if (loginResult.code === 0) { + setOpenid(loginResult.data) + return loginResult + } + } + return false +} + +// 微信公众号解除绑定 +const unbind = async (openid) => { + const { code } = await SocialApi.socialUnbind(socialType, openid) + return code === 0 +} + +// 获取公众号登陆地址 +async function getLoginUrl(event = 'login') { + const page = 'pages/index/login' + '?event=' + event // event 目的,区分是 login 还是 bind + // const page = getRootUrl() + 'pages/index/login' + '?event=' + event // event 目的,区分是 login 还是 bind + const { code, data } = await AuthUtil.socialAuthRedirect(socialType, page) + if (code !== 0) { + return undefined + } + return data +} + +// 设置 openid 到本地存储,目前只有 pay 支付时会使用 +function setOpenid(openid) { + uni.setStorageSync('openid', openid) +} + +// 获得 openid +async function getOpenid(force = false) { + let openid = uni.getStorageSync('openid') + if (!openid && force) { + const info = await getInfo() + if (info && info.openid) { + openid = info.openid + setOpenid(openid) + } + } + return openid +} + +// 获得社交信息 +async function getInfo() { + const { code, data } = await SocialApi.getSocialUser(socialType) + if (code !== 0) { + return undefined + } + return data +} + +export default { + load, + login, + bind, + unbind, + getInfo, + getOpenid, + jsWxSdk: $wxsdk +} diff --git a/src/utils/platform/provider/wechat/openPlatform.ts b/src/utils/platform/provider/wechat/openPlatform.ts new file mode 100644 index 0000000000000000000000000000000000000000..a511e9fac9bbf623f4ec4c54453a812279adb957 --- /dev/null +++ b/src/utils/platform/provider/wechat/openPlatform.ts @@ -0,0 +1,60 @@ +// 登录 +// import third from '@/service/migration/third' +import SocialApi from '@/service/member/social' + +// TODO 芋艿:等后面搞 App 再弄 +const socialType = 32 // 社交类型 - 微信开放平台 + +const load = async () => {} + +// 微信开放平台移动应用授权登陆 +const login = () => { + return new Promise(async (resolve, reject) => { + const loginRes = await uni.login({ + provider: 'weixin', + onlyAuthorize: true + }) + if (loginRes.errMsg === 'login:ok') { + // TODO third.wechat.login 函数未实现 + // const res = await third.wechat.login({ + // platform: 'openPlatform', + // shareInfo: uni.getStorageSync('shareLog') || {}, + // payload: encodeURIComponent( + // JSON.stringify({ + // code: loginRes.code + // }) + // ) + // }) + // if (res.error === 0) { + // resolve(true) + // } + } else { + uni.showToast({ + icon: 'none', + title: loginRes.errMsg + }) + } + resolve(false) + }) +} + +// 微信 App 解除绑定 +const unbind = async (openid) => { + const { code } = await SocialApi.socialUnbind(socialType, openid) + return code === 0 +} + +// 获得社交信息 +async function getInfo() { + const { code, data } = await SocialApi.getSocialUser(socialType) + if (code !== 0) { + return undefined + } + return data +} + +export default { + load, + login, + getInfo +}