✨ feat(ID1000916): 新增分享记录和来源统计
【新增分享记录和来源统计】 https://www.tapd.cn/53459131/prong/stories/view/1153459131001000916
This commit is contained in:
parent
5a1d6dddb0
commit
4fcd4de555
@ -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',
|
||||
})
|
||||
}
|
||||
|
||||
@ -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',
|
||||
})
|
||||
}
|
||||
|
||||
@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
@ -196,7 +196,7 @@ export default {
|
||||
root: 'pages/inviteCode',
|
||||
pages: [
|
||||
'index',
|
||||
'inviteCord/index',
|
||||
'inviteFriends/index',
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@ -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(() => {
|
||||
|
||||
@ -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
|
||||
// 生成密钥
|
||||
|
||||
@ -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,
|
||||
]
|
||||
|
||||
@ -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) {
|
||||
// 绑定上下级
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
88
src/common/shortCode/index.ts
Normal file
88
src/common/shortCode/index.ts
Normal 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 }))
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
@ -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() {
|
||||
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
export const DAY_LIST = ['日', '一', '二', '三', '四', '五', '六']
|
||||
|
||||
export const TYPE_PRE_MONTH = -1
|
||||
|
||||
export const TYPE_NOW_MONTH = 0
|
||||
|
||||
@ -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'
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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'
|
||||
|
||||
4
src/components/calendar/types/calendar.d.ts
vendored
4
src/components/calendar/types/calendar.d.ts
vendored
@ -33,9 +33,9 @@ declare namespace Calendar {
|
||||
|
||||
isToday?: boolean
|
||||
|
||||
isBeforeMin?: boolean
|
||||
isRowEnd?: boolean
|
||||
|
||||
isAfterMax?: boolean
|
||||
isRowStart?: boolean
|
||||
|
||||
isDisabled?: boolean
|
||||
|
||||
|
||||
@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@ -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>
|
||||
)
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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
|
||||
|
||||
27
src/components/inviteCodePopup/index.module.scss
Normal file
27
src/components/inviteCodePopup/index.module.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
168
src/components/inviteCodePopup/index.tsx
Normal file
168
src/components/inviteCodePopup/index.tsx
Normal 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)
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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>
|
||||
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
export default {
|
||||
navigationBarTitleText: '邀请码',
|
||||
navigationBarTitleText: '邀请朋友',
|
||||
navigationBarTextStyle: 'black',
|
||||
navigationBarBackgroundColor: '#E4EEFD',
|
||||
backgroundColor: '#E4EEFD',
|
||||
enableShareAppMessage: true,
|
||||
backgroundColorTop: '#E4EEFD',
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,7 +0,0 @@
|
||||
export default {
|
||||
navigationBarTitleText: '邀请记录',
|
||||
navigationBarTextStyle: 'black',
|
||||
navigationBarBackgroundColor: '#E4EEFD',
|
||||
backgroundColor: '#E4EEFD',
|
||||
backgroundColorTop: '#E4EEFD',
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
5
src/pages/inviteCode/inviteFriends/index.config.ts
Normal file
5
src/pages/inviteCode/inviteFriends/index.config.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export default {
|
||||
navigationBarTitleText: '邀请好友',
|
||||
enableShareAppMessage: true,
|
||||
navigationBarTextStyle: 'black',
|
||||
}
|
||||
78
src/pages/inviteCode/inviteFriends/index.module.scss
Normal file
78
src/pages/inviteCode/inviteFriends/index.module.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
170
src/pages/inviteCode/inviteFriends/index.tsx
Normal file
170
src/pages/inviteCode/inviteFriends/index.tsx
Normal 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
|
||||
@ -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'
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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 } // 右上角分享页面短码
|
||||
}
|
||||
|
||||
|
||||
@ -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 */
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user