feat(ID1000916): 新增分享记录和来源统计

【新增分享记录和来源统计】 https://www.tapd.cn/53459131/prong/stories/view/1153459131001000916
This commit is contained in:
xuan 2023-03-01 11:06:29 +08:00
parent 5a1d6dddb0
commit 4fcd4de555
48 changed files with 1280 additions and 1005 deletions

View File

@ -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',
})
}

View File

@ -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',
})
}

View File

@ -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,
})
}

View File

@ -196,7 +196,7 @@ export default {
root: 'pages/inviteCode',
pages: [
'index',
'inviteCord/index',
'inviteFriends/index',
],
},
{

View File

@ -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(() => {

View File

@ -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
// 生成密钥

View File

@ -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,
]

View File

@ -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) {
// 绑定上下级
}
},
})
}

View File

@ -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<UserAdminParam>((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 }))
}
},
})
}

View File

@ -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<string, any> = {}
sceneArr.forEach((item) => {
const itemArr = item.split('=')
sceneObj[itemArr[0]] = itemArr[1]
})
return sceneObj
}
function handleBindInvitationUser() {
}

View File

@ -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

View File

@ -1,3 +1,5 @@
export const DAY_LIST = ['日', '一', '二', '三', '四', '五', '六']
export const TYPE_PRE_MONTH = -1
export const TYPE_NOW_MONTH = 0

View File

@ -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'

View File

@ -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

View File

@ -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,

View File

@ -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;
}
}
}

View File

@ -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'

View File

