✨ 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 = () => {
|
export const LoginApi = () => {
|
||||||
return useRequest({
|
return useRequest({
|
||||||
url: '/v1/mall/login',
|
// url: '/v1/mall/login',
|
||||||
|
url: '/v3/mallCherry/login',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,4 +28,44 @@ export const BindShortCodeApi = () => {
|
|||||||
url: '/v1/mall/shortCode/bind',
|
url: '/v1/mall/shortCode/bind',
|
||||||
method: 'post',
|
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({
|
return useRequest({
|
||||||
url: '/v1/mall/user',
|
url: '/v1/mall/user',
|
||||||
method: 'put',
|
method: 'put',
|
||||||
@ -43,7 +43,7 @@ export const realNameUpdateApi = () => {
|
|||||||
/**
|
/**
|
||||||
* 公司类型|企业类型 下拉列表
|
* 公司类型|企业类型 下拉列表
|
||||||
*/
|
*/
|
||||||
export const companyTypeApi = () => {
|
export const CompanyTypeApi = () => {
|
||||||
return useRequest({
|
return useRequest({
|
||||||
url: '/v1/mall/enum/purchaserType',
|
url: '/v1/mall/enum/purchaserType',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@ -53,7 +53,7 @@ export const companyTypeApi = () => {
|
|||||||
/**
|
/**
|
||||||
* 头像更改
|
* 头像更改
|
||||||
*/
|
*/
|
||||||
export const portraitUpdateApi = () => {
|
export const PortraitUpdateApi = () => {
|
||||||
return useRequest({
|
return useRequest({
|
||||||
url: '/v1/mall/user/avatar',
|
url: '/v1/mall/user/avatar',
|
||||||
method: 'put',
|
method: 'put',
|
||||||
@ -134,10 +134,11 @@ export const GetInvitationInfo = () => {
|
|||||||
method: 'get',
|
method: 'get',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// 邀请码
|
// 邀请记录
|
||||||
export const GetInviteeRecord = () => {
|
export const GetInviteeRecord = () => {
|
||||||
return useRequest({
|
return useRequest({
|
||||||
url: '/v2/mall/user/invitee_record',
|
url: '/v2/mall/user/invitee_record',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
|
pagination: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -196,7 +196,7 @@ export default {
|
|||||||
root: 'pages/inviteCode',
|
root: 'pages/inviteCode',
|
||||||
pages: [
|
pages: [
|
||||||
'index',
|
'index',
|
||||||
'inviteCord/index',
|
'inviteFriends/index',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -2,10 +2,9 @@ import Taro, { onAppShow, useDidShow } from '@tarojs/taro'
|
|||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import { Provider } from 'react-redux'
|
import { Provider } from 'react-redux'
|
||||||
import configStore from './store'
|
import configStore from './store'
|
||||||
import { shareShop } from './common/util'
|
import { useShareShop } from './common/util'
|
||||||
import ContextBlueTooth from '@/use/contextBlueTooth'
|
import ContextBlueTooth from '@/use/contextBlueTooth'
|
||||||
import './app.scss'
|
import './app.scss'
|
||||||
import { BANk_WX_APPID } from './common/constant'
|
|
||||||
|
|
||||||
const store = configStore()
|
const store = configStore()
|
||||||
const App: FC = (params: { children?: React.ReactNode }) => {
|
const App: FC = (params: { children?: React.ReactNode }) => {
|
||||||
@ -14,7 +13,7 @@ const App: FC = (params: { children?: React.ReactNode }) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// 分享
|
// 分享
|
||||||
shareShop()
|
useShareShop()
|
||||||
|
|
||||||
// 检查版本更新
|
// 检查版本更新
|
||||||
onAppShow(() => {
|
onAppShow(() => {
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
// export const BASE_URL = `http://192.168.0.89:40001/lymarket`
|
// 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 = `http://192.168.1.165:40001/lymarket` // 王霞
|
||||||
// export const BASE_URL = 'https://test.zzfzyc.com/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:40001/lymarket` // 发
|
||||||
// export const BASE_URL = `http://192.168.1.9:50005/lymarket` // 发
|
// export const BASE_URL = `http://192.168.1.9:50005/lymarket` // 发
|
||||||
// export const BASE_URL = `http://192.168.1.30:50001/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 = 'https://www.zzfzyc.com/lymarket' // 正式环境
|
||||||
// export const BASE_URL = `http://192.168.1.5:40001/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.7:50002/lymarket' // 添
|
||||||
// export const BASE_URL = 'http://192.168.1.28:50002/lymarket' // 婷
|
// export const BASE_URL = 'http://192.168.1.28:50001/lymarket' // 婷
|
||||||
// export const BASE_URL = 'http://192.168.1.42:50002/lymarket' // 杰
|
export const BASE_URL = 'http://192.168.1.42:50002/lymarket' // 杰
|
||||||
|
|
||||||
// CDN
|
// 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: 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' },
|
{ 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 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 { 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
|
const page = Taro.getCurrentInstance().page
|
||||||
// 当有分享参数时,绑定上下级
|
const adminUserInfo = JSON.parse(Taro.getStorageSync('adminUserInfo') || '{}')
|
||||||
if (page && page.options?.share) {
|
|
||||||
analysisShortCodeApi(page.options.share)
|
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) {
|
if (page && page.onShareAppMessage) {
|
||||||
page.onShareAppMessage = (res) => {
|
console.log('adminUserInfo 真假', adminUserInfo)
|
||||||
let path = ''
|
page.onShareAppMessage = () => {
|
||||||
let title = ''
|
|
||||||
let imageUrl = ''
|
|
||||||
const sortCode = Taro.getStorageSync('sort_code') ? JSON.parse(Taro.getStorageSync('sort_code')) : ''
|
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
|
const pageInfo: any = page
|
||||||
// 商品详情分享
|
const promise = new Promise((resolve, reject) => {
|
||||||
if (pageInfo.route === 'pages/details/index') {
|
getUserInfo().then((newUserInfo) => {
|
||||||
path = `/pages/details/index?share=${sortCode.shareShortDetail.code}`
|
// 商品详情分享
|
||||||
title = sortCode.shareShortDetail.title
|
if (pageInfo.route === 'pages/details/index') {
|
||||||
imageUrl = sortCode.shareShortDetail.img
|
// 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}`
|
||||||
else {
|
title = sortCode.shareShortDetail.title
|
||||||
path
|
imageUrl = sortCode.shareShortDetail.img
|
||||||
= pageInfo.route === 'pages/user/index'
|
}
|
||||||
? `/pages/user/index?share=${sortCode.shareShortPage.code}`
|
else {
|
||||||
: `/pages/index/index?share=${sortCode.shareShortPage.code}`
|
// 分享链接方式 绑定邀请人
|
||||||
title = sortCode.shareShortPage.title
|
path = `/pages/index/index?user_id=${newUserInfo?.user_id}&invitation_way=${InvitationWay.INVITE_LINK}`
|
||||||
imageUrl = pageInfo.route === 'pages/user/index' ? sortCode.shareShortPage.img : formatImgUrl('/mall/share_img_01.png', '!w400')
|
title = '打造面料爆品 专注客户服务'
|
||||||
}
|
imageUrl = getCDNSource('/mall/share_img_01.png')
|
||||||
console.log('imageUrl:::', imageUrl)
|
}
|
||||||
|
console.log('inside promise imageUrl:::', path)
|
||||||
|
resolve({
|
||||||
|
path,
|
||||||
|
title,
|
||||||
|
imageUrl,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
console.log('outside promise imageUrl:::', path)
|
||||||
return {
|
return {
|
||||||
title,
|
title,
|
||||||
path,
|
path,
|
||||||
imageUrl,
|
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,
|
ITouch,
|
||||||
ITouchEvent,
|
ITouchEvent,
|
||||||
} from '@tarojs/components/types/common'
|
} 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 {
|
import type {
|
||||||
AtCalendarBodyListGroup,
|
AtCalendarBodyListGroup,
|
||||||
AtCalendarBodyProps,
|
AtCalendarBodyProps,
|
||||||
AtCalendarBodyState,
|
AtCalendarBodyState,
|
||||||
Calendar,
|
Calendar,
|
||||||
} from '../../../types/calendar'
|
} from '../types/calendar'
|
||||||
import generateCalendarGroup from '../common/helper'
|
|
||||||
import AtCalendarDateList from '../ui/date-list/index'
|
|
||||||
import AtCalendarDayList from '../ui/day-list/index'
|
|
||||||
import { delayQuerySelector } from '@/common/util'
|
import { delayQuerySelector } from '@/common/util'
|
||||||
|
|
||||||
const ANIMTE_DURATION = 300
|
const ANIMTE_DURATION = 300
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
export const DAY_LIST = ['日', '一', '二', '三', '四', '五', '六']
|
||||||
|
|
||||||
export const TYPE_PRE_MONTH = -1
|
export const TYPE_PRE_MONTH = -1
|
||||||
|
|
||||||
export const TYPE_NOW_MONTH = 0
|
export const TYPE_NOW_MONTH = 0
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import type { Dayjs } from 'dayjs'
|
import type { Dayjs } from 'dayjs'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import _flow from 'lodash/flow'
|
import _flow from 'lodash/flow'
|
||||||
import type { Calendar } from '../../../types/calendar'
|
import type { Calendar } from '../types/calendar'
|
||||||
import * as constant from './constant'
|
import * as constant from './constant'
|
||||||
import plugins from './plugins'
|
import plugins from './plugins'
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import _isEmpty from 'lodash/isEmpty'
|
import _isEmpty from 'lodash/isEmpty'
|
||||||
import type { Calendar } from '../../../types/calendar'
|
import type { Calendar } from '../types/calendar'
|
||||||
|
|
||||||
interface PluginArg {
|
interface PluginArg {
|
||||||
options: Calendar.GroupOptions
|
options: Calendar.GroupOptions
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import React from 'react'
|
|||||||
import type {
|
import type {
|
||||||
AtCalendarControllerProps,
|
AtCalendarControllerProps,
|
||||||
AtCalendarControllerState,
|
AtCalendarControllerState,
|
||||||
} from '../../../types/calendar'
|
} from '../types/calendar'
|
||||||
|
|
||||||
export default class AtCalendarController extends React.Component<
|
export default class AtCalendarController extends React.Component<
|
||||||
AtCalendarControllerProps,
|
AtCalendarControllerProps,
|
||||||
|
|||||||
@ -1,180 +1,207 @@
|
|||||||
|
|
||||||
@import '../../styles/variables/default.scss';
|
@import '../../styles/variables/default.scss';
|
||||||
@import '../../styles/mixins/index.scss';
|
@import '../../styles/mixins/index.scss';
|
||||||
.at-calendar {
|
.at-calendar {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
/* elements */
|
/* elements */
|
||||||
&__header {
|
&__header {
|
||||||
.header__flex {
|
.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 {
|
|
||||||
@include display-flex;
|
@include display-flex;
|
||||||
@include align-items(center);
|
@include align-items(center);
|
||||||
@include justify-content(center);
|
|
||||||
|
height: 72px;
|
||||||
margin-bottom: 20px;
|
color: $at-calendar-header-color;
|
||||||
|
text-align: center;
|
||||||
.controller__arrow {
|
|
||||||
@include flex(0 0 40px);
|
&-item {
|
||||||
|
@include flex(0 0 calc(100% / 7));
|
||||||
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;
|
font-size: 30px;
|
||||||
margin-left: 40px;
|
|
||||||
margin-right: 40px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.at-calendar-slider__main {
|
&__list {
|
||||||
.main__body {
|
&.flex {
|
||||||
@include display-flex;
|
@include display-flex;
|
||||||
|
@include align-items();
|
||||||
width: 100%;
|
@include flex-wrap(wrap);
|
||||||
|
|
||||||
&--animate {
|
color: $at-calendar-day-color;
|
||||||
transition: transform 300ms cubic-bezier(0.36, 0.66, 0.04, 1);
|
|
||||||
}
|
.flex__item {
|
||||||
|
@include flex(0 0 calc(100% / 7));
|
||||||
.body__slider {
|
|
||||||
@include flex(0 0 100%);
|
font-size: 30px;
|
||||||
}
|
text-align: center;
|
||||||
}
|
position: relative;
|
||||||
|
margin: 5px 0;
|
||||||
&--weapp,
|
|
||||||
&--swan {
|
&-container {
|
||||||
.main__body {
|
@include align-items(center);
|
||||||
height: 480px;
|
@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,
|
AtCalendarPropsWithDefaults,
|
||||||
AtCalendarState,
|
AtCalendarState,
|
||||||
Calendar,
|
Calendar,
|
||||||
} from '../../types/calendar'
|
} from './types/calendar'
|
||||||
import AtCalendarBody from './body/index'
|
import AtCalendarBody from './body/index'
|
||||||
import AtCalendarController from './controller/index'
|
import AtCalendarController from './controller/index'
|
||||||
import './index.scss'
|
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
|
isToday?: boolean
|
||||||
|
|
||||||
isBeforeMin?: boolean
|
isRowEnd?: boolean
|
||||||
|
|
||||||
isAfterMax?: boolean
|
isRowStart?: boolean
|
||||||
|
|
||||||
isDisabled?: boolean
|
isDisabled?: boolean
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import classnames from 'classnames'
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import type { Calendar } from '../../types/calendar'
|
import type { Calendar } from '../../types/calendar'
|
||||||
import * as constant from '../../common/constant'
|
import * as constant from '../../common/constant'
|
||||||
|
import { DAY_LIST } from '../../common/constant'
|
||||||
|
|
||||||
const MAP: Record<number, string> = {
|
const MAP: Record<number, string> = {
|
||||||
[constant.TYPE_PRE_MONTH]: 'pre',
|
[constant.TYPE_PRE_MONTH]: 'pre',
|
||||||
@ -34,47 +35,69 @@ export default class AtCalendarList extends React.Component<Props> {
|
|||||||
public render(): JSX.Element | null {
|
public render(): JSX.Element | null {
|
||||||
const { list } = this.props
|
const { list } = this.props
|
||||||
if (!list || list.length === 0) { return null }
|
if (!list || list.length === 0) { return null }
|
||||||
|
let row = 0 // 当前行数 第 0 行就是第一行
|
||||||
return (
|
return (
|
||||||
<View className="at-calendar__list flex">
|
<View className="at-calendar__list flex">
|
||||||
{list.map((item: Calendar.Item) => (
|
{list.map((item: Calendar.Item, index) => {
|
||||||
<View
|
const isRowStart = DAY_LIST.length * row === index
|
||||||
key={`list-item-${item.value}`}
|
const isRowEnd = (index + 1) % DAY_LIST.length === 0
|
||||||
onClick={this.handleClick.bind(this, item)}
|
if (isRowStart) {
|
||||||
onLongPress={this.handleLongClick.bind(this, item)}
|
row++
|
||||||
className={classnames(
|
}
|
||||||
'flex__item',
|
return (
|
||||||
`flex__item--${MAP[item.type]}`,
|
<View
|
||||||
{
|
key={`list-item-${item.value}`}
|
||||||
'flex__item--today': item.isToday,
|
onClick={this.handleClick.bind(this, item)}
|
||||||
'flex__item--active': item.isActive,
|
onLongPress={this.handleLongClick.bind(this, item)}
|
||||||
'flex__item--selected': item.isSelected,
|
className={classnames(
|
||||||
'flex__item--selected-head': item.isSelectedHead,
|
'flex__item',
|
||||||
'flex__item--selected-tail': item.isSelectedTail,
|
`flex__item--${MAP[item.type]}`,
|
||||||
'flex__item--blur':
|
{
|
||||||
|
'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.isDisabled
|
||||||
|| item.type === constant.TYPE_PRE_MONTH
|
|| item.type === constant.TYPE_PRE_MONTH
|
||||||
|| item.type === constant.TYPE_NEXT_MONTH,
|
|| item.type === constant.TYPE_NEXT_MONTH,
|
||||||
},
|
},
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<View className="flex__item-container">
|
<View className={classnames(
|
||||||
<View className="container-text">{item.text}</View>
|
{
|
||||||
|
'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>
|
||||||
<View className="flex__item-extra extra">
|
|
||||||
{item.marks && item.marks.length > 0
|
)
|
||||||
? (
|
})}
|
||||||
<View className="extra-marks">
|
|
||||||
{item.marks.map((mark, key) => (
|
|
||||||
<Text key={key} className="mark">
|
|
||||||
{mark.value as React.ReactNode}
|
|
||||||
</Text>
|
|
||||||
))}
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
: null}
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
))}
|
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,18 +1,17 @@
|
|||||||
import { View } from '@tarojs/components'
|
import { View } from '@tarojs/components'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import { DAY_LIST } from '../../common/constant'
|
||||||
|
|
||||||
export default class AtCalendarHeader extends React.Component {
|
export default class AtCalendarHeader extends React.Component {
|
||||||
public render(): JSX.Element {
|
public render(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<View className="at-calendar__header header">
|
<View className="at-calendar__header header">
|
||||||
<View className="header__flex">
|
<View className="header__flex">
|
||||||
<View className="header__flex-item">日</View>
|
{
|
||||||
<View className="header__flex-item">一</View>
|
DAY_LIST.map((day, index) => (
|
||||||
<View className="header__flex-item">二</View>
|
<View key={index} className="header__flex-item">{day}</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>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -34,7 +34,7 @@ const Cell: FC<CellPropsType> = (props) => {
|
|||||||
<View>{title}</View>
|
<View>{title}</View>
|
||||||
</View>
|
</View>
|
||||||
<View className={styles.desc}>
|
<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>}
|
{isLink && <IconFont name="icon-rukou" size={46} color="inherit"></IconFont>}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@ -1,89 +1,89 @@
|
|||||||
import React, { useState, useEffect, FC } from "react";
|
import React, { useState, useEffect, FC } from "react";
|
||||||
import { Block, View } from "@tarojs/components";
|
import { Block, View } from "@tarojs/components";
|
||||||
import "./iconfont.scss";
|
import "./iconfont.scss";
|
||||||
import Taro from "@tarojs/taro";
|
import Taro from "@tarojs/taro";
|
||||||
import classnames from "classnames";
|
import classnames from "classnames";
|
||||||
|
|
||||||
const SystemWidth = Taro.getSystemInfoSync().windowWidth
|
const SystemWidth = Taro.getSystemInfoSync().windowWidth
|
||||||
const quot = '"'
|
const quot = '"'
|
||||||
|
|
||||||
function hex2rgb(hex) {
|
function hex2rgb(hex) {
|
||||||
const rgb: number[] = [];
|
const rgb: number[] = [];
|
||||||
|
|
||||||
hex = hex.substr(1);
|
hex = hex.substr(1);
|
||||||
|
|
||||||
if (hex.length === 3) {
|
if (hex.length === 3) {
|
||||||
hex = hex.replace(/(.)/g, "$1$1");
|
hex = hex.replace(/(.)/g, "$1$1");
|
||||||
}
|
}
|
||||||
|
|
||||||
hex.replace(/../g, function(color: string) {
|
hex.replace(/../g, function(color: string) {
|
||||||
rgb.push(parseInt(color, 0x10));
|
rgb.push(parseInt(color, 0x10));
|
||||||
return color;
|
return color;
|
||||||
});
|
});
|
||||||
|
|
||||||
return "rgb(" + rgb.join(",") + ")";
|
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';
|
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 = {
|
type PropsType = {
|
||||||
name: IconNames;
|
name: IconNames;
|
||||||
size?: number;
|
size?: number;
|
||||||
color?: string | string[];
|
color?: string | string[];
|
||||||
customStyle?: React.CSSProperties;
|
customStyle?: React.CSSProperties;
|
||||||
customClassName?: string;
|
customClassName?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const IconFont:FC<PropsType> = ({
|
const IconFont:FC<PropsType> = ({
|
||||||
name,
|
name,
|
||||||
size = 36,
|
size = 36,
|
||||||
color,
|
color,
|
||||||
customStyle = {},
|
customStyle = {},
|
||||||
customClassName = ""
|
customClassName = ""
|
||||||
}) => {
|
}) => {
|
||||||
const [colors, setColors] = useState<PropsType['color']>()
|
const [colors, setColors] = useState<PropsType['color']>()
|
||||||
const [isStr, setIsStr] = useState(true)
|
const [isStr, setIsStr] = useState(true)
|
||||||
const [svgSize, setSvgSize] = useState(() => (size / 750) * SystemWidth)
|
const [svgSize, setSvgSize] = useState(() => (size / 750) * SystemWidth)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsStr(typeof color === 'string')
|
setIsStr(typeof color === 'string')
|
||||||
if (typeof color === 'string') {
|
if (typeof color === 'string') {
|
||||||
setColors(color.indexOf('#') === 0 ? hex2rgb(color) : color)
|
setColors(color.indexOf('#') === 0 ? hex2rgb(color) : color)
|
||||||
} else {
|
} else {
|
||||||
setColors(
|
setColors(
|
||||||
color?.map(function (item) {
|
color?.map(function (item) {
|
||||||
return item.indexOf('#') === 0 ? hex2rgb(item) : item
|
return item.indexOf('#') === 0 ? hex2rgb(item) : item
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return () => {}
|
return () => {}
|
||||||
}, [color])
|
}, [color])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setSvgSize((size / 750) * SystemWidth)
|
setSvgSize((size / 750) * SystemWidth)
|
||||||
}, [size])
|
}, [size])
|
||||||
|
|
||||||
// 也可以使用 if (name === 'xxx') { return <view> } 来渲染,但是测试发现在ios下会有问题,报错 Maximum call stack啥的。下面这个写法没问题
|
// 也可以使用 if (name === 'xxx') { return <view> } 来渲染,但是测试发现在ios下会有问题,报错 Maximum call stack啥的。下面这个写法没问题
|
||||||
return (
|
return (
|
||||||
<Block>
|
<Block>
|
||||||
{/* icon-colorCard 本地svg */ }
|
{/* 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)} />) } */}
|
{/* { 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 */}
|
{/* icon-alipay */}
|
||||||
{/* {name === "icon-alipay" && (
|
{/* {name === "icon-alipay" && (
|
||||||
<View
|
<View
|
||||||
style={{
|
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
|
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
|
||||||
: colors[0]) ||
|
: 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
|
"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
|
||||||
: colors[1]) ||
|
: colors[1]) ||
|
||||||
"rgb(91,139,212)"}' /%3E%3C/svg%3E${quot})`, width: `${svgSize}px`, height: `${svgSize}px`,
|
"rgb(91,139,212)"}' /%3E%3C/svg%3E${quot})`, width: `${svgSize}px`, height: `${svgSize}px`,
|
||||||
...customStyle
|
...customStyle
|
||||||
}}
|
}}
|
||||||
className={classnames(icon, customClassName)}
|
className={classnames(icon, customClassName)}
|
||||||
/>
|
/>
|
||||||
)} */}
|
)} */}
|
||||||
{/* icon-lijitixian */}
|
{/* 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)} />) }
|
{ 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 */}
|
{/* 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)} />) }
|
{ 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>
|
</Block>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default IconFont
|
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;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
font-size: 26px;
|
font-size: 26px;
|
||||||
|
color: #9f9f9f;
|
||||||
}
|
}
|
||||||
|
|
||||||
.th{
|
.th {
|
||||||
font-size: 26px;
|
font-size: 26px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,11 +41,9 @@
|
|||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ellipsis_1{
|
.ellipsis_1 {
|
||||||
@include common_ellipsis(1)
|
@include common_ellipsis(1);
|
||||||
}
|
}
|
||||||
.ellipsis_2{
|
.ellipsis_2 {
|
||||||
@include common_ellipsis(2)
|
@include common_ellipsis(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,12 +1,17 @@
|
|||||||
|
|
||||||
|
|
||||||
.time-box {
|
.time-box {
|
||||||
padding: 40px;
|
padding: 40px;
|
||||||
}
|
}
|
||||||
|
.left_slot{
|
||||||
|
width: 48%;
|
||||||
|
}
|
||||||
|
.confirm_button{
|
||||||
|
min-width: 48%;
|
||||||
|
}
|
||||||
.sure-box {
|
.sure-box {
|
||||||
margin-left: 102px;
|
display: flex;
|
||||||
margin-right: 102px;
|
flex-flow: row nowrap;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0 32px;
|
||||||
font-size: 28px;
|
font-size: 28px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,16 +4,18 @@ import dayjs from 'dayjs'
|
|||||||
import NormalButton from '../normalButton'
|
import NormalButton from '../normalButton'
|
||||||
import styles from './index.module.scss'
|
import styles from './index.module.scss'
|
||||||
import AtCalendar from '@/components/calendar/index'
|
import AtCalendar from '@/components/calendar/index'
|
||||||
import Popup from '@/components/popup'
|
|
||||||
|
|
||||||
type DateArg = string | number | Date
|
type DateArg = string | number | Date
|
||||||
interface Props {
|
|
||||||
|
export interface TimePickerPropsType {
|
||||||
end?: DateArg
|
end?: DateArg
|
||||||
start?: DateArg
|
start?: DateArg
|
||||||
onSelectDate?: (any) => void
|
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 [time, setTime] = useState<any>({})
|
||||||
|
|
||||||
const handTime = (e) => {
|
const handTime = (e) => {
|
||||||
@ -34,6 +36,7 @@ const TimePicker = (props: Props) => {
|
|||||||
console.log('e===>', e)
|
console.log('e===>', e)
|
||||||
setTime(e)
|
setTime(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentDate = useMemo(() => {
|
const currentDate = useMemo(() => {
|
||||||
return (!start && !end) ? { start: `${dayjs(new Date()).format('YYYY-MM-DD')} 00:00:00`, end } : { start, end }
|
return (!start && !end) ? { start: `${dayjs(new Date()).format('YYYY-MM-DD')} 00:00:00`, end } : { start, end }
|
||||||
}, [props])
|
}, [props])
|
||||||
@ -48,7 +51,13 @@ const TimePicker = (props: Props) => {
|
|||||||
onSelectDate={e => handTime(e)}
|
onSelectDate={e => handTime(e)}
|
||||||
/>
|
/>
|
||||||
</View>
|
</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 { memo } from 'react'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
import type { TimePickerPropsType } from '../timePicker'
|
||||||
import TimePicker from '../timePicker'
|
import TimePicker from '../timePicker'
|
||||||
import Popup from '@/components/popup'
|
import Popup from '@/components/popup'
|
||||||
|
|
||||||
type DateArg = string | number | Date
|
interface Props extends TimePickerPropsType {
|
||||||
interface Props {
|
|
||||||
showTime: boolean
|
showTime: boolean
|
||||||
closePopup?: () => void
|
closePopup?: () => void
|
||||||
end?: DateArg
|
|
||||||
start?: DateArg
|
|
||||||
onSelectDate?: (any) => void
|
|
||||||
}
|
}
|
||||||
const TimePickerPopup = (props: Props) => {
|
const TimePickerPopup = (props: Props) => {
|
||||||
const { showTime = false, closePopup, start = '', end = '', onSelectDate } = props
|
const { showTime = false, closePopup, start = '', end = '', onSelectDate, leftSlot } = props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popup title="选择时间" show={showTime} onClose={() => closePopup?.()}>
|
<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>
|
</Popup>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,6 +27,9 @@
|
|||||||
.share,
|
.share,
|
||||||
.collect {
|
.collect {
|
||||||
width: 76px;
|
width: 76px;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column nowrap;
|
||||||
|
align-items: center;
|
||||||
font-size: $font_size_min;
|
font-size: $font_size_min;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: $color_font_three;
|
color: $color_font_three;
|
||||||
|
|||||||
@ -57,19 +57,20 @@ const Details = (props: Params) => {
|
|||||||
const [recommendStatus, setRecommendStatus] = useState(false)
|
const [recommendStatus, setRecommendStatus] = useState(false)
|
||||||
|
|
||||||
// 解析短码参数
|
// 解析短码参数
|
||||||
const { fetchData: fetchDataAnalysisShortCode } = AnalysisShortCodeApi()
|
// const { fetchData: fetchDataAnalysisShortCode } = AnalysisShortCodeApi()
|
||||||
const analysisShortCode = async() => {
|
// const analysisShortCode = async() => {
|
||||||
const res = await fetchDataAnalysisShortCode({ md5_key: router.params.share })
|
// const res = await fetchDataAnalysisShortCode({ md5_key: router.params.share })
|
||||||
setParams({ id: res.data.product_id, share: res.data })
|
// setParams({ id: res.data.product_id, share: res.data })
|
||||||
}
|
// }
|
||||||
// 判断是否是分享过来的参数
|
// 判断是否是分享过来的参数
|
||||||
const judgeParam = async() => {
|
const judgeParam = async() => {
|
||||||
|
console.log('router.params', router.params)
|
||||||
if (router.params.id) {
|
if (router.params.id) {
|
||||||
setParams({ ...params, id: router.params.id })
|
setParams({ ...params, id: router.params.id })
|
||||||
}
|
}
|
||||||
else if (router.params.share) {
|
// else if (router.params.share) {
|
||||||
analysisShortCode()
|
// analysisShortCode()
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
// 获取购物车数据数量
|
// 获取购物车数据数量
|
||||||
const { getShopCount, commonData } = useCommonData()
|
const { getShopCount, commonData } = useCommonData()
|
||||||
@ -78,6 +79,7 @@ const Details = (props: Params) => {
|
|||||||
const [productInfo, setProductInfo] = useState<any>({})
|
const [productInfo, setProductInfo] = useState<any>({})
|
||||||
const { fetchData } = GetProductDetailApi()
|
const { fetchData } = GetProductDetailApi()
|
||||||
const getProductDetail = async() => {
|
const getProductDetail = async() => {
|
||||||
|
console.log('params', params)
|
||||||
const { data } = await fetchData({ id: params.id })
|
const { data } = await fetchData({ id: params.id })
|
||||||
setProductId(data.id)
|
setProductId(data.id)
|
||||||
setProductInfo(data)
|
setProductInfo(data)
|
||||||
@ -115,7 +117,7 @@ const Details = (props: Params) => {
|
|||||||
type: ShareDetail.value,
|
type: ShareDetail.value,
|
||||||
product_id: parseInt(params.id),
|
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(() => {
|
useDidShow(() => {
|
||||||
judgeParam()
|
judgeParam()
|
||||||
@ -282,17 +284,13 @@ const Details = (props: Params) => {
|
|||||||
<View className={styles.des}>{productInfo.describe}</View>
|
<View className={styles.des}>{productInfo.describe}</View>
|
||||||
</View>
|
</View>
|
||||||
<View className={styles.collect} onClick={openCollection}>
|
<View className={styles.collect} onClick={openCollection}>
|
||||||
<View
|
{
|
||||||
className={classnames(
|
collectStatus ? <IconFont name="icon-shoucangchenggong" size={45} color="#ffc300"></IconFont> : <IconFont name="icon-dianjishoucang" size={45}></IconFont>
|
||||||
`iconfont ${collectStatus ? 'icon-shoucangchenggong' : 'icon-dianjishoucang'}`,
|
}
|
||||||
styles.miconfont,
|
|
||||||
collectStatus && styles.collected,
|
|
||||||
)}
|
|
||||||
></View>
|
|
||||||
<View className={styles.text}>收藏</View>
|
<View className={styles.text}>收藏</View>
|
||||||
</View>
|
</View>
|
||||||
<View className={styles.share}>
|
<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>
|
<View className={styles.text}>分享</View>
|
||||||
<Button open-type="share" className={styles.shareBtn}></Button>
|
<Button open-type="share" className={styles.shareBtn}></Button>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
export default {
|
export default {
|
||||||
navigationBarTitleText: '邀请码',
|
navigationBarTitleText: '邀请朋友',
|
||||||
navigationBarTextStyle: 'black',
|
navigationBarTextStyle: 'black',
|
||||||
navigationBarBackgroundColor: '#E4EEFD',
|
navigationBarBackgroundColor: '#E4EEFD',
|
||||||
backgroundColor: '#E4EEFD',
|
backgroundColor: '#E4EEFD',
|
||||||
|
enableShareAppMessage: true,
|
||||||
backgroundColorTop: '#E4EEFD',
|
backgroundColorTop: '#E4EEFD',
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,10 +29,19 @@ page {
|
|||||||
padding-bottom: 5px;
|
padding-bottom: 5px;
|
||||||
font-size: 60px;
|
font-size: 60px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
&_text {
|
||||||
|
color: #424c6f;
|
||||||
|
}
|
||||||
|
&_plus {
|
||||||
|
font-size: 40px;
|
||||||
|
vertical-align: top;
|
||||||
|
color: $color_main;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.description {
|
.description {
|
||||||
font-size: 28px;
|
font-size: 28px;
|
||||||
color: #848689;
|
color: #686868;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -148,7 +157,7 @@ page {
|
|||||||
flex-flow: row nowrap;
|
flex-flow: row nowrap;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding-top: 24px;
|
padding-top: 16px;
|
||||||
padding-right: 48px;
|
padding-right: 48px;
|
||||||
padding-left: 48px;
|
padding-left: 48px;
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
@ -156,6 +165,7 @@ page {
|
|||||||
padding-bottom: calc(20px + env(safe-area-inset-bottom));
|
padding-bottom: calc(20px + env(safe-area-inset-bottom));
|
||||||
&__text {
|
&__text {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
font-size: 28px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.codePreview {
|
.codePreview {
|
||||||
@ -164,13 +174,15 @@ page {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
position: relative;
|
position: relative;
|
||||||
top: -120px;
|
top: -90px;
|
||||||
.imageContainer {
|
.imageContainer {
|
||||||
width: 80vw;
|
width: 80vw;
|
||||||
height: auto;
|
height: auto;
|
||||||
|
overflow: hidden;
|
||||||
.image {
|
.image {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
border-radius: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.previewTips {
|
.previewTips {
|
||||||
|
|||||||
@ -3,265 +3,95 @@ import Taro, { useReady } from '@tarojs/taro'
|
|||||||
import { useRef, useState } from 'react'
|
import { useRef, useState } from 'react'
|
||||||
import styles from './index.module.scss'
|
import styles from './index.module.scss'
|
||||||
import useLogin from '@/use/useLogin'
|
import useLogin from '@/use/useLogin'
|
||||||
import { alert, goLink } from '@/common/common'
|
|
||||||
import { GenBarCodeOrQrCode, GetInvitationInfo, GetInvitationInfoApi, GetInviteCodeList, GetInviteeRecord } from '@/api/user'
|
import { GenBarCodeOrQrCode, GetInvitationInfo, GetInvitationInfoApi, GetInviteCodeList, GetInviteeRecord } from '@/api/user'
|
||||||
import { getFilterData } from '@/common/util'
|
|
||||||
import LayoutBlock from '@/components/layoutBlock'
|
import LayoutBlock from '@/components/layoutBlock'
|
||||||
import { getCDNSource } from '@/common/constant'
|
import { getCDNSource } from '@/common/constant'
|
||||||
import NormalButton from '@/components/normalButton'
|
import NormalButton from '@/components/normalButton'
|
||||||
import Dialog from '@/components/Dialog'
|
import type { TablePropsType } from '@/components/table'
|
||||||
import IconText from '@/components/iconText'
|
import Table from '@/components/table'
|
||||||
import BindSalesManDialog from '@/components/bindSalesManDialog'
|
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 }
|
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 = () => {
|
const BindSalesman = () => {
|
||||||
useLogin()
|
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 getInviteeRecord = async() => {
|
||||||
const res = await getInviteeRecordAPI()
|
const res = await getInviteeRecordAPI()
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
setSalesMan(res.data)
|
setCurrentTable((prev: any) => ({
|
||||||
}
|
...prev,
|
||||||
}
|
dataSource: {
|
||||||
const { fetchData } = GetInvitationInfo()
|
list: res.data.list.map((item, index: number) => ({
|
||||||
// 获取邀请码
|
key: index,
|
||||||
const getInviteCode = async() => {
|
invitationWay: item.invitation_way_name || '--',
|
||||||
const res = await fetchData()
|
invitee: item.passive_invited_user_name || '--',
|
||||||
if (res.success) {
|
inviteTime: formatDateTime(item.invitation_time, 'YYYY-MM-DD') || '--',
|
||||||
setInviteInfo(res.data)
|
})),
|
||||||
// getInvitationInfo()
|
total: res.data.list.length,
|
||||||
}
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
})
|
}))
|
||||||
})
|
}
|
||||||
}
|
|
||||||
// 复制二维码
|
|
||||||
const handleCopyInviteCode = () => {
|
|
||||||
Taro.setClipboardData({
|
|
||||||
data: inviteInfo.invitation_code,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
const handleChange = (value: boolean) => {
|
|
||||||
setShowPopup(value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
useReady(() => {
|
useReady(() => {
|
||||||
getInviteCode()
|
|
||||||
getInvitationList()
|
|
||||||
getInviteeRecord()
|
getInviteeRecord()
|
||||||
setTimeout(() => {
|
|
||||||
initCanvas()
|
|
||||||
}, 200)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const inviteCodePopupRef = useRef<InviteCodePopupRef>(null)
|
||||||
|
|
||||||
const handleQRcodeShare = async() => {
|
const handleQRcodeShare = async() => {
|
||||||
try {
|
inviteCodePopupRef.current?.startDrawPoster()
|
||||||
const flag = await drawPictorial()
|
|
||||||
if (flag) {
|
|
||||||
setShowPopup(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
throw new Error('弹出二维码失败')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// 邀请记录
|
|
||||||
const handleInviteCord = () => {
|
const [currentTable, setCurrentTable] = useState<TablePropsType>({
|
||||||
goLink('/pages/inviteCode/inviteCord/index')
|
columns: inviteColumns,
|
||||||
}
|
dataSource: { list: [], total: 0 },
|
||||||
const handleBindSalesMan = () => {
|
})
|
||||||
inviteDialog.current.handleChange(true)
|
|
||||||
}
|
const handleLoadMore = () => {
|
||||||
const handleBindSuccess = () => {
|
size.current += defaultSize
|
||||||
getInviteeRecord()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View className={styles.main}>
|
<View className={styles.main}>
|
||||||
<View className={styles.content}>
|
<View className={styles.content}>
|
||||||
<View className={styles.background}>
|
<View className={styles.background}>
|
||||||
<View className={styles.left}>
|
<View className={styles.left}>
|
||||||
<View className={styles.title}>商城</View>
|
<View className={styles.title}>
|
||||||
<View className={styles.description}>邀请好友加入</View>
|
<Text className={styles.title_text}>面料优选A</Text>
|
||||||
|
<Text className={styles.title_plus}>+</Text>
|
||||||
|
</View>
|
||||||
|
<View className={styles.description}>查看成功邀请人信息</View>
|
||||||
</View>
|
</View>
|
||||||
<View className={styles.right}>
|
<View className={styles.right}>
|
||||||
<View className={styles.iconContainer}>
|
<View className={styles.iconContainer}>
|
||||||
@ -270,65 +100,24 @@ const BindSalesman = () => {
|
|||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<View className={styles.inviteCodeContent}>
|
<View className={styles.inviteCodeContent}>
|
||||||
<LayoutBlock circle>
|
<LayoutBlock circle customStyle={{ paddingTop: '10px', paddingBottom: '10px' }}>
|
||||||
<View className={styles.codeBar}>
|
<View className={styles.inviteListTitle}>
|
||||||
<View className={styles.inviteCodeBar}>
|
<View className={styles.titleIconLeft}></View>
|
||||||
<View className={styles.invite}>{inviteInfo.invitation_code ? inviteInfo.invitation_code : '暂无邀请码'}</View>
|
<Text className={styles.listTitle}>成功邀请</Text>
|
||||||
</View>
|
<View className={styles.titleIconRight}></View>
|
||||||
<View className={styles.codeTitle}>您的专属邀请码</View>
|
</View>
|
||||||
|
<View className={styles.inviteList}>
|
||||||
|
<Table columns={currentTable.columns} emptyText="暂无邀请信息" safeAreaInsetBottom={false} dataSource={currentTable.dataSource} onLoadMore={handleLoadMore}></Table>
|
||||||
</View>
|
</View>
|
||||||
</LayoutBlock>
|
</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>
|
||||||
</View>
|
</View>
|
||||||
{/* 已踩坑,这里必须设置canvas的style的width和height,单单只设置canvas实例的width和height是不行的。会模糊! */}
|
|
||||||
<Canvas style={{ position: 'absolute', left: '-9999rpx', ...canvasStyle }} id="canvas" type="2d" />
|
|
||||||
<View className={styles.bottomBar}>
|
<View className={styles.bottomBar}>
|
||||||
<NormalButton loading={loading} plain type="primary" customTextClassName={styles.bottomBar__text} customStyles={{ width: '45%' }} round onClick={handleQRcodeShare}>
|
<NormalButton type="primary" round customTextClassName={styles.bottomBar__text} customStyles={{ width: '100%' }} onClick={handleQRcodeShare}>
|
||||||
二维码分享
|
生成邀请海报
|
||||||
</NormalButton>
|
|
||||||
<NormalButton type="primary" round customTextClassName={styles.bottomBar__text} customStyles={{ width: '45%' }} onClick={handleCopyInviteCode}>
|
|
||||||
复制邀请码
|
|
||||||
</NormalButton>
|
</NormalButton>
|
||||||
</View>
|
</View>
|
||||||
<Dialog show={showPopup} onChange={handleChange}>
|
<InviteCodePopup ref={inviteCodePopupRef}></InviteCodePopup>
|
||||||
<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} />
|
|
||||||
</View>
|
</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 { Text, View } from '@tarojs/components'
|
||||||
import Taro, { usePullDownRefresh, useReady, useRouter } from '@tarojs/taro'
|
import Taro, { usePullDownRefresh, useReady, useRouter } from '@tarojs/taro'
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||||
|
import type { ListProps } from './components/selectData'
|
||||||
import SelectData from './components/selectData'
|
import SelectData from './components/selectData'
|
||||||
import styles from './searchList.module.scss'
|
import styles from './searchList.module.scss'
|
||||||
import Search from '@/components/search'
|
import Search from '@/components/search'
|
||||||
|
|||||||
@ -53,7 +53,22 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
position: relative;
|
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 {
|
.arcd-info-left-phone {
|
||||||
position: relative;
|
position: relative;
|
||||||
.header_title {
|
.header_title {
|
||||||
@ -63,10 +78,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
text {
|
text {
|
||||||
&:nth-child(1) {
|
|
||||||
font-size: 48px;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
&:nth-child(2) {
|
&:nth-child(2) {
|
||||||
font-size: 28px;
|
font-size: 28px;
|
||||||
color: rgba(0, 0, 0, 0.6);
|
color: rgba(0, 0, 0, 0.6);
|
||||||
|
|||||||
@ -11,8 +11,7 @@ import { userassets, userorderStatistics } from '@/api/mine'
|
|||||||
import useLogin from '@/use/useLogin'
|
import useLogin from '@/use/useLogin'
|
||||||
import IconFont from '@/components/iconfont/iconfont'
|
import IconFont from '@/components/iconfont/iconfont'
|
||||||
import SvgIconfont from '@/components/svgIconfont'
|
import SvgIconfont from '@/components/svgIconfont'
|
||||||
import MoveBtn from '@/components/moveBtn'
|
import { BASE_URL, getCDNSource } from '@/common/constant'
|
||||||
import { BASE_URL } from '@/common/constant'
|
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
const userInfo = useSelector(state => state.userInfo)
|
const userInfo = useSelector(state => state.userInfo)
|
||||||
@ -88,8 +87,17 @@ export default () => {
|
|||||||
Taro.stopPullDownRefresh()
|
Taro.stopPullDownRefresh()
|
||||||
})
|
})
|
||||||
|
|
||||||
const [current_version, setCurrent_version] = useState(CURRENT_VERSION)
|
const handleClickInviteFriends = () => {
|
||||||
const [current_env, setCurrent_env] = useState(CURRENT_ENV)
|
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 (
|
return (
|
||||||
<View className={styles.user_main}>
|
<View className={styles.user_main}>
|
||||||
@ -101,7 +109,14 @@ export default () => {
|
|||||||
<Image className={styles.header_img_src} mode="aspectFill" src={`${userInfo?.adminUserInfo?.avatar_url}`} />
|
<Image className={styles.header_img_src} mode="aspectFill" src={`${userInfo?.adminUserInfo?.avatar_url}`} />
|
||||||
</View>
|
</View>
|
||||||
<View className={styles.header_name}>
|
<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['arcd-info-left-phone']}>
|
||||||
<View className={styles.header_title}> {userInfo?.adminUserInfo?.phone || 'Hi,欢迎来到商城'}</View>
|
<View className={styles.header_title}> {userInfo?.adminUserInfo?.phone || 'Hi,欢迎来到商城'}</View>
|
||||||
</View>
|
</View>
|
||||||
@ -122,9 +137,9 @@ export default () => {
|
|||||||
<Text>0</Text>
|
<Text>0</Text>
|
||||||
<Text>活动积分</Text>
|
<Text>活动积分</Text>
|
||||||
</View>
|
</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} />
|
<IconFont name="icon-erweima" size={50} />
|
||||||
<Text>邀请码</Text>
|
<Text>邀请好友</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<Image className={styles.header_bg} src={formatImgUrl('/mall/user_header_bg.png', '')} mode="aspectFit"></Image>
|
<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 { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import { alert, goLink, isEmptyObject, retrieval } from '@/common/common'
|
import { alert, goLink, isEmptyObject, retrieval } from '@/common/common'
|
||||||
import Popup from '@/components/popup'
|
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 { companyDetailApi, companyUpdateApi } from '@/api/company'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
import ModifyModal from './components/ModifyModal'
|
import ModifyModal from './components/ModifyModal'
|
||||||
@ -33,7 +33,7 @@ export default () => {
|
|||||||
// 表单数据
|
// 表单数据
|
||||||
const [formData, setFormData] = useState<typeof adminUserInfo>(adminUserInfo)
|
const [formData, setFormData] = useState<typeof adminUserInfo>(adminUserInfo)
|
||||||
// 昵称修改保存
|
// 昵称修改保存
|
||||||
const { fetchData: realNameUpdateFetch } = realNameUpdateApi()
|
const { fetchData: realNameUpdateFetch } = RealNameUpdateApi()
|
||||||
const rules = {
|
const rules = {
|
||||||
text: [
|
text: [
|
||||||
{
|
{
|
||||||
@ -77,7 +77,7 @@ export default () => {
|
|||||||
goLink(url)
|
goLink(url)
|
||||||
}
|
}
|
||||||
// 肖像编辑
|
// 肖像编辑
|
||||||
const { fetchData: portraitUpdateFetch } = portraitUpdateApi()
|
const { fetchData: portraitUpdateFetch } = PortraitUpdateApi()
|
||||||
const { getWxPhoto } = useUploadCDNImg()
|
const { getWxPhoto } = useUploadCDNImg()
|
||||||
const handleSelectRortrait = () => {
|
const handleSelectRortrait = () => {
|
||||||
Taro.showModal({
|
Taro.showModal({
|
||||||
@ -112,7 +112,7 @@ export default () => {
|
|||||||
const ModifyIcknameEl = useRef(null)
|
const ModifyIcknameEl = useRef(null)
|
||||||
const ModifyCompanyNameEl = useRef(null)
|
const ModifyCompanyNameEl = useRef(null)
|
||||||
// 获取企业类型
|
// 获取企业类型
|
||||||
const { fetchData: companyTypeFetch, state: companyTypeData } = companyTypeApi()
|
const { fetchData: companyTypeFetch, state: companyTypeData } = CompanyTypeApi()
|
||||||
const getCompanyTypeData = async() => {
|
const getCompanyTypeData = async() => {
|
||||||
const reuslt = await companyTypeFetch()
|
const reuslt = await companyTypeFetch()
|
||||||
if (reuslt.success) {
|
if (reuslt.success) {
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import type { ModifyModalRef } from './components/ModifyModal'
|
|||||||
import ModifyModal from './components/ModifyModal'
|
import ModifyModal from './components/ModifyModal'
|
||||||
import { alert, goLink, isEmptyObject, retrieval } from '@/common/common'
|
import { alert, goLink, isEmptyObject, retrieval } from '@/common/common'
|
||||||
import Popup from '@/components/popup'
|
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 { companyDetailApi, companyUpdateApi } from '@/api/company'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
import useLogin from '@/use/useLogin'
|
import useLogin from '@/use/useLogin'
|
||||||
@ -45,7 +45,7 @@ export default () => {
|
|||||||
}, [adminUserInfo])
|
}, [adminUserInfo])
|
||||||
|
|
||||||
// 昵称修改保存
|
// 昵称修改保存
|
||||||
const { fetchData: realNameUpdateFetch } = realNameUpdateApi()
|
const { fetchData: realNameUpdateFetch } = RealNameUpdateApi()
|
||||||
const rules = {
|
const rules = {
|
||||||
text: [
|
text: [
|
||||||
{
|
{
|
||||||
@ -89,7 +89,7 @@ export default () => {
|
|||||||
goLink(url)
|
goLink(url)
|
||||||
}
|
}
|
||||||
// 肖像编辑
|
// 肖像编辑
|
||||||
const { fetchData: portraitUpdateFetch } = portraitUpdateApi()
|
const { fetchData: portraitUpdateFetch } = PortraitUpdateApi()
|
||||||
const { getWxPhoto } = useUploadCDNImg()
|
const { getWxPhoto } = useUploadCDNImg()
|
||||||
const handleSelectRortrait = () => {
|
const handleSelectRortrait = () => {
|
||||||
Taro.showModal({
|
Taro.showModal({
|
||||||
@ -123,7 +123,7 @@ export default () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取企业类型
|
// 获取企业类型
|
||||||
const { fetchData: companyTypeFetch, state: companyTypeData } = companyTypeApi()
|
const { fetchData: companyTypeFetch, state: companyTypeData } = CompanyTypeApi()
|
||||||
const getCompanyTypeData = async() => {
|
const getCompanyTypeData = async() => {
|
||||||
const reuslt = await companyTypeFetch()
|
const reuslt = await companyTypeFetch()
|
||||||
if (reuslt.success) {
|
if (reuslt.success) {
|
||||||
|
|||||||
@ -40,6 +40,8 @@ export interface UserAdminParam {
|
|||||||
wechat_user_open_id: number
|
wechat_user_open_id: number
|
||||||
is_authorize_name: boolean
|
is_authorize_name: boolean
|
||||||
is_authorize_phone: boolean
|
is_authorize_phone: boolean
|
||||||
|
is_passive_invite: boolean // 是否被邀请
|
||||||
|
is_bd: boolean // 是否是bd
|
||||||
phone: string
|
phone: string
|
||||||
authentication_status: number
|
authentication_status: number
|
||||||
authentication_status_name: string
|
authentication_status_name: string
|
||||||
@ -59,7 +61,7 @@ export interface UserAdminParam {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface SortCodeParam {
|
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 } // 右上角分享页面短码
|
shareShortPage?: { title: string; code: string; img: string } // 右上角分享页面短码
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -210,7 +210,7 @@ $at-fab-box-shadow-active:
|
|||||||
$at-calendar-day-size: 72px !default;
|
$at-calendar-day-size: 72px !default;
|
||||||
$at-calendar-mark-size: 8px !default;
|
$at-calendar-mark-size: 8px !default;
|
||||||
$at-calendar-header-color: #B8BFC6 !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;
|
$at-calendar-day-color: #7C86A2 !default;
|
||||||
|
|
||||||
/* Card */
|
/* Card */
|
||||||
|
|||||||
@ -1,13 +1,14 @@
|
|||||||
import Taro, { useDidShow, useRouter } from '@tarojs/taro'
|
import Taro, { useDidShow } from '@tarojs/taro'
|
||||||
import useUserInfo from './useUserInfo'
|
import useUserInfo from './useUserInfo'
|
||||||
import useLoginRequest from './useLoginRequest'
|
import useLoginRequest from './useLoginRequest'
|
||||||
import { BindingCompanyApi, GetAdminUserInfoApi, GetPhoneNumberApi, GetWxUserInfoApi } from '@/api/user'
|
import { BindingCompanyApi, GetAdminUserInfoApi, GetPhoneNumberApi, GetWxUserInfoApi } from '@/api/user'
|
||||||
|
import type { InvitationWay } from '@/common/enum'
|
||||||
import { SHARE_SCENE } from '@/common/enum'
|
import { SHARE_SCENE } from '@/common/enum'
|
||||||
import { GetShortCodeApi } from '@/api/share'
|
import { GetShortCodeApi, PrepareCreateInvitationInfoApi } from '@/api/share'
|
||||||
import { alert } from '@/common/common'
|
|
||||||
import { LoginApi } from '@/api/login'
|
|
||||||
import { IMG_CND_Prefix } from '@/common/constant'
|
import { IMG_CND_Prefix } from '@/common/constant'
|
||||||
import { formatImgUrl } from '@/common/fotmat'
|
import { formatImgUrl } from '@/common/fotmat'
|
||||||
|
import { bindInvitationUser } from '@/common/shortCode'
|
||||||
|
import { isEmptyObject } from '@/common/common'
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
const { setUserInfo, setAdminUserInfo, setSortCode, userInfo } = useUserInfo()
|
const { setUserInfo, setAdminUserInfo, setSortCode, userInfo } = useUserInfo()
|
||||||
@ -47,7 +48,8 @@ export default () => {
|
|||||||
if (res.success) {
|
if (res.success) {
|
||||||
setUserInfo({ ...userInfo.userInfo, phone: res.data.phone_number })
|
setUserInfo({ ...userInfo.userInfo, phone: res.data.phone_number })
|
||||||
await fetchBindingCompany()
|
await fetchBindingCompany()
|
||||||
getAdminUserInfo()
|
await getAdminUserInfo()
|
||||||
|
handleBindInvitationUser()
|
||||||
resolve(res.data)
|
resolve(res.data)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -57,11 +59,10 @@ export default () => {
|
|||||||
}
|
}
|
||||||
// 登录请求
|
// 登录请求
|
||||||
const { login } = useLoginRequest()
|
const { login } = useLoginRequest()
|
||||||
// const {fetchData:login} = LoginApi()
|
|
||||||
const wxLogin = async() => {
|
const wxLogin = async() => {
|
||||||
try {
|
try {
|
||||||
await login()
|
await login()
|
||||||
getAdminUserInfo()
|
await getAdminUserInfo()
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
console.log('登录失败::', 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 {
|
return {
|
||||||
checkLogin,
|
checkLogin,
|
||||||
wxLogin,
|
wxLogin,
|
||||||
|
|||||||
@ -20,17 +20,23 @@ export default () => {
|
|||||||
const loginData = useRef<Param>(initData)
|
const loginData = useRef<Param>(initData)
|
||||||
const { setToken, setSessionKey } = useUserInfo()
|
const { setToken, setSessionKey } = useUserInfo()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
|
const page = Taro.getCurrentInstance().page
|
||||||
// 微信登录请求v2
|
// 微信登录请求v2
|
||||||
const fetchDataLogin = async(login_code) => {
|
const fetchDataLogin = async(login_code) => {
|
||||||
const q = {
|
const q = {
|
||||||
url: `${BASE_URL}/v1/mall/login`,
|
url: `${BASE_URL}/v3/mallCherry/login`,
|
||||||
header: {
|
header: {
|
||||||
// Platform: 6,
|
// Platform: 6,
|
||||||
Platform,
|
Platform,
|
||||||
Appid: WX_APPID,
|
Appid: WX_APPID,
|
||||||
},
|
},
|
||||||
method: 'post',
|
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 {
|
try {
|
||||||
const result = await Taro.request(q as any)
|
const result = await Taro.request(q as any)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user