From 4fcd4de5553bd58479a39c742b2e5197d0ea992b Mon Sep 17 00:00:00 2001 From: xuan Date: Wed, 1 Mar 2023 11:06:29 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat(ID1000916):=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E5=88=86=E4=BA=AB=E8=AE=B0=E5=BD=95=E5=92=8C=E6=9D=A5?= =?UTF-8?q?=E6=BA=90=E7=BB=9F=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 【新增分享记录和来源统计】 https://www.tapd.cn/53459131/prong/stories/view/1153459131001000916 --- src/api/login.ts | 3 +- src/api/share.ts | 42 +- src/api/user.ts | 9 +- src/app.config.ts | 2 +- src/app.tsx | 5 +- src/common/constant.ts | 6 +- src/common/enum.ts | 15 + src/common/shortCode/index.js | 33 -- src/common/shortCode/index.ts | 88 +++++ src/common/util.ts | 113 ++++-- src/components/calendar/body/index.tsx | 8 +- src/components/calendar/common/constant.ts | 2 + src/components/calendar/common/helper.ts | 2 +- src/components/calendar/common/plugins.ts | 2 +- src/components/calendar/controller/index.tsx | 2 +- src/components/calendar/index.scss | 361 ++++++++++-------- src/components/calendar/index.tsx | 2 +- src/components/calendar/types/calendar.d.ts | 4 +- .../calendar/ui/date-list/index.tsx | 93 +++-- src/components/calendar/ui/day-list/index.tsx | 13 +- src/components/cell/index.tsx | 2 +- src/components/iconfont/iconfont.tsx | 184 ++++----- .../inviteCodePopup/index.module.scss | 27 ++ src/components/inviteCodePopup/index.tsx | 168 ++++++++ src/components/table/index.module.scss | 13 +- src/components/timePicker/index.module.scss | 15 +- src/components/timePicker/index.tsx | 19 +- src/components/timePickerPopup/index.tsx | 11 +- src/pages/details/index.module.scss | 3 + src/pages/details/index.tsx | 32 +- src/pages/inviteCode/index.config.ts | 3 +- src/pages/inviteCode/index.module.scss | 18 +- src/pages/inviteCode/index.tsx | 357 ++++------------- .../inviteCode/inviteCord/index.config.ts | 7 - .../inviteCode/inviteCord/index.module.scss | 163 -------- src/pages/inviteCode/inviteCord/index.tsx | 91 ----- .../inviteCode/inviteFriends/index.config.ts | 5 + .../inviteFriends/index.module.scss | 78 ++++ src/pages/inviteCode/inviteFriends/index.tsx | 170 +++++++++ src/pages/searchList/searchList.tsx | 1 + src/pages/user/index.module.scss | 21 +- src/pages/user/index.tsx | 29 +- src/pages/userEdit copy/index.tsx | 8 +- src/pages/userEdit/index.tsx | 8 +- src/reducers/userInfo.ts | 4 +- src/styles/variables/default.scss | 2 +- src/use/useLogin.ts | 31 +- src/use/useLoginRequest.ts | 10 +- 48 files changed, 1280 insertions(+), 1005 deletions(-) delete mode 100644 src/common/shortCode/index.js create mode 100644 src/common/shortCode/index.ts create mode 100644 src/components/inviteCodePopup/index.module.scss create mode 100644 src/components/inviteCodePopup/index.tsx delete mode 100644 src/pages/inviteCode/inviteCord/index.config.ts delete mode 100644 src/pages/inviteCode/inviteCord/index.module.scss delete mode 100644 src/pages/inviteCode/inviteCord/index.tsx create mode 100644 src/pages/inviteCode/inviteFriends/index.config.ts create mode 100644 src/pages/inviteCode/inviteFriends/index.module.scss create mode 100644 src/pages/inviteCode/inviteFriends/index.tsx diff --git a/src/api/login.ts b/src/api/login.ts index d0b56f6..ef8a96b 100644 --- a/src/api/login.ts +++ b/src/api/login.ts @@ -6,7 +6,8 @@ import { useRequest } from '@/use/useHttp' */ export const LoginApi = () => { return useRequest({ - url: '/v1/mall/login', + // url: '/v1/mall/login', + url: '/v3/mallCherry/login', method: 'post', }) } diff --git a/src/api/share.ts b/src/api/share.ts index 12108c6..a9f84f6 100644 --- a/src/api/share.ts +++ b/src/api/share.ts @@ -28,4 +28,44 @@ export const BindShortCodeApi = () => { url: '/v1/mall/shortCode/bind', method: 'post', }) -} +} + +/** + * 绑定邀请人 + */ +export const BindInvitationUserApi = () => { + return useRequest({ + url: '/v3/mallCherry/user/binding/inviteUser', + method: 'put', + }) +} + +/** + * 预创建 + */ +export const PrepareCreateInvitationInfoApi = () => { + return useRequest({ + url: '/v3/mallCherry/user/prepareCreate', + method: 'post', + }) +} + +/** + * 获取邀请记录 + */ +export const GetInvitationRecordList = () => { + return useRequest({ + url: '/v3/mallCherry/invitationRecord/list', + method: 'get', + }) +} + +/** + * 生成邀请QR码 + */ +export const GenShareQRCode = () => { + return useRequest({ + url: '/v3/mallCherry/invitationRecord/QRCode', + method: 'post', + }) +} diff --git a/src/api/user.ts b/src/api/user.ts index a5fb392..79e5665 100644 --- a/src/api/user.ts +++ b/src/api/user.ts @@ -33,7 +33,7 @@ export const GetPhoneNumberApi = () => { /** * 修改用户昵称 */ -export const realNameUpdateApi = () => { +export const RealNameUpdateApi = () => { return useRequest({ url: '/v1/mall/user', method: 'put', @@ -43,7 +43,7 @@ export const realNameUpdateApi = () => { /** * 公司类型|企业类型 下拉列表 */ -export const companyTypeApi = () => { +export const CompanyTypeApi = () => { return useRequest({ url: '/v1/mall/enum/purchaserType', method: 'get', @@ -53,7 +53,7 @@ export const companyTypeApi = () => { /** * 头像更改 */ -export const portraitUpdateApi = () => { +export const PortraitUpdateApi = () => { return useRequest({ url: '/v1/mall/user/avatar', method: 'put', @@ -134,10 +134,11 @@ export const GetInvitationInfo = () => { method: 'get', }) } -// 邀请码 +// 邀请记录 export const GetInviteeRecord = () => { return useRequest({ url: '/v2/mall/user/invitee_record', method: 'get', + pagination: true, }) } diff --git a/src/app.config.ts b/src/app.config.ts index fefcef8..3460155 100644 --- a/src/app.config.ts +++ b/src/app.config.ts @@ -196,7 +196,7 @@ export default { root: 'pages/inviteCode', pages: [ 'index', - 'inviteCord/index', + 'inviteFriends/index', ], }, { diff --git a/src/app.tsx b/src/app.tsx index 7ca76a5..8ab6039 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -2,10 +2,9 @@ import Taro, { onAppShow, useDidShow } from '@tarojs/taro' import type { FC } from 'react' import { Provider } from 'react-redux' import configStore from './store' -import { shareShop } from './common/util' +import { useShareShop } from './common/util' import ContextBlueTooth from '@/use/contextBlueTooth' import './app.scss' -import { BANk_WX_APPID } from './common/constant' const store = configStore() const App: FC = (params: { children?: React.ReactNode }) => { @@ -14,7 +13,7 @@ const App: FC = (params: { children?: React.ReactNode }) => { }) // 分享 - shareShop() + useShareShop() // 检查版本更新 onAppShow(() => { diff --git a/src/common/constant.ts b/src/common/constant.ts index 5d8465b..898a1e9 100644 --- a/src/common/constant.ts +++ b/src/common/constant.ts @@ -5,7 +5,7 @@ // export const BASE_URL = `http://192.168.0.89:40001/lymarket` // export const BASE_URL = `http://192.168.1.165:40001/lymarket` // 王霞 // export const BASE_URL = 'https://test.zzfzyc.com/lymarket' // 测试环境 -export const BASE_URL = 'https://pre.zzfzyc.com/lymarket' // 预发布 +// export const BASE_URL = 'https://pre.zzfzyc.com/lymarket' // 预发布 // export const BASE_URL = `http://192.168.1.9:40001/lymarket` // 发 // export const BASE_URL = `http://192.168.1.9:50005/lymarket` // 发 // export const BASE_URL = `http://192.168.1.30:50001/lymarket` // 发 @@ -13,8 +13,8 @@ export const BASE_URL = 'https://pre.zzfzyc.com/lymarket' // 预发布 // export const BASE_URL = 'https://www.zzfzyc.com/lymarket' // 正式环境 // export const BASE_URL = `http://192.168.1.5:40001/lymarket` // 王霞 // export const BASE_URL = 'http://192.168.1.7:50002/lymarket' // 添 -// export const BASE_URL = 'http://192.168.1.28:50002/lymarket' // 婷 -// export const BASE_URL = 'http://192.168.1.42:50002/lymarket' // 杰 +// export const BASE_URL = 'http://192.168.1.28:50001/lymarket' // 婷 +export const BASE_URL = 'http://192.168.1.42:50002/lymarket' // 杰 // CDN // 生成密钥 diff --git a/src/common/enum.ts b/src/common/enum.ts index 7b3c036..8e88834 100644 --- a/src/common/enum.ts +++ b/src/common/enum.ts @@ -97,3 +97,18 @@ export const SALE_MODE_SETTING: listType[] = [ { value: 1, title: '剪板', unit: '米', eunit: 'm', step: 1, digits: 2, minNum: 0.5, maxNum: 9.99, defaultNum: 1, field: 'length_length' }, { value: 2, title: '散剪', unit: '米', eunit: 'kg', step: 1, digits: 2, minNum: 3, maxNum: 100000, defaultNum: 3, field: 'weight_number' }, ] + +// 后端的 InvitationWay 邀请枚举 +export const enum InvitationWay { + AUTO_SEARCH = 0, // 自动搜索 + QR_CODE = 1, // 邀请码 + INVITE_LINK = 2, // 分享链接 + INVITE_CODE = 3, // 邀请码(暂不开放) +} + +export const invitationWay: InvitationWay[] = [ + InvitationWay.AUTO_SEARCH, + InvitationWay.QR_CODE, + InvitationWay.INVITE_LINK, + InvitationWay.INVITE_CODE, +] diff --git a/src/common/shortCode/index.js b/src/common/shortCode/index.js deleted file mode 100644 index 22d6dc5..0000000 --- a/src/common/shortCode/index.js +++ /dev/null @@ -1,33 +0,0 @@ -import Taro from '@tarojs/taro' -import { BASE_URL } from '../constant' - -// 解析短码(主要用于右上角按钮分享) -export const analysisShortCodeApi = (val) => { - // 解析短码 - Taro.request({ - url: `${BASE_URL}/v1/mall/shortCode`, - method: 'GET', - data: { md5_key: val }, - success: (res) => { - if (res.data.code == 0) { - // 绑定上下级 - bindParent(res.data.data.share_user_id) - } - }, - }) -} - -// 绑定上下级 -const bindParent = (share_user_id) => { - // 绑定上下级 - Taro.request({ - url: `${BASE_URL}/v1/mall/shortCode/bind`, - method: 'POST', - data: { share_user_id }, - success: (res) => { - if (res.data.code == 0) { - // 绑定上下级 - } - }, - }) -} diff --git a/src/common/shortCode/index.ts b/src/common/shortCode/index.ts new file mode 100644 index 0000000..74d1145 --- /dev/null +++ b/src/common/shortCode/index.ts @@ -0,0 +1,88 @@ +import Taro from '@tarojs/taro' +import { BASE_URL, Platform, WX_APPID } from '../constant' +import type { InvitationWay } from '../enum' +import type { UserAdminParam } from '@/reducers/userInfo' +// 为什么不用useRequest 而是使用Taro.request 是因为useRequest内部使用了react redux 在provider外面使用会报错 + +// 解析短码(主要用于右上角按钮分享) +export const analysisShortCodeApi = (val) => { + // 解析短码 + Taro.request({ + url: `${BASE_URL}/v1/mall/shortCode`, + method: 'GET', + data: { md5_key: val }, + success: (res) => { + if (res.data.code == 0) { + // 绑定上下级 + bindParent(res.data.data.share_user_id) + } + }, + }) +} + +// 绑定上下级 +const bindParent = (share_user_id) => { + // 绑定上下级 + Taro.request({ + url: `${BASE_URL}/v1/mall/shortCode/bind`, + method: 'POST', + data: { share_user_id }, + success: (res) => { + if (res.data.code == 0) { + // 绑定上下级 + } + }, + }) +} + +export const getUserInfo = () => { + const token = Taro.getStorageSync('token') + return new Promise((resolve, reject) => { + Taro.request({ + url: `${BASE_URL}/v1/mall/user/info`, + method: 'GET', + header: { + // Platform: 6, + Platform, + Appid: WX_APPID, + Authorization: token, + }, + success: (res) => { + if (res.data.code == 0) { + resolve(res.data.data) + } + else { + reject(res.data.msg) + } + }, + }) + }) +} + +// 绑定邀请人 +export const bindInvitationUser = async(invitationWay?: InvitationWay, userId?: number) => { + const token = Taro.getStorageSync('token') + Taro.request({ + url: `${BASE_URL}/v3/mallCherry/user/binding/inviteUser`, + method: 'PUT', + header: { + // Platform: 6, + Platform, + Appid: WX_APPID, + Authorization: token, + }, + data: { invitation_way: invitationWay, invited_user_id: userId }, + success: (res) => { + if (res.data.code == 0) { + Taro.showToast({ + title: '绑定邀请人成功', + icon: 'success', + }) + Taro.removeStorageSync('invitationInfo') + // 修改状态 + // const adminUserInfo = JSON.parse(Taro.getStorageSync('adminUserInfo')) + // Taro.setStorageSync('adminUserInfo', JSON.stringify({ ...adminUserInfo, is_passive_invite: true })) + } + }, + }) +} diff --git a/src/common/util.ts b/src/common/util.ts index 9da4f04..1357f39 100644 --- a/src/common/util.ts +++ b/src/common/util.ts @@ -1,7 +1,11 @@ import type { SelectorQuery } from '@tarojs/taro' -import Taro from '@tarojs/taro' +import Taro, { useDidShow, useLaunch, useLoad, useRouter, useShareAppMessage } from '@tarojs/taro' import { formatImgUrl } from './fotmat' -import { analysisShortCodeApi } from './shortCode' +import { analysisShortCodeApi, bindInvitationUser, getUserInfo } from './shortCode/index' +import { InvitationWay, invitationWay } from './enum' +import { isEmptyObject } from './common' +import { getCDNSource } from './constant' +import { GetAdminUserInfoApi } from '@/api/user' /** * 防抖 @@ -115,38 +119,81 @@ export const dataLoadingStatus = ({ list = [], total = 0, status = false }: { li } // 全局分享监听 -export const shareShop = () => { +export const useShareShop = () => { const page = Taro.getCurrentInstance().page - // 当有分享参数时,绑定上下级 - if (page && page.options?.share) { - analysisShortCodeApi(page.options.share) - } + const adminUserInfo = JSON.parse(Taro.getStorageSync('adminUserInfo') || '{}') + + useDidShow(() => { + setTimeout(async() => { + const enterOptions = Taro.getEnterOptionsSync() + console.log('enterOptions', enterOptions) + // 当有分享参数时,绑定上下级 + // if (enterOptions.query?.share) { + // analysisShortCodeApi(enterOptions.query?.share) + // } + // 扫小程序码进入 + if (enterOptions.query?.scene) { + const scene = decodeURIComponent(enterOptions.query?.scene) + const query = analysisScene(scene) + Taro.setStorageSync('invitationInfo', JSON.stringify(query)) + const { user_id, invitation_way } = query + // 获取最新的用户信息 + const newUserInfo = await getUserInfo() + // 校验是否被绑定过了 自己不能邀请自己 + if (!newUserInfo.is_passive_invite && user_id !== newUserInfo.user_id) { + bindInvitationUser(Number(invitation_way) as InvitationWay, Number(user_id)) + } + } + if (enterOptions.query.user_id && enterOptions.query.invitation_way) { + Taro.setStorageSync('invitationInfo', JSON.stringify(enterOptions.query)) + const { user_id, invitation_way } = enterOptions.query + // 获取最新的用户信息 + const newUserInfo = await getUserInfo() + // 校验是否被绑定过了 自己不能邀请自己 + if (!newUserInfo.is_passive_invite && user_id !== newUserInfo.user_id) { + bindInvitationUser(Number(invitation_way) as InvitationWay, Number(user_id)) + } + } + }, 500) + }) + // } if (page && page.onShareAppMessage) { - page.onShareAppMessage = (res) => { - let path = '' - let title = '' - let imageUrl = '' + console.log('adminUserInfo 真假', adminUserInfo) + page.onShareAppMessage = () => { const sortCode = Taro.getStorageSync('sort_code') ? JSON.parse(Taro.getStorageSync('sort_code')) : '' + let path = `/pages/index/index?share=${sortCode.shareShortPage.code}` + let title = sortCode.shareShortPage.title + let imageUrl = formatImgUrl('/mall/share_img_01.png', '!w400') const pageInfo: any = page - // 商品详情分享 - if (pageInfo.route === 'pages/details/index') { - path = `/pages/details/index?share=${sortCode.shareShortDetail.code}` - title = sortCode.shareShortDetail.title - imageUrl = sortCode.shareShortDetail.img - } - else { - path - = pageInfo.route === 'pages/user/index' - ? `/pages/user/index?share=${sortCode.shareShortPage.code}` - : `/pages/index/index?share=${sortCode.shareShortPage.code}` - title = sortCode.shareShortPage.title - imageUrl = pageInfo.route === 'pages/user/index' ? sortCode.shareShortPage.img : formatImgUrl('/mall/share_img_01.png', '!w400') - } - console.log('imageUrl:::', imageUrl) + const promise = new Promise((resolve, reject) => { + getUserInfo().then((newUserInfo) => { + // 商品详情分享 + if (pageInfo.route === 'pages/details/index') { + // path = `/pages/details/index?user_id=${newUserInfo?.user_id}&invitation_way=${InvitationWay.INVITE_LINK}` + path = `/pages/details/index?id=${sortCode.shareShortDetail.id}&user_id=${newUserInfo?.user_id}&invitation_way=${InvitationWay.INVITE_LINK}` + title = sortCode.shareShortDetail.title + imageUrl = sortCode.shareShortDetail.img + } + else { + // 分享链接方式 绑定邀请人 + path = `/pages/index/index?user_id=${newUserInfo?.user_id}&invitation_way=${InvitationWay.INVITE_LINK}` + title = '打造面料爆品 专注客户服务' + imageUrl = getCDNSource('/mall/share_img_01.png') + } + console.log('inside promise imageUrl:::', path) + resolve({ + path, + title, + imageUrl, + }) + }) + }) + console.log('outside promise imageUrl:::', path) return { title, path, imageUrl, + promise, } } } @@ -174,3 +221,17 @@ export function delayQuerySelector(selectorStr: string, delayTime = 500): Promis }) }) } + +function analysisScene(scene: string, split = ';') { + const sceneArr = scene.split(split) + const sceneObj: Record = {} + sceneArr.forEach((item) => { + const itemArr = item.split('=') + sceneObj[itemArr[0]] = itemArr[1] + }) + return sceneObj +} + +function handleBindInvitationUser() { + +} diff --git a/src/components/calendar/body/index.tsx b/src/components/calendar/body/index.tsx index f6664bc..1501e52 100644 --- a/src/components/calendar/body/index.tsx +++ b/src/components/calendar/body/index.tsx @@ -7,15 +7,15 @@ import type { ITouch, ITouchEvent, } from '@tarojs/components/types/common' +import generateCalendarGroup from '../common/helper' +import AtCalendarDateList from '../ui/date-list/index' +import AtCalendarDayList from '../ui/day-list/index' import type { AtCalendarBodyListGroup, AtCalendarBodyProps, AtCalendarBodyState, Calendar, -} from '../../../types/calendar' -import generateCalendarGroup from '../common/helper' -import AtCalendarDateList from '../ui/date-list/index' -import AtCalendarDayList from '../ui/day-list/index' +} from '../types/calendar' import { delayQuerySelector } from '@/common/util' const ANIMTE_DURATION = 300 diff --git a/src/components/calendar/common/constant.ts b/src/components/calendar/common/constant.ts index 237d035..3c2ef8f 100644 --- a/src/components/calendar/common/constant.ts +++ b/src/components/calendar/common/constant.ts @@ -1,3 +1,5 @@ +export const DAY_LIST = ['日', '一', '二', '三', '四', '五', '六'] + export const TYPE_PRE_MONTH = -1 export const TYPE_NOW_MONTH = 0 diff --git a/src/components/calendar/common/helper.ts b/src/components/calendar/common/helper.ts index d5dd45e..7cc4349 100644 --- a/src/components/calendar/common/helper.ts +++ b/src/components/calendar/common/helper.ts @@ -1,7 +1,7 @@ import type { Dayjs } from 'dayjs' import dayjs from 'dayjs' import _flow from 'lodash/flow' -import type { Calendar } from '../../../types/calendar' +import type { Calendar } from '../types/calendar' import * as constant from './constant' import plugins from './plugins' diff --git a/src/components/calendar/common/plugins.ts b/src/components/calendar/common/plugins.ts index 47a4473..6ed4e90 100644 --- a/src/components/calendar/common/plugins.ts +++ b/src/components/calendar/common/plugins.ts @@ -1,6 +1,6 @@ import dayjs from 'dayjs' import _isEmpty from 'lodash/isEmpty' -import type { Calendar } from '../../../types/calendar' +import type { Calendar } from '../types/calendar' interface PluginArg { options: Calendar.GroupOptions diff --git a/src/components/calendar/controller/index.tsx b/src/components/calendar/controller/index.tsx index a575a15..8ebc2a0 100644 --- a/src/components/calendar/controller/index.tsx +++ b/src/components/calendar/controller/index.tsx @@ -6,7 +6,7 @@ import React from 'react' import type { AtCalendarControllerProps, AtCalendarControllerState, -} from '../../../types/calendar' +} from '../types/calendar' export default class AtCalendarController extends React.Component< AtCalendarControllerProps, diff --git a/src/components/calendar/index.scss b/src/components/calendar/index.scss index 7aad719..70e77b6 100644 --- a/src/components/calendar/index.scss +++ b/src/components/calendar/index.scss @@ -1,180 +1,207 @@ - @import '../../styles/variables/default.scss'; @import '../../styles/mixins/index.scss'; .at-calendar { - overflow: hidden; - - /* elements */ - &__header { - .header__flex { - @include display-flex; - @include align-items(center); - - height: 72px; - color: $at-calendar-header-color; - text-align: center; - - &-item { - @include flex(0 0 calc(100% / 7)); - - font-size: 30px; - } - } - } - - &__list { - &.flex { - @include display-flex; - @include align-items(); - @include flex-wrap(wrap); - - color: $at-calendar-day-color; - - .flex__item { - @include flex(0 0 calc(100% / 7)); - - font-size: 30px; - text-align: center; - position: relative; - margin: 5px 0; - - &-container { - @include align-items(center); - @include display-flex; - - width: $at-calendar-day-size; - height: $at-calendar-day-size; - margin-left: auto; - margin-right: auto; - border-radius: 50%; - - .container-text { - @include flex; - } - } - - &-extra { - .extra-marks { - position: absolute; - bottom: 5px; - line-height: 0; - left: 50%; - transform: translateX(-50%); - - .mark { - width: $at-calendar-mark-size; - height: $at-calendar-mark-size; - margin-right: 4px; - display: inline-block; - background-color: $at-calendar-main-color; - border-radius: 50%; - overflow: hidden; - - &:last-child { - margin-right: 0; - } - } - } - } - - &--today { - color: $at-calendar-main-color; - font-weight: bolder; - } - - &--blur { - color: #e1e4e7; - } - - &--selected { - color: white; - background-color: rgba($color: $at-calendar-main-color, $alpha: 1); - - &-head { - border-top-left-radius: 40px; - border-bottom-left-radius: 40px; - } - - &-tail { - border-top-right-radius: 40px; - border-bottom-right-radius: 40px; - } - - /* stylelint-disable-next-line */ - .extra-marks .mark { - background-color: white; - } - - &-head.flex__item--selected-tail { - background-color: transparent; - - .flex__item-container { - background-color: rgba($color: $at-calendar-main-color, - $alpha: 0.7); - } - } - } - } - } - } - - &__controller { + overflow: hidden; + + /* elements */ + &__header { + .header__flex { @include display-flex; @include align-items(center); - @include justify-content(center); - - margin-bottom: 20px; - - .controller__arrow { - @include flex(0 0 40px); - - height: 40px; - border-radius: 12px; - display: inline-block; - background-size: 16px 24px; - background-position: center; - background-color: #f7f8fc; - background-repeat: no-repeat; - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAYCAYAAADzoH0MAAAAAXNSR0IArs4c6QAAAnFJREFUOBGVVF1rE0EUnXt3tzFtWmqjKYKfqIhVa1L8FQVRWtwnXwRhidXGDwQf81oCUQMioZRCHwNSgiD4lD9QSYVKsA8KbaW1jbamX8adnWsmMnESbYz7cs6ee8/ZnZm7y9h/Xk/Gs70TE9lOZQNFWsGx1IvDJoozxNDttNpmHOfyTssBj59PHxceP6keREDlYPvBGUMJzTD5LHuKhHtC70EEQe72atMAIoLu0MWzRPxInZnEdxZib2I37L2XEI/HsSvYd44AQrqZIW5b3J8fHR0sS/2ve5DJZIzFFexnSD262QAs+c1js45zyVU6KqIwnU5bS58x0mhGhusbaz153Sw9dW+QSr3yCdwJe4wCKlCigbAWiw7PAYDQdclrAclkxk8+iDBifr3JMq3lO86VQsVMuq549RQSU687mOcNANE+VfiFxuLd6NX3e5llD8qjskqb54E8n24mk5Yf3B6ab2auBsgGC8Q7QOJ1AS6ExrSZ12s6r57CyIi99cNgswywtkkIzDB2eSSdftmuGxp57RgfOfY38HlvRWVNqgmYsDb57sDkZK5hb1RHZQ9+U8bu37S/MtOc0zUg8G2U1yOV4WrTdcXrAqT4MDq0yokXVINEwb32pS9WOJfLmboueW0OGgtP05mj3IXTum6iuXHogDtr27an9D/eQBVijr2AiB/VvUQuePenNXZBfmhKrxEl6Hjv1vAHA2lJ1wRBcH9vf5+cH6k3DZANsei1eWCwIrm6uOf1Jsenq8v7Z4ActFJxrsBMo6gC0GAebPHq/Z6bqJoVyn/EQpGFK08MmF2B/Oj1wZKqtYzxeM5MJKY6dMNPQnnePR8FubkAAAAASUVORK5CYII="); - - &--right { - transform: rotate(180deg); - } - - &--disabled { - opacity: 0.5; - } - } - - .controller__info { - @include flex(0 0 auto); - + + height: 72px; + color: $at-calendar-header-color; + text-align: center; + + &-item { + @include flex(0 0 calc(100% / 7)); + font-size: 30px; - margin-left: 40px; - margin-right: 40px; } } } - - .at-calendar-slider__main { - .main__body { + + &__list { + &.flex { @include display-flex; - - width: 100%; - - &--animate { - transition: transform 300ms cubic-bezier(0.36, 0.66, 0.04, 1); - } - - .body__slider { - @include flex(0 0 100%); - } - } - - &--weapp, - &--swan { - .main__body { - height: 480px; + @include align-items(); + @include flex-wrap(wrap); + + color: $at-calendar-day-color; + + .flex__item { + @include flex(0 0 calc(100% / 7)); + + font-size: 30px; + text-align: center; + position: relative; + margin: 5px 0; + + &-container { + @include align-items(center); + @include display-flex; + + width: $at-calendar-day-size; + height: $at-calendar-day-size; + margin-left: auto; + margin-right: auto; + border-radius: 50%; + + .container-text { + @include flex; + } + } + .today-mark { + position: absolute; + bottom: -2px; + left: 50%; + transform: translateX(-50%); + color: $color-main; + font-size: 20px; + } + + &-extra { + .extra-marks { + position: absolute; + bottom: 5px; + line-height: 0; + left: 50%; + transform: translateX(-50%); + + .mark { + width: $at-calendar-mark-size; + height: $at-calendar-mark-size; + margin-right: 4px; + display: inline-block; + background-color: $at-calendar-main-color; + border-radius: 50%; + overflow: hidden; + + &:last-child { + margin-right: 0; + } + } + } + } + + &--today { + color: $color-brand; + font-weight: bolder; + } + + &--blur { + color: #e1e4e7; + } + + &--selected { + color: #333437; + background-color: rgba($color: $at-calendar-main-color, $alpha: 1); + + &-row-end { + border-top-right-radius: 20px; + border-bottom-right-radius: 20px; + } + &-row-start { + border-top-left-radius: 20px; + border-bottom-left-radius: 20px; + } + + &-head { + border-top-left-radius: 20px; + border-bottom-left-radius: 20px; + } + + &-tail { + border-top-right-radius: 20px; + border-bottom-right-radius: 20px; + } + &-head, + &-tail { + &-cover { + color: white; + border-radius: 20px; + background-color: $color-main; + } + } + + .today-mark { + color: inherit; + } + + /* stylelint-disable-next-line */ + .extra-marks .mark { + background-color: white; + } + + // &-head.flex__item--selected-tail { + // background-color: transparent; + + // .flex__item-container { + // background-color: rgba($color: $at-calendar-main-color, $alpha: 0.7); + // } + // } + } } } } + + &__controller { + @include display-flex; + @include align-items(center); + @include justify-content(center); + + margin-bottom: 20px; + + .controller__arrow { + @include flex(0 0 40px); + + height: 40px; + border-radius: 12px; + display: inline-block; + background-size: 16px 24px; + background-position: center; + background-color: #f7f8fc; + background-repeat: no-repeat; + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAYCAYAAADzoH0MAAAAAXNSR0IArs4c6QAAAnFJREFUOBGVVF1rE0EUnXt3tzFtWmqjKYKfqIhVa1L8FQVRWtwnXwRhidXGDwQf81oCUQMioZRCHwNSgiD4lD9QSYVKsA8KbaW1jbamX8adnWsmMnESbYz7cs6ee8/ZnZm7y9h/Xk/Gs70TE9lOZQNFWsGx1IvDJoozxNDttNpmHOfyTssBj59PHxceP6keREDlYPvBGUMJzTD5LHuKhHtC70EEQe72atMAIoLu0MWzRPxInZnEdxZib2I37L2XEI/HsSvYd44AQrqZIW5b3J8fHR0sS/2ve5DJZIzFFexnSD262QAs+c1js45zyVU6KqIwnU5bS58x0mhGhusbaz153Sw9dW+QSr3yCdwJe4wCKlCigbAWiw7PAYDQdclrAclkxk8+iDBifr3JMq3lO86VQsVMuq549RQSU687mOcNANE+VfiFxuLd6NX3e5llD8qjskqb54E8n24mk5Yf3B6ab2auBsgGC8Q7QOJ1AS6ExrSZ12s6r57CyIi99cNgswywtkkIzDB2eSSdftmuGxp57RgfOfY38HlvRWVNqgmYsDb57sDkZK5hb1RHZQ9+U8bu37S/MtOc0zUg8G2U1yOV4WrTdcXrAqT4MDq0yokXVINEwb32pS9WOJfLmboueW0OGgtP05mj3IXTum6iuXHogDtr27an9D/eQBVijr2AiB/VvUQuePenNXZBfmhKrxEl6Hjv1vAHA2lJ1wRBcH9vf5+cH6k3DZANsei1eWCwIrm6uOf1Jsenq8v7Z4ActFJxrsBMo6gC0GAebPHq/Z6bqJoVyn/EQpGFK08MmF2B/Oj1wZKqtYzxeM5MJKY6dMNPQnnePR8FubkAAAAASUVORK5CYII='); + + &--right { + transform: rotate(180deg); + } + + &--disabled { + opacity: 0.5; + } + } + + .controller__info { + @include flex(0 0 auto); + + font-size: 30px; + margin-left: 40px; + margin-right: 40px; + } + } +} + +.at-calendar-slider__main { + .main__body { + @include display-flex; + + width: 100%; + + &--animate { + transition: transform 300ms cubic-bezier(0.36, 0.66, 0.04, 1); + } + + .body__slider { + @include flex(0 0 100%); + } + } + + &--weapp, + &--swan { + .main__body { + height: 480px; + } + } +} diff --git a/src/components/calendar/index.tsx b/src/components/calendar/index.tsx index 24b6581..5ce6d54 100644 --- a/src/components/calendar/index.tsx +++ b/src/components/calendar/index.tsx @@ -10,7 +10,7 @@ import type { AtCalendarPropsWithDefaults, AtCalendarState, Calendar, -} from '../../types/calendar' +} from './types/calendar' import AtCalendarBody from './body/index' import AtCalendarController from './controller/index' import './index.scss' diff --git a/src/components/calendar/types/calendar.d.ts b/src/components/calendar/types/calendar.d.ts index 548af63..989bf53 100644 --- a/src/components/calendar/types/calendar.d.ts +++ b/src/components/calendar/types/calendar.d.ts @@ -33,9 +33,9 @@ declare namespace Calendar { isToday?: boolean - isBeforeMin?: boolean + isRowEnd?: boolean - isAfterMax?: boolean + isRowStart?: boolean isDisabled?: boolean diff --git a/src/components/calendar/ui/date-list/index.tsx b/src/components/calendar/ui/date-list/index.tsx index fc34aba..311e8d4 100644 --- a/src/components/calendar/ui/date-list/index.tsx +++ b/src/components/calendar/ui/date-list/index.tsx @@ -3,6 +3,7 @@ import classnames from 'classnames' import React from 'react' import type { Calendar } from '../../types/calendar' import * as constant from '../../common/constant' +import { DAY_LIST } from '../../common/constant' const MAP: Record = { [constant.TYPE_PRE_MONTH]: 'pre', @@ -34,47 +35,69 @@ export default class AtCalendarList extends React.Component { public render(): JSX.Element | null { const { list } = this.props if (!list || list.length === 0) { return null } + let row = 0 // 当前行数 第 0 行就是第一行 return ( - {list.map((item: Calendar.Item) => ( - { + const isRowStart = DAY_LIST.length * row === index + const isRowEnd = (index + 1) % DAY_LIST.length === 0 + if (isRowStart) { + row++ + } + return ( + - - {item.text} + }, + )} + > + + + {item.text} + + + {item.isToday + ? 今天 + : null} + {item.marks && item.marks.length > 0 + ? ( + + {item.marks.map((mark, key) => ( + + {mark.value as React.ReactNode} + + ))} + + ) + : null} + + - - {item.marks && item.marks.length > 0 - ? ( - - {item.marks.map((mark, key) => ( - - {mark.value as React.ReactNode} - - ))} - - ) - : null} - - - ))} + + ) + })} ) } diff --git a/src/components/calendar/ui/day-list/index.tsx b/src/components/calendar/ui/day-list/index.tsx index f6b4f83..34d23ce 100644 --- a/src/components/calendar/ui/day-list/index.tsx +++ b/src/components/calendar/ui/day-list/index.tsx @@ -1,18 +1,17 @@ import { View } from '@tarojs/components' import React from 'react' +import { DAY_LIST } from '../../common/constant' export default class AtCalendarHeader extends React.Component { public render(): JSX.Element { return ( - - - - - - - + { + DAY_LIST.map((day, index) => ( + {day} + )) + } ) diff --git a/src/components/cell/index.tsx b/src/components/cell/index.tsx index 0431b89..f8a5840 100644 --- a/src/components/cell/index.tsx +++ b/src/components/cell/index.tsx @@ -34,7 +34,7 @@ const Cell: FC = (props) => { {title} - {desc} + {desc} {isLink && } diff --git a/src/components/iconfont/iconfont.tsx b/src/components/iconfont/iconfont.tsx index 368eee2..fa4d39c 100644 --- a/src/components/iconfont/iconfont.tsx +++ b/src/components/iconfont/iconfont.tsx @@ -1,89 +1,89 @@ -import React, { useState, useEffect, FC } from "react"; -import { Block, View } from "@tarojs/components"; -import "./iconfont.scss"; -import Taro from "@tarojs/taro"; -import classnames from "classnames"; - -const SystemWidth = Taro.getSystemInfoSync().windowWidth -const quot = '"' - -function hex2rgb(hex) { - const rgb: number[] = []; - - hex = hex.substr(1); - - if (hex.length === 3) { - hex = hex.replace(/(.)/g, "$1$1"); - } - - hex.replace(/../g, function(color: string) { - rgb.push(parseInt(color, 0x10)); - return color; - }); - - return "rgb(" + rgb.join(",") + ")"; -} - -export type IconNames = 'icon-lijitixian' | 'icon-hongbao' | 'icon-xuanzhongshijian' | 'icon-zhankai1' | 'icon-shouqi1' | 'icon-shoucang1' | 'icon-weixinyijiandenglu' | 'icon-nanzhuang' | 'icon-zhuanyefenlei' | 'icon-tongzhuang' | 'icon-chaoliumianliao' | 'icon-nvzhuang' | 'icon-dingwei' | 'icon-xuanzhongyanse' | 'icon-sekajianyanglingqu' | 'icon-lingseka' | 'icon-lingjianyang' | 'icon-gerenzhongxin-dianji' | 'icon-shouye-dianji' | 'icon-gouwuche-weidianji' | 'icon-gerenzhongxin-weidianji' | 'icon-gouwuche-dianji' | 'icon-shouye-weidianji' | 'icon-paixu1' | 'icon-zhankai' | 'icon-shouqi' | 'icon-tips' | 'icon-dianhua' | 'icon-paixu' | 'icon-shaixuan' | 'icon-bodakehujingli' | 'icon-guanfangweixinkefu' | 'icon-tuijianbiaoqian' | 'icon-rukou' | 'icon-renzhengchenggong' | 'icon-wodekefu' | 'icon-yanseduibi' | 'icon-dizhiguanli' | 'icon-weixin' | 'icon-riqi' | 'icon-shuru' | 'icon-a-0tianzhangqi' | 'icon-huodaofukuan' | 'icon-huozhuziti' | 'icon-saomazhifu' | 'icon-xianxiahuikuan' | 'icon-yufukuan' | 'icon-xinzengshoucangjia' | 'icon-qingchusousuo' | 'icon-xuanzechenggong' | 'icon-gongnengtubiao-saomiao' | 'icon-bianjizidingyimadan' | 'icon-zidingyimadanyulan' | 'icon-yuanshimadanyulan' | 'icon-xiala' | 'icon-shangla' | 'icon-qingchuxinxi' | 'icon-sousuo' | 'icon-guanli' | 'icon-bianji' | 'icon-shoucangjia' | 'icon-shezhi' | 'icon-tishi' | 'icon-erweima' | 'icon-dianjishoucang' | 'icon-gouwuche' | 'icon-shoucangchenggong' | 'icon-fenxiangshangpin' | 'icon-kefu' | 'icon-xinzenganniu' | 'icon-jianshaoanniu' | 'icon-daifahuo2' | 'icon-daishouhuo2' | 'icon-tuikuan-shouhou' | 'icon-daipeibu2' | 'icon-daifukuan2'; - -type PropsType = { - name: IconNames; - size?: number; - color?: string | string[]; - customStyle?: React.CSSProperties; - customClassName?: string; -}; - -const IconFont:FC = ({ - name, - size = 36, - color, - customStyle = {}, - customClassName = "" -}) => { - const [colors, setColors] = useState() - const [isStr, setIsStr] = useState(true) - const [svgSize, setSvgSize] = useState(() => (size / 750) * SystemWidth) - - useEffect(() => { - setIsStr(typeof color === 'string') - if (typeof color === 'string') { - setColors(color.indexOf('#') === 0 ? hex2rgb(color) : color) - } else { - setColors( - color?.map(function (item) { - return item.indexOf('#') === 0 ? hex2rgb(item) : item - }) - ) - } - return () => {} - }, [color]) - - useEffect(() => { - setSvgSize((size / 750) * SystemWidth) - }, [size]) - - // 也可以使用 if (name === 'xxx') { return } 来渲染,但是测试发现在ios下会有问题,报错 Maximum call stack啥的。下面这个写法没问题 - return ( - - {/* icon-colorCard 本地svg */ } - {/* { name === 'icon-colorCard' && () } */} - {/* icon-alipay */} - {/* {name === "icon-alipay" && ( - - )} */} +import React, { useState, useEffect, FC } from "react"; +import { Block, View } from "@tarojs/components"; +import "./iconfont.scss"; +import Taro from "@tarojs/taro"; +import classnames from "classnames"; + +const SystemWidth = Taro.getSystemInfoSync().windowWidth +const quot = '"' + +function hex2rgb(hex) { + const rgb: number[] = []; + + hex = hex.substr(1); + + if (hex.length === 3) { + hex = hex.replace(/(.)/g, "$1$1"); + } + + hex.replace(/../g, function(color: string) { + rgb.push(parseInt(color, 0x10)); + return color; + }); + + return "rgb(" + rgb.join(",") + ")"; +} + +export type IconNames = 'icon-lijitixian' | 'icon-hongbao' | 'icon-xuanzhongshijian' | 'icon-zhankai1' | 'icon-shouqi1' | 'icon-shoucang1' | 'icon-weixinyijiandenglu' | 'icon-nanzhuang' | 'icon-zhuanyefenlei' | 'icon-tongzhuang' | 'icon-chaoliumianliao' | 'icon-nvzhuang' | 'icon-dingwei' | 'icon-xuanzhongyanse' | 'icon-sekajianyanglingqu' | 'icon-lingseka' | 'icon-lingjianyang' | 'icon-gerenzhongxin-dianji' | 'icon-shouye-dianji' | 'icon-gouwuche-weidianji' | 'icon-gerenzhongxin-weidianji' | 'icon-gouwuche-dianji' | 'icon-shouye-weidianji' | 'icon-paixu1' | 'icon-zhankai' | 'icon-shouqi' | 'icon-tips' | 'icon-dianhua' | 'icon-paixu' | 'icon-shaixuan' | 'icon-bodakehujingli' | 'icon-guanfangweixinkefu' | 'icon-tuijianbiaoqian' | 'icon-rukou' | 'icon-renzhengchenggong' | 'icon-wodekefu' | 'icon-yanseduibi' | 'icon-dizhiguanli' | 'icon-weixin' | 'icon-riqi' | 'icon-shuru' | 'icon-a-0tianzhangqi' | 'icon-huodaofukuan' | 'icon-huozhuziti' | 'icon-saomazhifu' | 'icon-xianxiahuikuan' | 'icon-yufukuan' | 'icon-xinzengshoucangjia' | 'icon-qingchusousuo' | 'icon-xuanzechenggong' | 'icon-gongnengtubiao-saomiao' | 'icon-bianjizidingyimadan' | 'icon-zidingyimadanyulan' | 'icon-yuanshimadanyulan' | 'icon-xiala' | 'icon-shangla' | 'icon-qingchuxinxi' | 'icon-sousuo' | 'icon-guanli' | 'icon-bianji' | 'icon-shoucangjia' | 'icon-shezhi' | 'icon-tishi' | 'icon-erweima' | 'icon-dianjishoucang' | 'icon-gouwuche' | 'icon-shoucangchenggong' | 'icon-fenxiangshangpin' | 'icon-kefu' | 'icon-xinzenganniu' | 'icon-jianshaoanniu' | 'icon-daifahuo2' | 'icon-daishouhuo2' | 'icon-tuikuan-shouhou' | 'icon-daipeibu2' | 'icon-daifukuan2'; + +type PropsType = { + name: IconNames; + size?: number; + color?: string | string[]; + customStyle?: React.CSSProperties; + customClassName?: string; +}; + +const IconFont:FC = ({ + name, + size = 36, + color, + customStyle = {}, + customClassName = "" +}) => { + const [colors, setColors] = useState() + const [isStr, setIsStr] = useState(true) + const [svgSize, setSvgSize] = useState(() => (size / 750) * SystemWidth) + + useEffect(() => { + setIsStr(typeof color === 'string') + if (typeof color === 'string') { + setColors(color.indexOf('#') === 0 ? hex2rgb(color) : color) + } else { + setColors( + color?.map(function (item) { + return item.indexOf('#') === 0 ? hex2rgb(item) : item + }) + ) + } + return () => {} + }, [color]) + + useEffect(() => { + setSvgSize((size / 750) * SystemWidth) + }, [size]) + + // 也可以使用 if (name === 'xxx') { return } 来渲染,但是测试发现在ios下会有问题,报错 Maximum call stack啥的。下面这个写法没问题 + return ( + + {/* icon-colorCard 本地svg */ } + {/* { name === 'icon-colorCard' && () } */} + {/* icon-alipay */} + {/* {name === "icon-alipay" && ( + + )} */} {/* icon-lijitixian */} { name === 'icon-lijitixian' && () } @@ -312,9 +312,9 @@ const IconFont:FC = ({ {/* icon-daifukuan2 */} { name === 'icon-daifukuan2' && () } - - - ) -} - -export default IconFont + + + ) +} + +export default IconFont diff --git a/src/components/inviteCodePopup/index.module.scss b/src/components/inviteCodePopup/index.module.scss new file mode 100644 index 0000000..fc6ef5f --- /dev/null +++ b/src/components/inviteCodePopup/index.module.scss @@ -0,0 +1,27 @@ +.codePreview { + display: flex; + flex-flow: column nowrap; + justify-content: center; + align-items: center; + position: relative; + top: -90px; + .imageContainer { + width: 80vw; + height: auto; + overflow: hidden; + .image { + width: 100%; + height: 100%; + border-radius: 20px; + } + } + .previewTips { + position: absolute; + bottom: -80px; + font-size: 40px; + font-weight: 400; + color: #c2c2c2; + letter-spacing: 5px; + white-space: nowrap; + } +} diff --git a/src/components/inviteCodePopup/index.tsx b/src/components/inviteCodePopup/index.tsx new file mode 100644 index 0000000..f405d35 --- /dev/null +++ b/src/components/inviteCodePopup/index.tsx @@ -0,0 +1,168 @@ +import { Image, Text, View } from '@tarojs/components' +import Taro, { useReady } from '@tarojs/taro' +import type { Ref } from 'react' +import { forwardRef, useImperativeHandle, useState } from 'react' +import Dialog from '../Dialog' +import styles from './index.module.scss' +import { alert } from '@/common/common' +import { getCDNSource } from '@/common/constant' +import { GenShareQRCode } from '@/api/share' +import useUserInfo from '@/use/useUserInfo' +import { InvitationWay } from '@/common/enum' + +export interface InviteCodePopupRef { + startDrawPoster: () => void +} +const InviteCodePopup = (_, ref: Ref) => { + const { userInfo } = useUserInfo() + const [targetImageUrl, setTargetImageUrl] = useState('') + + const [showPopup, setShowPopup] = useState(false) + + const handleChange = (value: boolean) => { + setShowPopup(value) + } + + const getImageObject = (canvas: Taro.OffscreenCanvas, src: string) => { + return new Promise((resolve, reject) => { + // console.log('getImageObject param', canvas, src) + const img = canvas.createImage() + img.onload = (res) => { + console.log('onload res', res) + console.log('image===>', img) + resolve(img) + } + img.onerror = (err) => { + console.log('image error===>', err) + alert.error('图片加载失败') + reject(err) + } + img.src = src + }) + } + const doublePick = (num, minus = false) => { + if (minus) { + return num / 2 + } + else { + return num * 2 + } + } + // canvas 生成 图片 + const saveCanvasToImage = (canvas) => { + const image = canvas.toDataURL() + console.log('image:', image) + setTargetImageUrl(image) + Taro.hideLoading() + } + + const { fetchData: genCode } = GenShareQRCode() + // 生成二维码 + const genQRcode = async() => { + const res = await genCode({ path: 'pages/index/index', width: 480, scene: `user_id=${userInfo.adminUserInfo.user_id};invitation_way=${InvitationWay.QR_CODE}` }) + if (res.success) { + console.log('res==>', `data:image/png;base64,${res.data.buffer}`) + return `data:image/png;base64,${res.data.buffer}` + } + else { + Taro.hideLoading() + return '' + } + } + + const startPaint = async(ctx: Taro.RenderingContext, canvas: Taro.OffscreenCanvas, image: Taro.Image) => { + // 开始绘制 + // @ts-expect-error asd + const { width, height } = canvas! + console.log('startPaint param', ctx, canvas, image) + const cCanvasCtx = ctx as any + cCanvasCtx.clearRect(0, 0, width, height) + // 绘制背景图 + cCanvasCtx.drawImage(image, 0, 0, width, height) + cCanvasCtx.save() + // 生成二维码 + const codeUrl = await genQRcode() + try { + const code = await getImageObject(canvas, codeUrl) + cCanvasCtx.drawImage(code, doublePick(574), doublePick(1270), doublePick(140), doublePick(140)) + } + catch (err) { + console.error('合成二维邀请码失败', err) + Taro.hideLoading() + throw new Error('合成二维邀请码失败') + } + + saveCanvasToImage(canvas) + } + // 绘制最终的海报,图片使用两倍大小,canvas大小是图片的二分之一 就能让canvas生成出来的图片清晰了 + const drawPictorial = async() => { + // eslint-disable-next-line no-async-promise-executor + return new Promise(async(resolve, reject) => { + Taro.showLoading({ + title: '加载中', + }) + Taro.getImageInfo({ + src: getCDNSource('/mall/poster.png'), + success: (res) => { + console.log('res==>', res) + const canvas = Taro.createOffscreenCanvas({ type: '2d', width: res.width, height: res.height }) + const context = canvas.getContext('2d')// 渲染上下文 + // @ts-expect-error sdfa + canvas.width = res.width + // @ts-expect-error sdfa + canvas.height = res.height + getImageObject(canvas, `${res.path}`).then(async(image) => { + try { + // 开始绘制 + await startPaint(context, canvas, image) + resolve(true) + } + catch (err) { + console.log(err) + Taro.hideLoading() + reject(new Error('绘制失败')) + } + }).catch((error) => { + Taro.hideLoading() + throw new Error(error) + }) + }, + }) + }) + } + + useImperativeHandle( + ref, + () => ({ + startDrawPoster: handleQRcodeShare, + }), + [], + ) + + const handleQRcodeShare = async() => { + try { + const flag = await drawPictorial() + if (flag) { + setShowPopup(true) + } + } + catch (err) { + throw new Error('弹出二维码失败') + } + } + + // useReady(() => { + // getInviteCode() + // }) + + return + + + {/* showMenuByLongpress 属性只对 小程序有效 */} + + + 长按图片保存到手机 + + +} +export default forwardRef(InviteCodePopup) diff --git a/src/components/table/index.module.scss b/src/components/table/index.module.scss index 9f35e98..f190212 100644 --- a/src/components/table/index.module.scss +++ b/src/components/table/index.module.scss @@ -20,9 +20,10 @@ align-items: center; display: flex; font-size: 26px; + color: #9f9f9f; } -.th{ +.th { font-size: 26px; } @@ -40,11 +41,9 @@ border-radius: 8px; } -.ellipsis_1{ - @include common_ellipsis(1) +.ellipsis_1 { + @include common_ellipsis(1); } -.ellipsis_2{ - @include common_ellipsis(2) +.ellipsis_2 { + @include common_ellipsis(2); } - - diff --git a/src/components/timePicker/index.module.scss b/src/components/timePicker/index.module.scss index e59600b..57677e5 100644 --- a/src/components/timePicker/index.module.scss +++ b/src/components/timePicker/index.module.scss @@ -1,12 +1,17 @@ - - .time-box { padding: 40px; } - +.left_slot{ + width: 48%; +} +.confirm_button{ + min-width: 48%; +} .sure-box { - margin-left: 102px; - margin-right: 102px; + display: flex; + flex-flow: row nowrap; + justify-content: space-between; + padding: 0 32px; font-size: 28px; font-weight: 500; } diff --git a/src/components/timePicker/index.tsx b/src/components/timePicker/index.tsx index a1dd230..c4780e9 100644 --- a/src/components/timePicker/index.tsx +++ b/src/components/timePicker/index.tsx @@ -4,16 +4,18 @@ import dayjs from 'dayjs' import NormalButton from '../normalButton' import styles from './index.module.scss' import AtCalendar from '@/components/calendar/index' -import Popup from '@/components/popup' type DateArg = string | number | Date -interface Props { + +export interface TimePickerPropsType { end?: DateArg start?: DateArg onSelectDate?: (any) => void + leftSlot?: React.ReactNode } -const TimePicker = (props: Props) => { - const { start = '', end = '', onSelectDate } = props + +const TimePicker = (props: TimePickerPropsType) => { + const { start = '', end = '', onSelectDate, leftSlot } = props const [time, setTime] = useState({}) const handTime = (e) => { @@ -34,6 +36,7 @@ const TimePicker = (props: Props) => { console.log('e===>', e) setTime(e) } + const currentDate = useMemo(() => { return (!start && !end) ? { start: `${dayjs(new Date()).format('YYYY-MM-DD')} 00:00:00`, end } : { start, end } }, [props]) @@ -48,7 +51,13 @@ const TimePicker = (props: Props) => { onSelectDate={e => handTime(e)} /> - onSelectDate?.(time)} size="normal" round customClassName={styles['sure-box']}>确认 + + { + leftSlot && {leftSlot} + } + onSelectDate?.(time)} size="normal" round>确认 + + ) } diff --git a/src/components/timePickerPopup/index.tsx b/src/components/timePickerPopup/index.tsx index 7b53258..1b364b9 100644 --- a/src/components/timePickerPopup/index.tsx +++ b/src/components/timePickerPopup/index.tsx @@ -1,22 +1,19 @@ import { memo } from 'react' import './index.scss' +import type { TimePickerPropsType } from '../timePicker' import TimePicker from '../timePicker' import Popup from '@/components/popup' -type DateArg = string | number | Date -interface Props { +interface Props extends TimePickerPropsType { showTime: boolean closePopup?: () => void - end?: DateArg - start?: DateArg - onSelectDate?: (any) => void } const TimePickerPopup = (props: Props) => { - const { showTime = false, closePopup, start = '', end = '', onSelectDate } = props + const { showTime = false, closePopup, start = '', end = '', onSelectDate, leftSlot } = props return ( closePopup?.()}> - + ) } diff --git a/src/pages/details/index.module.scss b/src/pages/details/index.module.scss index b41a9a1..d61bdbd 100644 --- a/src/pages/details/index.module.scss +++ b/src/pages/details/index.module.scss @@ -27,6 +27,9 @@ .share, .collect { width: 76px; + display: flex; + flex-flow: column nowrap; + align-items: center; font-size: $font_size_min; text-align: center; color: $color_font_three; diff --git a/src/pages/details/index.tsx b/src/pages/details/index.tsx index 141628f..649c53a 100644 --- a/src/pages/details/index.tsx +++ b/src/pages/details/index.tsx @@ -57,19 +57,20 @@ const Details = (props: Params) => { const [recommendStatus, setRecommendStatus] = useState(false) // 解析短码参数 - const { fetchData: fetchDataAnalysisShortCode } = AnalysisShortCodeApi() - const analysisShortCode = async() => { - const res = await fetchDataAnalysisShortCode({ md5_key: router.params.share }) - setParams({ id: res.data.product_id, share: res.data }) - } + // const { fetchData: fetchDataAnalysisShortCode } = AnalysisShortCodeApi() + // const analysisShortCode = async() => { + // const res = await fetchDataAnalysisShortCode({ md5_key: router.params.share }) + // setParams({ id: res.data.product_id, share: res.data }) + // } // 判断是否是分享过来的参数 const judgeParam = async() => { + console.log('router.params', router.params) if (router.params.id) { setParams({ ...params, id: router.params.id }) } - else if (router.params.share) { - analysisShortCode() - } + // else if (router.params.share) { + // analysisShortCode() + // } } // 获取购物车数据数量 const { getShopCount, commonData } = useCommonData() @@ -78,6 +79,7 @@ const Details = (props: Params) => { const [productInfo, setProductInfo] = useState({}) const { fetchData } = GetProductDetailApi() const getProductDetail = async() => { + console.log('params', params) const { data } = await fetchData({ id: params.id }) setProductId(data.id) setProductInfo(data) @@ -115,7 +117,7 @@ const Details = (props: Params) => { type: ShareDetail.value, product_id: parseInt(params.id), }) - setSortCode({ ...userObj.sort_code, shareShortDetail: { title: productName as string, code: resDetail.md5_key, img: shareImg } }) + setSortCode({ ...userObj.sort_code, shareShortDetail: { id: Number(router.params.id), title: productName as string, code: resDetail.md5_key, img: shareImg } }) } useDidShow(() => { judgeParam() @@ -282,17 +284,13 @@ const Details = (props: Params) => { {productInfo.describe} - + { + collectStatus ? : + } 收藏 - + 分享 diff --git a/src/pages/inviteCode/index.config.ts b/src/pages/inviteCode/index.config.ts index 29bfdd0..686bea4 100644 --- a/src/pages/inviteCode/index.config.ts +++ b/src/pages/inviteCode/index.config.ts @@ -1,7 +1,8 @@ export default { - navigationBarTitleText: '邀请码', + navigationBarTitleText: '邀请朋友', navigationBarTextStyle: 'black', navigationBarBackgroundColor: '#E4EEFD', backgroundColor: '#E4EEFD', + enableShareAppMessage: true, backgroundColorTop: '#E4EEFD', } diff --git a/src/pages/inviteCode/index.module.scss b/src/pages/inviteCode/index.module.scss index 3039a1f..8f1740c 100644 --- a/src/pages/inviteCode/index.module.scss +++ b/src/pages/inviteCode/index.module.scss @@ -29,10 +29,19 @@ page { padding-bottom: 5px; font-size: 60px; font-weight: 500; + &_text { + color: #424c6f; + } + &_plus { + font-size: 40px; + vertical-align: top; + color: $color_main; + font-weight: 600; + } } .description { font-size: 28px; - color: #848689; + color: #686868; font-weight: 400; } } @@ -148,7 +157,7 @@ page { flex-flow: row nowrap; justify-content: space-between; align-items: center; - padding-top: 24px; + padding-top: 16px; padding-right: 48px; padding-left: 48px; background-color: #ffffff; @@ -156,6 +165,7 @@ page { padding-bottom: calc(20px + env(safe-area-inset-bottom)); &__text { font-weight: 500; + font-size: 28px; } } .codePreview { @@ -164,13 +174,15 @@ page { justify-content: center; align-items: center; position: relative; - top: -120px; + top: -90px; .imageContainer { width: 80vw; height: auto; + overflow: hidden; .image { width: 100%; height: 100%; + border-radius: 20px; } } .previewTips { diff --git a/src/pages/inviteCode/index.tsx b/src/pages/inviteCode/index.tsx index 21a1264..c09863d 100644 --- a/src/pages/inviteCode/index.tsx +++ b/src/pages/inviteCode/index.tsx @@ -3,265 +3,95 @@ import Taro, { useReady } from '@tarojs/taro' import { useRef, useState } from 'react' import styles from './index.module.scss' import useLogin from '@/use/useLogin' -import { alert, goLink } from '@/common/common' import { GenBarCodeOrQrCode, GetInvitationInfo, GetInvitationInfoApi, GetInviteCodeList, GetInviteeRecord } from '@/api/user' -import { getFilterData } from '@/common/util' import LayoutBlock from '@/components/layoutBlock' import { getCDNSource } from '@/common/constant' import NormalButton from '@/components/normalButton' -import Dialog from '@/components/Dialog' -import IconText from '@/components/iconText' -import BindSalesManDialog from '@/components/bindSalesManDialog' +import type { TablePropsType } from '@/components/table' +import Table from '@/components/table' +import type { InviteCodePopupRef } from '@/components/inviteCodePopup' +import InviteCodePopup from '@/components/inviteCodePopup' +import { GetInvitationRecordList } from '@/api/share' +import { formatDateTime } from '@/common/fotmat' + // 获取业务员信息 interface Param { inviter_id: number; inviter_name: string; phone: string } +// 需要传进来的表头数据示例 +const inviteColumns = [ + { + key: 'invitee', + title: '被邀请人', + dataIndex: 'invitee', + width: '35%', + }, + { + key: 'invitationWay', + title: '邀请方式', + dataIndex: 'invitationWay', + width: '30%', + }, + { + key: 'inviteTime', + title: '邀请时间', + dataIndex: 'inviteTime', + width: '35%', + }, +] +const defaultSize = 24 const BindSalesman = () => { useLogin() + const size = useRef(defaultSize) - const [inviteInfo, setInviteInfo] = useState({}) - - const [salesMan, setSalesMan] = useState(null) - // const { fetchData: GetInvitationInfoFetchData } = GetInvitationInfoApi() - // const getInvitationInfo = async() => { - // const res = await GetInvitationInfoFetchData(getFilterData({ ...inviteInfo })) - // res.success ? setSalesMan(res.data) : setSalesMan(null) - // } // 获取被邀请记录 - const { fetchData: getInviteeRecordAPI } = GetInviteeRecord() + const { fetchData: getInviteeRecordAPI } = GetInvitationRecordList() const getInviteeRecord = async() => { const res = await getInviteeRecordAPI() if (res.success) { - setSalesMan(res.data) - } - } - const { fetchData } = GetInvitationInfo() - // 获取邀请码 - const getInviteCode = async() => { - const res = await fetchData() - if (res.success) { - setInviteInfo(res.data) - // getInvitationInfo() - } - } - const [invite, setInvite] = useState(0) - const { fetchData: getInvitationListAPI } = GetInviteCodeList() - const getInvitationList = async() => { - const res = await getInvitationListAPI() - if (res.success) { - console.log('getInviteCode', res) - setInvite(res.data.total) - } - } - - const canvasNode = useRef(null) - const ctx = useRef(null) - const [showPopup, setShowPopup] = useState(false) - - const [loading, setLoading] = useState(false) - const { fetchData: genCode } = GenBarCodeOrQrCode() - // 生成二维码 - const genQRcode = async() => { - const res = await genCode({ content: `InviteCode:${inviteInfo.invitation_code}` }) - if (res.success) { - return res.data.qrcode_base64 - } - else { - setLoading(false) - } - } - const inviteDialog = useRef(null) - - const getImageObject = (canvas: Taro.Canvas, src: string) => { - return new Promise((resolve, reject) => { - console.log('getImageObject param', canvas, src) - const img = canvas.createImage() - img.src = src - img.onload = () => { - console.log('image===>', img) - resolve(img) - } - img.onerror = (err) => { - console.log('image error===>', err) - alert.error('图片加载失败') - reject(err) - } - }) - } - const doublePick = (num, minus = false) => { - if (minus) { - return num / 2 - } - else { - return num * 2 - } - } - const [targetImageUrl, setTargetImageUrl] = useState('') - // canvas 生成 图片 - const saveCanvasToImage = (canvas) => { - Taro.canvasToTempFilePath({ - canvas, - fileType: 'png', - success: (res) => { - console.log('tempFilePath', res.tempFilePath) - setTargetImageUrl(res.tempFilePath) - }, - fail: (error) => { - console.log('error', error) - }, - complete: () => { - setLoading(false) - }, - }) - } - const [, setForceUpdate] = useState({}) - // 初始化 canvas - const initCanvas = async() => { - return new Promise((resolve, reject) => { - Taro.nextTick(() => { - const query = Taro.createSelectorQuery() - query.select('#canvas').node(({ node: canvas }: { node: Taro.Canvas }) => { - console.log('canvas==>', canvas) - const context = canvas.getContext('2d') - console.log('ctx', context) - canvasNode.current = canvas - ctx.current = context - console.log('canvas', canvas) - setForceUpdate({}) - resolve(true) - }).exec() - }) - }) - } - const [canvasStyle, setCanvasStyle] = useState({}) - const startPaint = async(ctx: Taro.RenderingContext, canvas: Taro.Canvas, image: Taro.Image) => { - // 开始绘制 - const { width, height } = canvas - console.log('startPaint param', ctx, canvas, image) - const cCanvasCtx = ctx as any - cCanvasCtx.clearRect(0, 0, width, height) - cCanvasCtx.drawImage(image, 0, 0, width, height) - cCanvasCtx.save() - cCanvasCtx.font = `${doublePick(40)}px 微软雅黑` - cCanvasCtx.fillStyle = '#000000' - cCanvasCtx.fillText('商城', doublePick(40), doublePick(80)) // text up above canvas - cCanvasCtx.save() - cCanvasCtx.font = `${doublePick(26)}px 微软雅黑` - cCanvasCtx.fillStyle = '#8f9398' - cCanvasCtx.fillText('邀请好友加入', doublePick(40), doublePick(130)) // text up above canvas - cCanvasCtx.save() - cCanvasCtx.font = `${doublePick(24)}px 微软雅黑` - cCanvasCtx.fillStyle = '#a6a6a6' - cCanvasCtx.fillText('请前往商城,进行填写或扫描邀请', doublePick(100), doublePick(630)) // text up above canvas - cCanvasCtx.save() - cCanvasCtx.font = `${doublePick(36)}px 微软雅黑` - cCanvasCtx.fillStyle = '#7f7f7f' - cCanvasCtx.fillText('邀 请 码', doublePick(72), doublePick(730)) // text up above canvas - cCanvasCtx.save() - cCanvasCtx.font = `${doublePick(24)}px 微软雅黑` - cCanvasCtx.fillStyle = '#cccccc' - cCanvasCtx.fillText('|', doublePick(258), doublePick(724)) // text up above canvas - cCanvasCtx.save() - cCanvasCtx.font = `${doublePick(36)}px 微软雅黑` - cCanvasCtx.fillStyle = '#7f7f7f' - cCanvasCtx.fillText(`${inviteInfo.invitation_code}`, doublePick(311), doublePick(730)) // text up above canvas - cCanvasCtx.save() - const codeUrl = await genQRcode() - try { - const code = await getImageObject(canvas, codeUrl) - cCanvasCtx.drawImage(code, doublePick(110), doublePick(213), doublePick(342), doublePick(342)) - } - catch (err) { - console.error('合成二维邀请码失败', err) - setLoading(false) - throw new Error('合成二维邀请码失败') - } - - saveCanvasToImage(canvas) - } - // 绘制最终的海报,图片使用两倍大小,canvas大小是图片的二分之一 就能让canvas生成出来的图片清晰了 - const drawPictorial = async() => { - // eslint-disable-next-line no-async-promise-executor - return new Promise(async(resolve, reject) => { - setLoading(true) - if (!ctx.current) { - // 重新初始化canvas - await initCanvas() - } - const canvas = canvasNode.current! - Taro.getImageInfo({ - src: getCDNSource('/user/inviteCodePopup.png'), - success: (res) => { - console.log('res==>', res) - canvas.width = res.width - canvas.height = res.height - setCanvasStyle({ - width: `${doublePick(canvas.width, true)}px`, - height: `${doublePick(canvas.height, true)}px`, - }) - getImageObject(canvas, `${res.path}`).then(async(image) => { - try { - // 开始绘制 - await startPaint(ctx.current!, canvas, image) - resolve(true) - } - catch (err) { - console.log(err) - setLoading(false) - reject(new Error('绘制失败')) - } - }).catch((error) => { - setLoading(false) - throw new Error(error) - }) + setCurrentTable((prev: any) => ({ + ...prev, + dataSource: { + list: res.data.list.map((item, index: number) => ({ + key: index, + invitationWay: item.invitation_way_name || '--', + invitee: item.passive_invited_user_name || '--', + inviteTime: formatDateTime(item.invitation_time, 'YYYY-MM-DD') || '--', + })), + total: res.data.list.length, }, - }) - }) - } - // 复制二维码 - const handleCopyInviteCode = () => { - Taro.setClipboardData({ - data: inviteInfo.invitation_code, - }) - } - const handleChange = (value: boolean) => { - setShowPopup(value) + })) + } } + useReady(() => { - getInviteCode() - getInvitationList() getInviteeRecord() - setTimeout(() => { - initCanvas() - }, 200) }) + + const inviteCodePopupRef = useRef(null) + const handleQRcodeShare = async() => { - try { - const flag = await drawPictorial() - if (flag) { - setShowPopup(true) - } - } - catch (err) { - throw new Error('弹出二维码失败') - } + inviteCodePopupRef.current?.startDrawPoster() } - // 邀请记录 - const handleInviteCord = () => { - goLink('/pages/inviteCode/inviteCord/index') - } - const handleBindSalesMan = () => { - inviteDialog.current.handleChange(true) - } - const handleBindSuccess = () => { - getInviteeRecord() + + const [currentTable, setCurrentTable] = useState({ + columns: inviteColumns, + dataSource: { list: [], total: 0 }, + }) + + const handleLoadMore = () => { + size.current += defaultSize } + return ( - 商城 - 邀请好友加入 + + 面料优选A + + + + 查看成功邀请人信息 @@ -270,65 +100,24 @@ const BindSalesman = () => { - - - - {inviteInfo.invitation_code ? inviteInfo.invitation_code : '暂无邀请码'} - - 您的专属邀请码 + + + + 成功邀请 + + + +
- - - - 我的邀请记录: - {invite === 0 && 暂无邀请信息} - - {invite !== 0 && {invite}人} - - - - - - - - - 朋友邀请我: - {!salesMan?.inviter_id && 未绑定} - - {!!salesMan?.inviter_id && - 已绑定 {salesMan.inviter_name}({salesMan.phone}) - } - {!salesMan?.inviter_id - && - - - } - - - 温馨提示:邀请码确定绑定后,不支持解绑。
- {/* 已踩坑,这里必须设置canvas的style的width和height,单单只设置canvas实例的width和height是不行的。会模糊! */} - - - 二维码分享 - - - 复制邀请码 + + 生成邀请海报 - - - - {/* showMenuByLongpress 属性只对 小程序有效 */} - - - 长按图片保存到手机 - - - +
) } diff --git a/src/pages/inviteCode/inviteCord/index.config.ts b/src/pages/inviteCode/inviteCord/index.config.ts deleted file mode 100644 index cf00b9b..0000000 --- a/src/pages/inviteCode/inviteCord/index.config.ts +++ /dev/null @@ -1,7 +0,0 @@ -export default { - navigationBarTitleText: '邀请记录', - navigationBarTextStyle: 'black', - navigationBarBackgroundColor: '#E4EEFD', - backgroundColor: '#E4EEFD', - backgroundColorTop: '#E4EEFD', -} diff --git a/src/pages/inviteCode/inviteCord/index.module.scss b/src/pages/inviteCode/inviteCord/index.module.scss deleted file mode 100644 index 008b84d..0000000 --- a/src/pages/inviteCode/inviteCord/index.module.scss +++ /dev/null @@ -1,163 +0,0 @@ -page { - display: flex; - flex-flow: column nowrap; - height: 100%; -} -.main { - display: flex; - flex-flow: column nowrap; - height: 100%; - background: linear-gradient(to bottom, #e4eefd 25%, $color_bg_one 42%); - padding-bottom: 0; - box-sizing: border-box; - overflow-y: scroll; - .content { - flex: 1 1 auto; - overflow: scroll; - } -} -.background { - display: flex; - flex-flow: row nowrap; - padding: 20px 56px; - padding-bottom: 0; - align-items: center; - justify-content: space-between; - overflow: hidden; - .left { - .title { - padding-bottom: 5px; - font-size: 60px; - font-weight: 500; - } - .description { - font-size: 28px; - color: #848689; - font-weight: 400; - } - } - .right { - .iconContainer { - width: 260px; - position: relative; - bottom: -36px; - .icon { - width: 130%; - } - } - } -} -.inviteCodeContent { - position: relative; - top: -20px; -} -.codeBar { - border-radius: 15px; - background-color: #f7f8fa; - padding: 40px 0; - .inviteCodeBar { - display: flex; - flex-flow: row nowrap; - justify-content: center; - align-items: center; - margin-bottom: 16px; - .invite { - padding: 0 40px; - font-size: 60px; - font-weight: 500; - color: #337fff; - line-height: 65px; - } - } - .codeTitle { - display: flex; - flex-flow: row nowrap; - justify-content: center; - align-items: center; - letter-spacing: 10px; - font-size: 40px; - font-weight: 400; - color: #9fa0a1; - line-height: 28px; - padding: 10px 40px; - padding-bottom: 0; - } -} -.tips { - display: flex; - flex-flow: row nowrap; - justify-content: center; - align-items: center; - font-size: 24px; - font-weight: 400; - color: #9fa0a1; - line-height: 28px; - padding: 0 40px; -} -.inviteListTitle { - display: flex; - flex-flow: row nowrap; - justify-content: center; - align-items: center; - padding-top: 15px; - padding-bottom: 30px; - .listTitle { - padding: 0 20px; - font-size: 32px; - font-weight: 500; - color: #000000; - } - .titleIconLeft { - width: 24px; - height: 4px; - background: linear-gradient(270deg, #333333 0%, rgba(51, 51, 51, 0) 100%); - opacity: 0.3; - } - .titleIconRight { - width: 24px; - height: 4px; - background: linear-gradient(270deg, rgba(51, 51, 51, 0) 0%, #333333 100%); - opacity: 0.3; - } -} -.bottomBar { - flex: none; - display: flex; - flex-flow: row nowrap; - justify-content: space-between; - align-items: center; - padding-top: 24px; - padding-right: 48px; - padding-left: 48px; - background-color: #ffffff; - padding-bottom: calc(20px + constant(safe-area-inset-bottom)); - padding-bottom: calc(20px + env(safe-area-inset-bottom)); - &__text { - font-weight: 500; - } -} -.codePreview { - display: flex; - flex-flow: column nowrap; - justify-content: center; - align-items: center; - position: relative; - top: -120px; - .imageContainer { - width: 80vw; - height: auto; - .image { - width: 100%; - height: 100%; - } - } - .previewTips { - position: absolute; - bottom: -80px; - font-size: 40px; - font-weight: 400; - color: #c2c2c2; - letter-spacing: 5px; - white-space: nowrap; - } -} diff --git a/src/pages/inviteCode/inviteCord/index.tsx b/src/pages/inviteCode/inviteCord/index.tsx deleted file mode 100644 index 9a8cd8b..0000000 --- a/src/pages/inviteCode/inviteCord/index.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import { Image, Swiper, SwiperItem, Text, View } from '@tarojs/components' -import Taro, { useReady } from '@tarojs/taro' -import { useEffect, useRef, useState } from 'react' -import styles from './index.module.scss' -import { formatImgUrl } from '@/common/fotmat' -import { goLink } from '@/common/common' -import CloseBtn from '@/components/closeBtn' -import { getCDNSource } from '@/common/constant' -import LayoutBlock from '@/components/layoutBlock' -import type { TablePropsType } from '@/components/table' -import Table from '@/components/table' -import { GetInviteCodeList } from '@/api/user' -// 需要传进来的表头数据示例 -const inviteColumns = [ - { - key: 'invitee', - title: '被邀请人', - dataIndex: 'invitee', - width: '50%', - }, - { - key: 'InviteTime', - title: '邀请时间', - dataIndex: 'InviteTime', - width: '50%', - }, -] -const defaultSize = 24 -const InviteCord = () => { - const size = useRef(defaultSize) - const [currentTable, setCurrentTable] = useState({ - columns: inviteColumns, - dataSource: { list: [], total: 0 }, - }) - const { fetchData: getInvitationListAPI } = GetInviteCodeList() - const getInvitationList = async() => { - const res = await getInvitationListAPI({ size: size.current }) - if (res.success) { - console.log('getInviteCode', res) - setCurrentTable((prev: any) => ({ - ...prev, - dataSource: { - list: res.data.list.map((item, index: number) => ({ - key: index, - index: index + 1, - invitee: item.invitee_name || '--', - InviteTime: item.schedule, - })), - total: res.data.list.length, - }, - })) - } - } - const handleLoadMore = () => { - size.current += defaultSize - } - useReady(() => { - getInvitationList() - }) - return ( - - - - - 邀请记录 - 查看成功邀请人信息 - - - - - - - - - - - - 成功邀请 - - - -
-
-
-
-
-
- ) -} - -export default InviteCord diff --git a/src/pages/inviteCode/inviteFriends/index.config.ts b/src/pages/inviteCode/inviteFriends/index.config.ts new file mode 100644 index 0000000..32067c2 --- /dev/null +++ b/src/pages/inviteCode/inviteFriends/index.config.ts @@ -0,0 +1,5 @@ +export default { + navigationBarTitleText: '邀请好友', + enableShareAppMessage: true, + navigationBarTextStyle: 'black', +} diff --git a/src/pages/inviteCode/inviteFriends/index.module.scss b/src/pages/inviteCode/inviteFriends/index.module.scss new file mode 100644 index 0000000..25fd85c --- /dev/null +++ b/src/pages/inviteCode/inviteFriends/index.module.scss @@ -0,0 +1,78 @@ +page { + display: flex; + flex-flow: column nowrap; + height: 100%; +} +.main { + display: flex; + flex-flow: column nowrap; + height: 100%; + box-sizing: border-box; + .search { + width: 100%; + height: 96px; + background-color: #fff; + padding: 16px 24px; + box-sizing: border-box; + display: flex; + justify-content: space-between; + border-bottom: 1px solid rgba(0, 0, 0, 0.1); + .flexBox { + margin-left: 32px; + } + } + .content { + flex: 1 1 auto; + overflow: hidden; + background-color: #f7f7f7; + display: flex; + flex-flow: column nowrap; + .total { + padding: 24px; + padding-bottom: 12px; + color: #a6a6a6; + font-size: 28px; + } + .scroll_list { + flex: 1 1 auto; + overflow: scroll; + } + } + .bottomBar { + display: flex; + flex-flow: row nowrap; + justify-content: space-between; + align-items: center; + width: 100%; + box-sizing: border-box; + background-color: #fff; + padding-top: 16px; + padding-left: 48px; + padding-right: 48px; + padding-bottom: 16px; + padding-bottom: constant(safe-area-inset-bottom); + padding-bottom: env(safe-area-inset-bottom); + } +} +.layout { + padding: 24px; + margin-top: 16px; + margin-bottom: 24px; + &:first-child { + margin-top: 0; + } +} +.title { + display: flex; + flex-flow: row nowrap; + justify-content: space-between; + font-size: 28px; + font-weight: 550; +} +.block_content { + font-size: 28px; + color: #6f6f6f; + .cell_desc { + color: #6f6f6f; + } +} diff --git a/src/pages/inviteCode/inviteFriends/index.tsx b/src/pages/inviteCode/inviteFriends/index.tsx new file mode 100644 index 0000000..e84e95e --- /dev/null +++ b/src/pages/inviteCode/inviteFriends/index.tsx @@ -0,0 +1,170 @@ + +import { ScrollView, Text, View } from '@tarojs/components' +import Taro, { usePullDownRefresh, useReady } from '@tarojs/taro' +import classNames from 'classnames' +import { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import styles from './index.module.scss' +import Search from '@/components/searchBar' +import IconText from '@/components/iconText' +import NormalButton from '@/components/normalButton' +import LayoutBlock from '@/components/layoutBlock' +import Divider from '@/components/divider' +import Cell from '@/components/cell' +import TimePickerPopup from '@/components/timePickerPopup' +import { alert } from '@/common/common' +import type { InviteCodePopupRef } from '@/components/inviteCodePopup' +import InviteCodePopup from '@/components/inviteCodePopup' +import { GetInvitationRecordList } from '@/api/share' +import InfiniteScroll from '@/components/infiniteScroll' +import { dataLoadingStatus, debounce, getFilterData } from '@/common/util' +import { formatDateTime } from '@/common/fotmat' + +const InviteCord = () => { + const [formData, setFormData] = useState<{ + start_time?: string + end_time?: string + name?: string + size?: number + page?: number + }>({ + size: 15, + page: 1, + }) + const onSearch = debounce((e) => { + pageNum.current.page = 1 + setFormData(val => ({ ...val, name: e, size: 10 })) + pageNum.current = { size: 10, page: 1 } + }, 300) + + // 获取被邀请记录 + const { fetchData: getInviteeRecordAPI, state } = GetInvitationRecordList() + const getInviteeRecord = async() => { + const res = await getInviteeRecordAPI(getFilterData(formData)) + if (res.success) { + setRecordList({ list: res.data.list, total: res.data.total }) + Taro.stopPullDownRefresh() + } + } + + // 页面下拉刷新 + usePullDownRefresh(() => { + setFormData(prev => ({ ...prev, size: 10 })) + }) + + useEffect(() => { + getInviteeRecord() + }, [formData]) + + const handleConfirm = () => { + inviteCodePopupRef.current?.startDrawPoster() + } + + const [recordList, setRecordList] = useState<{ list: any[]; total: number }>({ list: [], total: 0 }) + + const [showTime, setShowTime] = useState(false) + + const isFilter = useMemo(() => { + if (formData?.start_time || formData?.end_time) { + return true + } + return false + } + , [formData?.start_time, formData?.end_time]) + + const handleClickTimePicker = () => { + setShowTime(true) + } + + const handClose = () => { + setShowTime(false) + } + const onSelectDate = useCallback((val) => { + if (!val.value?.start && !val.value?.end) { + alert.error('请选择日期') + } + else { + setFormData(e => ({ ...e, start_time: val.value.start, end_time: val.value.end })) + } + console.log('val::', val) + handClose() + }, []) + + const handleReset = () => { + setFormData(e => ({ + ...e, + start_time: '', + end_time: '', + })) + handClose() + } + + const total = useMemo(() => { + if (formData.name || isFilter) { + return recordList.list.length + } + return recordList.total + }, [isFilter, formData.name, recordList]) + + const inviteCodePopupRef = useRef(null) + + // 上拉加载数据 + const pageNum = useRef({ size: formData.size, page: formData.page }) + const getScrollToLower = () => { + if (recordList.list.length < recordList.total) { + pageNum.current.page!++ + const size = pageNum.current.size! * pageNum.current.page! + setFormData(prev => ({ ...prev, size })) + } + } + + const statusMore = useMemo(() => { + return dataLoadingStatus({ list: recordList.list, total: recordList.total, status: state.loading }) + }, [recordList, state]) + + return ( + + + + + + + + + + 共 {total} 条数据 + + + {recordList.list.map((item, index) => { + return ( + + + {item?.passive_invited_user_name} + {item?.invitation_way_name} + + + + + + + + ) + })} + + + + + 生成邀请海报 + + 重置 + } + onSelectDate={onSelectDate} + /> + + + + ) +} + +export default InviteCord diff --git a/src/pages/searchList/searchList.tsx b/src/pages/searchList/searchList.tsx index c7f72f6..9345ecd 100644 --- a/src/pages/searchList/searchList.tsx +++ b/src/pages/searchList/searchList.tsx @@ -1,6 +1,7 @@ import { Text, View } from '@tarojs/components' import Taro, { usePullDownRefresh, useReady, useRouter } from '@tarojs/taro' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import type { ListProps } from './components/selectData' import SelectData from './components/selectData' import styles from './searchList.module.scss' import Search from '@/components/search' diff --git a/src/pages/user/index.module.scss b/src/pages/user/index.module.scss index 8a4db06..fb4ed98 100644 --- a/src/pages/user/index.module.scss +++ b/src/pages/user/index.module.scss @@ -53,7 +53,22 @@ flex-direction: column; flex: 1; position: relative; - + .header_user{ + display: flex; + align-items: center; + } + .header_user_label { + width: 81px; + height: 44px; + .BD_label { + width: 100%; + height: 100%; + } + } + .header_user_name { + font-size: 36px; + font-weight: 500; + } .arcd-info-left-phone { position: relative; .header_title { @@ -63,10 +78,6 @@ } } text { - &:nth-child(1) { - font-size: 48px; - font-weight: 500; - } &:nth-child(2) { font-size: 28px; color: rgba(0, 0, 0, 0.6); diff --git a/src/pages/user/index.tsx b/src/pages/user/index.tsx index 366f63d..a4de480 100644 --- a/src/pages/user/index.tsx +++ b/src/pages/user/index.tsx @@ -11,8 +11,7 @@ import { userassets, userorderStatistics } from '@/api/mine' import useLogin from '@/use/useLogin' import IconFont from '@/components/iconfont/iconfont' import SvgIconfont from '@/components/svgIconfont' -import MoveBtn from '@/components/moveBtn' -import { BASE_URL } from '@/common/constant' +import { BASE_URL, getCDNSource } from '@/common/constant' export default () => { const userInfo = useSelector(state => state.userInfo) @@ -88,8 +87,17 @@ export default () => { Taro.stopPullDownRefresh() }) - const [current_version, setCurrent_version] = useState(CURRENT_VERSION) - const [current_env, setCurrent_env] = useState(CURRENT_ENV) + const handleClickInviteFriends = () => { + if (!userInfo?.adminUserInfo?.is_bd) { + goLink('/pages/inviteCode/index') + } + else { + goLink('/pages/inviteCode/inviteFriends/index') + } + } + + const [current_env, _] = useState(CURRENT_ENV) + const [current_version, __] = useState(CURRENT_VERSION) return ( @@ -101,7 +109,14 @@ export default () => { - {userInfo?.adminUserInfo?.phone ? userInfo?.adminUserInfo?.user_name : '点击登录'} + + {userInfo?.adminUserInfo?.phone ? userInfo?.adminUserInfo?.user_name : '点击登录'} + { + userInfo?.adminUserInfo?.is_bd && + + + } + {userInfo?.adminUserInfo?.phone || 'Hi,欢迎来到商城'} @@ -122,9 +137,9 @@ export default () => { 0 活动积分 - goLink('/pages/inviteCode/index')}> + - 邀请码 + 邀请好友 diff --git a/src/pages/userEdit copy/index.tsx b/src/pages/userEdit copy/index.tsx index 9d4ca93..0ce7cab 100644 --- a/src/pages/userEdit copy/index.tsx +++ b/src/pages/userEdit copy/index.tsx @@ -3,7 +3,7 @@ import Taro, { chooseMedia } from '@tarojs/taro' import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react' import { alert, goLink, isEmptyObject, retrieval } from '@/common/common' import Popup from '@/components/popup' -import { companyTypeApi, portraitUpdateApi, realNameUpdateApi } from '@/api/user' +import { CompanyTypeApi, PortraitUpdateApi, RealNameUpdateApi } from '@/api/user' import { companyDetailApi, companyUpdateApi } from '@/api/company' import './index.scss' import ModifyModal from './components/ModifyModal' @@ -33,7 +33,7 @@ export default () => { // 表单数据 const [formData, setFormData] = useState(adminUserInfo) // 昵称修改保存 - const { fetchData: realNameUpdateFetch } = realNameUpdateApi() + const { fetchData: realNameUpdateFetch } = RealNameUpdateApi() const rules = { text: [ { @@ -77,7 +77,7 @@ export default () => { goLink(url) } // 肖像编辑 - const { fetchData: portraitUpdateFetch } = portraitUpdateApi() + const { fetchData: portraitUpdateFetch } = PortraitUpdateApi() const { getWxPhoto } = useUploadCDNImg() const handleSelectRortrait = () => { Taro.showModal({ @@ -112,7 +112,7 @@ export default () => { const ModifyIcknameEl = useRef(null) const ModifyCompanyNameEl = useRef(null) // 获取企业类型 - const { fetchData: companyTypeFetch, state: companyTypeData } = companyTypeApi() + const { fetchData: companyTypeFetch, state: companyTypeData } = CompanyTypeApi() const getCompanyTypeData = async() => { const reuslt = await companyTypeFetch() if (reuslt.success) { diff --git a/src/pages/userEdit/index.tsx b/src/pages/userEdit/index.tsx index ee018bf..d373f3f 100644 --- a/src/pages/userEdit/index.tsx +++ b/src/pages/userEdit/index.tsx @@ -5,7 +5,7 @@ import type { ModifyModalRef } from './components/ModifyModal' import ModifyModal from './components/ModifyModal' import { alert, goLink, isEmptyObject, retrieval } from '@/common/common' import Popup from '@/components/popup' -import { companyTypeApi, portraitUpdateApi, realNameUpdateApi } from '@/api/user' +import { CompanyTypeApi, PortraitUpdateApi, RealNameUpdateApi } from '@/api/user' import { companyDetailApi, companyUpdateApi } from '@/api/company' import './index.scss' import useLogin from '@/use/useLogin' @@ -45,7 +45,7 @@ export default () => { }, [adminUserInfo]) // 昵称修改保存 - const { fetchData: realNameUpdateFetch } = realNameUpdateApi() + const { fetchData: realNameUpdateFetch } = RealNameUpdateApi() const rules = { text: [ { @@ -89,7 +89,7 @@ export default () => { goLink(url) } // 肖像编辑 - const { fetchData: portraitUpdateFetch } = portraitUpdateApi() + const { fetchData: portraitUpdateFetch } = PortraitUpdateApi() const { getWxPhoto } = useUploadCDNImg() const handleSelectRortrait = () => { Taro.showModal({ @@ -123,7 +123,7 @@ export default () => { } // 获取企业类型 - const { fetchData: companyTypeFetch, state: companyTypeData } = companyTypeApi() + const { fetchData: companyTypeFetch, state: companyTypeData } = CompanyTypeApi() const getCompanyTypeData = async() => { const reuslt = await companyTypeFetch() if (reuslt.success) { diff --git a/src/reducers/userInfo.ts b/src/reducers/userInfo.ts index 36e087b..3766055 100644 --- a/src/reducers/userInfo.ts +++ b/src/reducers/userInfo.ts @@ -40,6 +40,8 @@ export interface UserAdminParam { wechat_user_open_id: number is_authorize_name: boolean is_authorize_phone: boolean + is_passive_invite: boolean // 是否被邀请 + is_bd: boolean // 是否是bd phone: string authentication_status: number authentication_status_name: string @@ -59,7 +61,7 @@ export interface UserAdminParam { } export interface SortCodeParam { - shareShortDetail?: { title: string; code: string; img: string } // 详情分享页面短码 + shareShortDetail?: { id: number; title: string; code: string; img: string } // 详情分享页面短码 shareShortPage?: { title: string; code: string; img: string } // 右上角分享页面短码 } diff --git a/src/styles/variables/default.scss b/src/styles/variables/default.scss index 8ef0cb5..e4c9acf 100644 --- a/src/styles/variables/default.scss +++ b/src/styles/variables/default.scss @@ -210,7 +210,7 @@ $at-fab-box-shadow-active: $at-calendar-day-size: 72px !default; $at-calendar-mark-size: 8px !default; $at-calendar-header-color: #B8BFC6 !default; -$at-calendar-main-color: $color-brand !default; +$at-calendar-main-color: #eaf2ff !default; $at-calendar-day-color: #7C86A2 !default; /* Card */ diff --git a/src/use/useLogin.ts b/src/use/useLogin.ts index 578b604..ed0a87b 100644 --- a/src/use/useLogin.ts +++ b/src/use/useLogin.ts @@ -1,13 +1,14 @@ -import Taro, { useDidShow, useRouter } from '@tarojs/taro' +import Taro, { useDidShow } from '@tarojs/taro' import useUserInfo from './useUserInfo' import useLoginRequest from './useLoginRequest' import { BindingCompanyApi, GetAdminUserInfoApi, GetPhoneNumberApi, GetWxUserInfoApi } from '@/api/user' +import type { InvitationWay } from '@/common/enum' import { SHARE_SCENE } from '@/common/enum' -import { GetShortCodeApi } from '@/api/share' -import { alert } from '@/common/common' -import { LoginApi } from '@/api/login' +import { GetShortCodeApi, PrepareCreateInvitationInfoApi } from '@/api/share' import { IMG_CND_Prefix } from '@/common/constant' import { formatImgUrl } from '@/common/fotmat' +import { bindInvitationUser } from '@/common/shortCode' +import { isEmptyObject } from '@/common/common' export default () => { const { setUserInfo, setAdminUserInfo, setSortCode, userInfo } = useUserInfo() @@ -47,7 +48,8 @@ export default () => { if (res.success) { setUserInfo({ ...userInfo.userInfo, phone: res.data.phone_number }) await fetchBindingCompany() - getAdminUserInfo() + await getAdminUserInfo() + handleBindInvitationUser() resolve(res.data) } else { @@ -57,11 +59,10 @@ export default () => { } // 登录请求 const { login } = useLoginRequest() - // const {fetchData:login} = LoginApi() const wxLogin = async() => { try { await login() - getAdminUserInfo() + await getAdminUserInfo() } catch (e) { console.log('登录失败::', e) @@ -128,6 +129,22 @@ export default () => { }) }) } + + const handleBindInvitationUser = () => { + // 从缓存拿出 + const newPageInfo = JSON.parse(Taro.getStorageSync('invitationInfo') || '{}') + console.log('handleBindInvitationUser router', newPageInfo) + // else if (page && page.options?.user_id && invitationWay.includes(Number(page.options?.invitation_way) as InvitationWay)) { + if (!isEmptyObject(newPageInfo)) { + const { user_id, invitation_way } = newPageInfo! + // 获取最新的用户信息 + // 校验是否被绑定过了 自己不能邀请自己 + if (!userInfo.adminUserInfo.is_passive_invite && newPageInfo?.user_id !== userInfo.adminUserInfo.user_id) { + bindInvitationUser(Number(invitation_way) as InvitationWay, Number(user_id)) + } + } + } + return { checkLogin, wxLogin, diff --git a/src/use/useLoginRequest.ts b/src/use/useLoginRequest.ts index 7e536ec..21caa2a 100644 --- a/src/use/useLoginRequest.ts +++ b/src/use/useLoginRequest.ts @@ -20,17 +20,23 @@ export default () => { const loginData = useRef(initData) const { setToken, setSessionKey } = useUserInfo() const router = useRouter() + + const page = Taro.getCurrentInstance().page // 微信登录请求v2 const fetchDataLogin = async(login_code) => { const q = { - url: `${BASE_URL}/v1/mall/login`, + url: `${BASE_URL}/v3/mallCherry/login`, header: { // Platform: 6, Platform, Appid: WX_APPID, }, method: 'post', - data: { js_code: login_code }, + data: { + js_code: login_code, + invitation_way: Number(page?.options?.invitation_way), + invited_user_id: Number(page?.options?.user_id), + }, } try { const result = await Taro.request(q as any)