@ -33,9 +33,9 @@ declare namespace Calendar {
isToday?: boolean
isBeforeMin?: boolean
isRowEnd?: boolean
isAfterMax?: boolean
isRowStart?: boolean
isDisabled?: boolean

View File

@ -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<number, string> = {
[constant.TYPE_PRE_MONTH]: 'pre',
@ -34,47 +35,69 @@ export default class AtCalendarList extends React.Component<Props> {
public render(): JSX.Element | null {
const { list } = this.props
if (!list || list.length === 0) { return null }
let row = 0 // 当前行数 第 0 行就是第一行
return (
<View className="at-calendar__list flex">
{list.map((item: Calendar.Item) => (
<View
key={`list-item-${item.value}`}
onClick={this.handleClick.bind(this, item)}
onLongPress={this.handleLongClick.bind(this, item)}
className={classnames(
'flex__item',
`flex__item--${MAP[item.type]}`,
{
'flex__item--today': item.isToday,
'flex__item--active': item.isActive,
'flex__item--selected': item.isSelected,
'flex__item--selected-head': item.isSelectedHead,
'flex__item--selected-tail': item.isSelectedTail,
'flex__item--blur':
{list.map((item: Calendar.Item, index) => {
const isRowStart = DAY_LIST.length * row === index
const isRowEnd = (index + 1) % DAY_LIST.length === 0
if (isRowStart) {
row++
}
return (
<View
key={`list-item-${item.value}`}
onClick={this.handleClick.bind(this, item)}
onLongPress={this.handleLongClick.bind(this, item)}
className={classnames(
'flex__item',
`flex__item--${MAP[item.type]}`,
{
'flex__item--today': item.isToday,
'flex__item--active': item.isActive,
'flex__item--selected': item.isSelected,
'flex__item--selected-row-end': item.isSelected && isRowEnd,
'flex__item--selected-row-start': item.isSelected && isRowStart,
'flex__item--selected-head': item.isSelectedHead,
'flex__item--selected-tail': item.isSelectedTail,
'flex__item--blur':
item.isDisabled
|| item.type === constant.TYPE_PRE_MONTH
|| item.type === constant.TYPE_NEXT_MONTH,
},
)}
>
<View className="flex__item-container">
<View className="container-text">{item.text}</View>
},
)}
>
<View className={classnames(
{
'flex__item--selected-head-cover': item.isSelectedHead,
'flex__item--selected-tail-cover': item.isSelectedTail,
},
)}
>
<View className="flex__item-container">
<View className="container-text">{item.text}</View>
</View>
<View className="flex__item-extra extra">
{item.isToday
? <View className="today-mark"></View>
: null}
{item.marks && item.marks.length > 0
? (
<View className="extra-marks">
{item.marks.map((mark, key) => (
<Text key={key} className="mark">
{mark.value as React.ReactNode}
</Text>
))}
</View>
)
: null}
</View>
</View>
</View>
<View className="flex__item-extra extra">
{item.marks && item.marks.length > 0
? (
<View className="extra-marks">
{item.marks.map((mark, key) => (
<Text key={key} className="mark">
{mark.value as React.ReactNode}
</Text>
))}
</View>
)
: null}
</View>
</View>
))}
)
})}
</View>
)
}

View File

@ -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 (
<View className="at-calendar__header header">
<View className="header__flex">
<View className="header__flex-item"></View>
<View className="header__flex-item"></View>
<View className="header__flex-item"></View>
<View className="header__flex-item"></View>
<View className="header__flex-item"></View>
<View className="header__flex-item"></View>
<View className="header__flex-item"></View>
{
DAY_LIST.map((day, index) => (
<View key={index} className="header__flex-item">{day}</View>
))
}
</View>
</View>
)

View File

@ -34,7 +34,7 @@ const Cell: FC<CellPropsType> = (props) => {
<View>{title}</View>
</View>
<View className={styles.desc}>
<Text className={classNames(styles.descText, customDescClassName)}>{desc}</Text>
<View className={classNames(styles.descText, customDescClassName)}>{desc}</View>
{isLink && <IconFont name="icon-rukou" size={46} color="inherit"></IconFont>}
</View>
</View>

View File

@ -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<PropsType> = ({
name,
size = 36,
color,
customStyle = {},
customClassName = ""
}) => {
const [colors, setColors] = useState<PropsType['color']>()
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 <view> } 来渲染但是测试发现在ios下会有问题报错 Maximum call stack啥的。下面这个写法没问题
return (
<Block>
{/* icon-colorCard 本地svg */ }
{/* { name === 'icon-colorCard' && (<View style={{backgroundImage: `url(${quot}data:image/svg+xml, %3Csvg xmlns='http://www.w3.org/2000/svg' width='${svgSize}px' height='${svgSize}px' viewBox='0 0 72 72'><defs><linearGradient id='a' x1='56.049%' x2='45.965%' y1='85.384%' y2='36.243%'><stop offset='0%' stop-color='${(isStr ? colors : colors?.[0]) || '%233667EF'}' stop-opacity='.572'/><stop offset='100%' stop-color='${(isStr ? colors : colors?.[1]) || '%233591FD'}' stop-opacity='.551'/></linearGradient><linearGradient id='b' x1='100%' x2='16.645%' y1='85.384%' y2='36.243%'><stop offset='0%' stop-color='${(isStr ? colors : colors?.[2]) || '%233667EF'}' stop-opacity='.572'/><stop offset='100%' stop-color='${(isStr ? colors : colors?.[3]) || '%233591FD'}' stop-opacity='.551'/></linearGradient><linearGradient id='c' x1='18.906%' x2='80.404%' y1='44.444%' y2='55.556%'><stop offset='0%' stop-color='${(isStr ? colors : colors?.[4]) || '%233591FD'}'/><stop offset='100%' stop-color='${(isStr ? colors : colors?.[5]) || '%233667EF'}'/></linearGradient></defs><g fill='none' fill-rule='nonzero'><path fill='url(%23a)' d='M24.75 11.25A2.25 2.25 0 0 1 27 13.5v47.25A2.25 2.25 0 0 1 24.75 63h-13.5A2.25 2.25 0 0 1 9 60.75V13.5a2.25 2.25 0 0 1 2.25-2.25h13.5ZM18 50.625a3.375 3.375 0 1 0 0 6.75 3.375 3.375 0 0 0 0-6.75Z'/><path fill='url(%23b)' d='m45.593 16.216 9.546 9.546a2.25 2.25 0 0 1 0 3.182l-31.82 31.82a2.25 2.25 0 0 1-3.182 0L16.273 56.9a3.375 3.375 0 1 0-1.174-1.174l-4.508-4.508a2.25 2.25 0 0 1 0-3.182l31.82-31.82a2.25 2.25 0 0 1 3.182 0Z'/><path fill='url(%23c)' d='M60.75 45A2.25 2.25 0 0 1 63 47.25v13.5A2.25 2.25 0 0 1 60.75 63h-49.5A2.25 2.25 0 0 1 9 60.75v-13.5A2.25 2.25 0 0 1 11.25 45h49.5ZM18 50.625a3.375 3.375 0 1 0 0 6.75 3.375 3.375 0 0 0 0-6.75Z' opacity='.95'/></g></svg%3E${quot})`, width: `${svgSize}px`, height: `${svgSize}px`, ...customStyle}} className={classnames("icon", customClassName)} />) } */}
{/* icon-alipay */}
{/* {name === "icon-alipay" && (
<View
style={{
backgroundImage: `url(${quot}data:image/svg+xml, %3Csvg viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg' width='${svgSize}px' height='${svgSize}px'%3E%3Cpath d='M192 692.736c0-69.632 51.2-106.496 88.064-111.104 111.104-18.432 264.192 74.24 264.192 74.24-69.632 88.064-166.912 134.144-241.152 134.144-65.024-4.608-111.104-41.472-111.104-97.28z' fill='${(isStr
? colors
: colors[0]) ||
"rgb(91,139,212)"}' /%3E%3Cpath d='M979.456 729.6c-13.824-4.608-329.216-101.888-319.488-111.104 46.592-55.808 78.848-185.344 78.848-185.344v-27.648h-185.344V335.872h226.816v-41.472h-226.816V192.512H460.8v97.28H257.024v41.472H460.8v69.632H298.496v27.648h333.824c0 13.824-23.04 106.496-46.08 148.48-4.608-9.216-153.088-60.416-236.544-65.024-88.064 4.608-157.696 32.256-189.952 97.28-46.592 120.32 27.648 241.152 194.56 241.152 27.648 0 162.304-13.824 264.192-153.088 27.648 13.824 185.344 92.672 282.624 143.872-92.672 111.104-231.936 180.736-389.12 180.736-280.576 1.024-508.928-226.304-509.44-506.88v-3.072C1.024 231.424 227.84 3.072 508.928 2.56h3.072c280.576-1.024 508.928 226.304 509.44 506.88v3.072c4.608 82.944-13.824 152.576-41.984 217.088z' fill='${(isStr
? colors
: colors[1]) ||
"rgb(91,139,212)"}' /%3E%3C/svg%3E${quot})`, width: `${svgSize}px`, height: `${svgSize}px`,
...customStyle
}}
className={classnames(icon, customClassName)}
/>
)} */}
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<PropsType> = ({
name,
size = 36,
color,
customStyle = {},
customClassName = ""
}) => {
const [colors, setColors] = useState<PropsType['color']>()
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 <view> } 来渲染但是测试发现在ios下会有问题报错 Maximum call stack啥的。下面这个写法没问题
return (
<Block>
{/* icon-colorCard 本地svg */ }
{/* { name === 'icon-colorCard' && (<View style={{backgroundImage: `url(${quot}data:image/svg+xml, %3Csvg xmlns='http://www.w3.org/2000/svg' width='${svgSize}px' height='${svgSize}px' viewBox='0 0 72 72'><defs><linearGradient id='a' x1='56.049%' x2='45.965%' y1='85.384%' y2='36.243%'><stop offset='0%' stop-color='${(isStr ? colors : colors?.[0]) || '%233667EF'}' stop-opacity='.572'/><stop offset='100%' stop-color='${(isStr ? colors : colors?.[1]) || '%233591FD'}' stop-opacity='.551'/></linearGradient><linearGradient id='b' x1='100%' x2='16.645%' y1='85.384%' y2='36.243%'><stop offset='0%' stop-color='${(isStr ? colors : colors?.[2]) || '%233667EF'}' stop-opacity='.572'/><stop offset='100%' stop-color='${(isStr ? colors : colors?.[3]) || '%233591FD'}' stop-opacity='.551'/></linearGradient><linearGradient id='c' x1='18.906%' x2='80.404%' y1='44.444%' y2='55.556%'><stop offset='0%' stop-color='${(isStr ? colors : colors?.[4]) || '%233591FD'}'/><stop offset='100%' stop-color='${(isStr ? colors : colors?.[5]) || '%233667EF'}'/></linearGradient></defs><g fill='none' fill-rule='nonzero'><path fill='url(%23a)' d='M24.75 11.25A2.25 2.25 0 0 1 27 13.5v47.25A2.25 2.25 0 0 1 24.75 63h-13.5A2.25 2.25 0 0 1 9 60.75V13.5a2.25 2.25 0 0 1 2.25-2.25h13.5ZM18 50.625a3.375 3.375 0 1 0 0 6.75 3.375 3.375 0 0 0 0-6.75Z'/><path fill='url(%23b)' d='m45.593 16.216 9.546 9.546a2.25 2.25 0 0 1 0 3.182l-31.82 31.82a2.25 2.25 0 0 1-3.182 0L16.273 56.9a3.375 3.375 0 1 0-1.174-1.174l-4.508-4.508a2.25 2.25 0 0 1 0-3.182l31.82-31.82a2.25 2.25 0 0 1 3.182 0Z'/><path fill='url(%23c)' d='M60.75 45A2.25 2.25 0 0 1 63 47.25v13.5A2.25 2.25 0 0 1 60.75 63h-49.5A2.25 2.25 0 0 1 9 60.75v-13.5A2.25 2.25 0 0 1 11.25 45h49.5ZM18 50.625a3.375 3.375 0 1 0 0 6.75 3.375 3.375 0 0 0 0-6.75Z' opacity='.95'/></g></svg%3E${quot})`, width: `${svgSize}px`, height: `${svgSize}px`, ...customStyle}} className={classnames("icon", customClassName)} />) } */}
{/* icon-alipay */}
{/* {name === "icon-alipay" && (
<View
style={{
backgroundImage: `url(${quot}data:image/svg+xml, %3Csvg viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg' width='${svgSize}px' height='${svgSize}px'%3E%3Cpath d='M192 692.736c0-69.632 51.2-106.496 88.064-111.104 111.104-18.432 264.192 74.24 264.192 74.24-69.632 88.064-166.912 134.144-241.152 134.144-65.024-4.608-111.104-41.472-111.104-97.28z' fill='${(isStr
? colors
: colors[0]) ||
"rgb(91,139,212)"}' /%3E%3Cpath d='M979.456 729.6c-13.824-4.608-329.216-101.888-319.488-111.104 46.592-55.808 78.848-185.344 78.848-185.344v-27.648h-185.344V335.872h226.816v-41.472h-226.816V192.512H460.8v97.28H257.024v41.472H460.8v69.632H298.496v27.648h333.824c0 13.824-23.04 106.496-46.08 148.48-4.608-9.216-153.088-60.416-236.544-65.024-88.064 4.608-157.696 32.256-189.952 97.28-46.592 120.32 27.648 241.152 194.56 241.152 27.648 0 162.304-13.824 264.192-153.088 27.648 13.824 185.344 92.672 282.624 143.872-92.672 111.104-231.936 180.736-389.12 180.736-280.576 1.024-508.928-226.304-509.44-506.88v-3.072C1.024 231.424 227.84 3.072 508.928 2.56h3.072c280.576-1.024 508.928 226.304 509.44 506.88v3.072c4.608 82.944-13.824 152.576-41.984 217.088z' fill='${(isStr
? colors
: colors[1]) ||
"rgb(91,139,212)"}' /%3E%3C/svg%3E${quot})`, width: `${svgSize}px`, height: `${svgSize}px`,
...customStyle
}}
className={classnames(icon, customClassName)}
/>
)} */}
{/* icon-lijitixian */}
{ name === 'icon-lijitixian' && (<View style={{backgroundImage: `url(${quot}data:image/svg+xml, %3Csvg viewBox='0 0 1396 1024' xmlns='http://www.w3.org/2000/svg' width='${svgSize}px' height='${svgSize}px'%3E%3Cpath d='M1349.818182 512v418.909091a46.405818 46.405818 0 0 1-46.545455 46.545454H93.090909a46.405818 46.405818 0 0 1-46.545454-46.545454v-418.909091h1303.272727z m-866.350546 139.636364H152.669091l-77.544727 232.727272H405.876364l77.544727-232.727272zM1303.272727 46.545455a46.405818 46.405818 0 0 1 46.545455 46.545454v186.181818H46.545455V93.090909a46.405818 46.405818 0 0 1 46.545454-46.545454z' fill='${(isStr ? colors : colors?.[0]) || 'rgb(255,255,255)'}'/%3E%3C/svg%3E${quot})`, width: `${svgSize}px`, height: `${svgSize}px`, ...customStyle}} className={classnames("icon", customClassName)} />) }
@ -312,9 +312,9 @@ const IconFont:FC<PropsType> = ({
{/* icon-daifukuan2 */}
{ name === 'icon-daifukuan2' && (<View style={{backgroundImage: `url(${quot}data:image/svg+xml, %3Csvg viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg' width='${svgSize}px' height='${svgSize}px'%3E%3Cpath d='M848 368a32 32 0 0 1 32 32v448a32 32 0 0 1-32 32H176a32 32 0 0 1-32-32V400a32 32 0 0 1 32-32h672zM512 688H240a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h272a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16z' fill='${(isStr ? colors : colors?.[0]) || 'rgb(64,123,244)'}'/%3E%3Cpath d='M765.664 216.496L812 368H160.688l565.024-172.736a32 32 0 0 1 39.952 21.248z' fill='${(isStr ? colors : colors?.[1]) || 'rgb(142,186,251)'}'/%3E%3C/svg%3E${quot})`, width: `${svgSize}px`, height: `${svgSize}px`, ...customStyle}} className={classnames("icon", customClassName)} />) }
</Block>
)
}
export default IconFont
</Block>
)
}
export default IconFont

View File

@ -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;
}
}

View File

@ -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<InviteCodePopupRef>) => {
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<Taro.Image>((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 <Dialog show={showPopup} onChange={handleChange}>
<View className={styles.codePreview}>
<View className={styles.imageContainer}>
{/* showMenuByLongpress 属性只对 小程序有效 */}
<Image className={styles.image} src={targetImageUrl} mode="widthFix" id="originImage" showMenuByLongpress />
</View>
<Text className={styles.previewTips}></Text>
</View>
</Dialog>
}
export default forwardRef(InviteCodePopup)

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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<any>({})
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)}
/>
</View>
<NormalButton type="primary" onClick={() => onSelectDate?.(time)} size="normal" round customClassName={styles['sure-box']}></NormalButton>
<View className={styles['sure-box']}>
{
leftSlot && <View className={styles.left_slot}>{leftSlot}</View>
}
<NormalButton type="primary" customClassName={styles.confirm_button} onClick={() => onSelectDate?.(time)} size="normal" round></NormalButton>
</View>
</>
)
}

View File

@ -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 (
<Popup title="选择时间" show={showTime} onClose={() => closePopup?.()}>
<TimePicker start={start} end={end} onSelectDate={onSelectDate}></TimePicker>
<TimePicker start={start} end={end} onSelectDate={onSelectDate} leftSlot={leftSlot}></TimePicker>
</Popup>
)
}

View File

@ -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;

View File

@ -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<any>({})
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) => {
<View className={styles.des}>{productInfo.describe}</View>
</View>
<View className={styles.collect} onClick={openCollection}>
<View
className={classnames(
`iconfont ${collectStatus ? 'icon-shoucangchenggong' : 'icon-dianjishoucang'}`,
styles.miconfont,
collectStatus && styles.collected,
)}
></View>
{
collectStatus ? <IconFont name="icon-shoucangchenggong" size={45} color="#ffc300"></IconFont> : <IconFont name="icon-dianjishoucang" size={45}></IconFont>
}
<View className={styles.text}></View>
</View>
<View className={styles.share}>
<View className={classnames('iconfont icon-fenxiangshangpin', styles.miconfont)}></View>
<IconFont name="icon-fenxiangshangpin" size={45}></IconFont>
<View className={styles.text}></View>
<Button open-type="share" className={styles.shareBtn}></Button>
</View>

View File

@ -1,7 +1,8 @@
export default {
navigationBarTitleText: '邀请',
navigationBarTitleText: '邀请朋友',
navigationBarTextStyle: 'black',
navigationBarBackgroundColor: '#E4EEFD',
backgroundColor: '#E4EEFD',
enableShareAppMessage: true,
backgroundColorTop: '#E4EEFD',
}

View File

@ -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 {

View File

@ -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<any>({})
const [salesMan, setSalesMan] = useState<Param | null>(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<number>(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<Taro.Canvas | null>(null)
const ctx = useRef<Taro.RenderingContext | null>(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<any>(null)
const getImageObject = (canvas: Taro.Canvas, src: string) => {
return new Promise<Taro.Image>((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<React.CSSProperties>({})
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<InviteCodePopupRef>(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<TablePropsType>({
columns: inviteColumns,
dataSource: { list: [], total: 0 },
})
const handleLoadMore = () => {
size.current += defaultSize
}
return (
<View className={styles.main}>
<View className={styles.content}>
<View className={styles.background}>
<View className={styles.left}>
<View className={styles.title}></View>
<View className={styles.description}></View>
<View className={styles.title}>
<Text className={styles.title_text}>A</Text>
<Text className={styles.title_plus}>+</Text>
</View>
<View className={styles.description}></View>
</View>
<View className={styles.right}>
<View className={styles.iconContainer}>
@ -270,65 +100,24 @@ const BindSalesman = () => {
</View>
</View>
<View className={styles.inviteCodeContent}>
<LayoutBlock circle>
<View className={styles.codeBar}>
<View className={styles.inviteCodeBar}>
<View className={styles.invite}>{inviteInfo.invitation_code ? inviteInfo.invitation_code : '暂无邀请码'}</View>
</View>
<View className={styles.codeTitle}></View>
<LayoutBlock circle customStyle={{ paddingTop: '10px', paddingBottom: '10px' }}>
<View className={styles.inviteListTitle}>
<View className={styles.titleIconLeft}></View>
<Text className={styles.listTitle}></Text>
<View className={styles.titleIconRight}></View>
</View>
<View className={styles.inviteList}>
<Table columns={currentTable.columns} emptyText="暂无邀请信息" safeAreaInsetBottom={false} dataSource={currentTable.dataSource} onLoadMore={handleLoadMore}></Table>
</View>
</LayoutBlock>
<LayoutBlock circle>
<View className={styles.inviteCord}>
<View className={styles.inviteCordTitle}>
<Text className={styles.titleText}></Text>
{invite === 0 && <Text className={styles.noop}></Text>}
</View>
{invite !== 0 && <Text>{invite}</Text>}
<View className={styles.inviteCordMore} onClick={handleInviteCord}>
<IconText svg text="查看" direction="right" iconName="icon-rukou" textCustomStyle={{ color: '#337FFF' }} color="#337FFF"></IconText>
</View>
</View>
</LayoutBlock>
<LayoutBlock circle>
<View className={styles.inviteCord}>
<View className={styles.inviteCordTitle}>
<Text className={styles.titleText}></Text>
{!salesMan?.inviter_id && <Text className={styles.noop}></Text>}
</View>
{!!salesMan?.inviter_id && <View className={styles.inviteCordMore}>
{salesMan.inviter_name}{salesMan.phone}
</View>}
{!salesMan?.inviter_id
&& <View onClick={handleBindSalesMan}>
<IconText svg text="立即绑定" direction="right" iconName="icon-rukou" textCustomStyle={{ color: '#337FFF' }} color="#337FFF"></IconText>
</View>
}
</View>
</LayoutBlock>
<View className={styles.tips} style={{ justifyContent: 'flex-start' }}></View>
</View>
</View>
{/* 已踩坑这里必须设置canvas的style的width和height单单只设置canvas实例的width和height是不行的。会模糊 */}
<Canvas style={{ position: 'absolute', left: '-9999rpx', ...canvasStyle }} id="canvas" type="2d" />
<View className={styles.bottomBar}>
<NormalButton loading={loading} plain type="primary" customTextClassName={styles.bottomBar__text} customStyles={{ width: '45%' }} round onClick={handleQRcodeShare}>
</NormalButton>
<NormalButton type="primary" round customTextClassName={styles.bottomBar__text} customStyles={{ width: '45%' }} onClick={handleCopyInviteCode}>
<NormalButton type="primary" round customTextClassName={styles.bottomBar__text} customStyles={{ width: '100%' }} onClick={handleQRcodeShare}>
</NormalButton>
</View>
<Dialog show={showPopup} onChange={handleChange}>
<View className={styles.codePreview}>
<View className={styles.imageContainer}>
{/* showMenuByLongpress 属性只对 小程序有效 */}
<Image className={styles.image} src={targetImageUrl} mode="widthFix" id="originImage" showMenuByLongpress />
</View>
<Text className={styles.previewTips}></Text>
</View>
</Dialog>
<BindSalesManDialog ref={inviteDialog} onSuccess={handleBindSuccess} />
<InviteCodePopup ref={inviteCodePopupRef}></InviteCodePopup>
</View>
)
}

View File

@ -1,7 +0,0 @@
export default {
navigationBarTitleText: '邀请记录',
navigationBarTextStyle: 'black',
navigationBarBackgroundColor: '#E4EEFD',
backgroundColor: '#E4EEFD',
backgroundColorTop: '#E4EEFD',
}

View File

@ -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;
}
}

View File

@ -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<TablePropsType>({
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 (
<View className={styles.main}>
<View className={styles.content}>
<View className={styles.background}>
<View className={styles.left}>
<View className={styles.title}></View>
<View className={styles.description}></View>
</View>
<View className={styles.right}>
<View className={styles.iconContainer}>
<Image className={styles.icon} src={getCDNSource('/user/inviteCode.png')} mode="widthFix" />
</View>
</View>
</View>
<View className={styles.inviteCodeContent}>
<LayoutBlock circle customStyle={{ paddingTop: '10px', paddingBottom: '10px' }}>
<View className={styles.inviteListTitle}>
<View className={styles.titleIconLeft}></View>
<Text className={styles.listTitle}></Text>
<View className={styles.titleIconRight}></View>
</View>
<View className={styles.inviteList}>
<Table columns={currentTable.columns} emptyText="暂无邀请信息" safeAreaInsetBottom={false} dataSource={currentTable.dataSource} onLoadMore={handleLoadMore}></Table>
</View>
</LayoutBlock>
</View>
</View>
</View>
)
}
export default InviteCord

View File

@ -0,0 +1,5 @@
export default {
navigationBarTitleText: '邀请好友',
enableShareAppMessage: true,
navigationBarTextStyle: 'black',
}

View File

@ -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;
}
}

View File

@ -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<InviteCodePopupRef>(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 (
<View className={styles.main}>
<View className={styles.search}>
<Search showBtn={false} changeOnSearch={onSearch} placeholder="请输入邀请人" >
<View className={styles.flexBox} onClick={handleClickTimePicker}>
<IconText svg iconName="icon-xuanzhongshijian" text="日期" color={isFilter ? '#4581ff' : '#3a3a3a'} textCustomStyle={{ color: isFilter ? '#4581ff' : '#3a3a3a' }} customClass={styles['icon--manage--cancel']} />
</View>
</Search>
</View>
<View className={styles.content}>
<View className={styles.total}> {total} </View>
<InfiniteScroll safeAreaInsetBottom={false} selfonScrollToLower={getScrollToLower} statusMore={statusMore}>
<View className={styles.scroll_list}>
{recordList.list.map((item, index) => {
return (
<LayoutBlock key={index} circle customClassName={styles.layout}>
<View className={styles.title}>
<View>{item?.passive_invited_user_name}</View>
<View>{item?.invitation_way_name}</View>
</View>
<Divider direction="horizontal" customStyles={{ margin: '12px 0', marginBottom: '6px' }}></Divider>
<View className={styles.block_content}>
<Cell customDescClassName={styles.cell_desc} title="邀请人:" desc={item?.invite_user_name || '暂无信息'}></Cell>
<Cell customDescClassName={styles.cell_desc} title="邀请时间:" desc={formatDateTime(item?.invitation_time, 'YYYY-MM-DD') || '暂无信息'}></Cell>
</View>
</LayoutBlock>
)
})}
</View>
</InfiniteScroll>
</View>
<View className={styles.bottomBar}>
<NormalButton type="primary" round customTextClassName={styles.bottomBar__text} customClassName={styles.bottomBar__button} customStyles={{ width: '100%' }} onClick={handleConfirm}></NormalButton>
</View>
<TimePickerPopup showTime={showTime} end={formData?.end_time} start={formData?.start_time} closePopup={handClose}
leftSlot={
<NormalButton type="primary" plain round onClick={handleReset}></NormalButton>
}
onSelectDate={onSelectDate}
/>
<InviteCodePopup ref={inviteCodePopupRef}></InviteCodePopup>
</View>
)
}
export default InviteCord

View File

@ -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'

View File

@ -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);

View File

@ -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 (
<View className={styles.user_main}>
@ -101,7 +109,14 @@ export default () => {
<Image className={styles.header_img_src} mode="aspectFill" src={`${userInfo?.adminUserInfo?.avatar_url}`} />
</View>
<View className={styles.header_name}>
<Text>{userInfo?.adminUserInfo?.phone ? userInfo?.adminUserInfo?.user_name : '点击登录'}</Text>
<View className={styles.header_user}>
<Text className={styles.header_user_name}>{userInfo?.adminUserInfo?.phone ? userInfo?.adminUserInfo?.user_name : '点击登录'}</Text>
{
userInfo?.adminUserInfo?.is_bd && <View className={styles.header_user_label}>
<Image className={styles.BD_label} mode="aspectFill" src={getCDNSource('/mall/BD_label.png')}></Image>
</View>
}
</View>
<View className={styles['arcd-info-left-phone']}>
<View className={styles.header_title}> {userInfo?.adminUserInfo?.phone || 'Hi,欢迎来到商城'}</View>
</View>
@ -122,9 +137,9 @@ export default () => {
<Text>0</Text>
<Text></Text>
</View>
<View className={styles.header_count__item} onClick={() => goLink('/pages/inviteCode/index')}>
<View className={styles.header_count__item} onClick={handleClickInviteFriends}>
<IconFont name="icon-erweima" size={50} />
<Text></Text>
<Text></Text>
</View>
</View>
<Image className={styles.header_bg} src={formatImgUrl('/mall/user_header_bg.png', '')} mode="aspectFit"></Image>

View File

@ -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<typeof adminUserInfo>(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) {

View File

@ -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) {

View File

@ -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 } // 右上角分享页面短码
}

View File

@ -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 */

View File

@ -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,

View File

@ -20,17 +20,23 @@ export default () => {
const loginData = useRef<Param>(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)