首次提交

This commit is contained in:
2025-04-13 19:40:44 +08:00
commit eec6aef7d7
440 changed files with 44422 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
export default definePageConfig({
navigationBarTitleText: '邀请函',
navigationBarBackgroundColor: '#ffe0e0'
})

View File

@@ -0,0 +1,13 @@
.cert-bg{
width: 750px;
height: 1320px;
margin: 0 auto;
background: url("https://oss.wsdns.cn/20250304/5147356628a3460e811cf5fb90df26a7.png") no-repeat;
background-size: 100%;
}
.cert-content{
width: 480px;
height: 720px;
font-size: 24px;
padding-top: 250px;
}

View File

@@ -0,0 +1,89 @@
import './bm-cert.scss'
import {useEffect, useState} from "react";
import Taro from '@tarojs/taro'
import {getPoster} from "@/api/bszx/bszxBm";
import {ConfigProvider, Loading} from "@nutui/nutui-react-taro";
function BmCert() {
const [poster, setPoster] = useState<string>('')
const [loading, setLoading] = useState<boolean>(true)
const generatePoster = () => {
if (process.env.NODE_ENV === 'development') {
// setPoster("https://oss.wsdns.cn/20250306/deec1c968aed4bc58a168a0f63ac8c87.jpg")
}
// 保存到临时地址
Taro.downloadFile({
url: poster,
success: function (res) {
Taro.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success: function () {
Taro.showToast({
title: '保存成功',
icon: 'success',
duration: 2000
});
},
fail: function (err) {
if (err.errMsg === 'saveImageToPhotosAlbum:fail auth deny') {
}
}
})
}
})
}
const reload = () => {
getPoster().then(img => {
setPoster(`${img}`)
setTimeout(() => {
setLoading(false)
},1000)
if (process.env.NODE_ENV === 'development') {
// setPoster("https://oss.wsdns.cn/20250306/deec1c968aed4bc58a168a0f63ac8c87.jpg")
}
})
}
useEffect(() => {
reload()
}, []);
return (
<>
{
loading ?
<div className={'bg-white w-full flex justify-center items-center'} style={{height: '100vh'}}>
<ConfigProvider theme={{ nutuiLoadingIconSize: '28px' }}>
<Loading type="spinner"></Loading>
</ConfigProvider>
</div>
:
<div className={'flex justify-center'}>
<img src={poster} width={'100vw'} height={'91vh'}/>
{/*<div className={'cert-content'}>*/}
{/* <p className={'py-2'}>尊敬的 {nickName} 校友 :</p>*/}
{/* 百廿风华桃李百中。2025年<i className={'text-red-500 font-bold'}>百色市百色中学</i>迎来建校 120*/}
{/* 周年。十秩沧桑砺洗、薪火相继,百色中学走过百折不挠、上下求索的光辉历程;爱党爱国、无私奉献、团结务实、积极进取的百中精神,始终引领学校风雨兼程、砥砺奋进;一代代百中人的耕耘与拼搏,闪耀着不同时期特有的亮点,积淀深厚的文化底蕴,承载着国家和人民殷切期望,铸造了今日的辉煌。兹定于*/}
{/* 2025年5月1日(星期四)上午9:00 举行百色中学建校 120*/}
{/* 周年庆典活动。诚邀您拨冗莅临出席、共襄盛举,为母校的改革发展献计献策!相约浓浓绎夏,畅谈别后心路,共话同窗师友情;相信您的到来必定令校庆活动添光异彩。*/}
{/* 专此诚邀,敬祈惠允。*/}
{/* <p className={'mt-2 text-right'}>百色市百色中学</p>*/}
{/* <p className={'text-right'}>2025年1月2日</p>*/}
{/*</div>*/}
<div className={'fixed bottom-10'}>
{/*<Button />*/}
<button
className={'item px-20 py-3 bg-yellow-300 text-black flex items-center gap-2 text-nowrap whitespace-nowrap'}
onClick={() => {
generatePoster()
}}>
</button>
</div>
</div>
}
</>
)
}
export default BmCert;

View File

@@ -0,0 +1,4 @@
export default definePageConfig({
navigationBarTitleText: '我的报名',
navigationBarBackgroundColor: '#ffe0e0'
})

View File

@@ -0,0 +1,33 @@
import { Cell } from '@nutui/nutui-react-taro'
import {useEffect, useState} from "react";
import {myPageBszxBm} from "@/api/bszx/bszxBm";
import {BszxBm} from "@/api/bszx/bszxBm/model";
import navTo from "@/utils/common";
const BmLog = () => {
const [list, setList] = useState<BszxBm[]>()
const reload = () => {
myPageBszxBm({limit: 1000}).then(res => {
if(res.list){
setList(res.list);
}
})
}
useEffect(() => {
reload()
}, [])
return (
<div className={'p-4'}>
{list?.map((item, index) => {
return (
<Cell title={item.name} extra={item.createTime} key={index} onClick={() => {
navTo('/bszx/bm-cert/bm-cert?id=' + item.id,true)
}} />
)
})}
</div>
)
}
export default BmLog

4
src/bszx/bm.config.ts Normal file
View File

@@ -0,0 +1,4 @@
export default definePageConfig({
navigationBarTitleText: '我要报名',
navigationBarBackgroundColor: '#ffe0e0'
})

388
src/bszx/bm.tsx Normal file
View File

@@ -0,0 +1,388 @@
import {useEffect, useState} from 'react'
import Taro, {getCurrentInstance} from '@tarojs/taro'
import {
Form,
Button,
Input,
Radio,
SideNavBar,
SubSideNavBar,
SideNavBarItem
} from '@nutui/nutui-react-taro'
import {DictData} from "@/api/system/dict-data/model";
import {Picker} from '@nutui/nutui-react-taro'
import {pageDictData} from "@/api/system/dict-data";
import {addBszxBm, myPageBszxBm} from "@/api/bszx/bszxBm";
import {BszxBm} from "@/api/bszx/bszxBm/model";
import {getBszxClassForTree} from "@/api/bszx/bszxClass";
// import {User} from "@/api/system/user/model";
// import Banner from "../pages/index/Banner";
const {router} = getCurrentInstance()
const Bm = () => {
const formId = Number(router?.params.id)
const [form] = Form.useForm()
const [sex, setSex] = useState<DictData[]>()
const [gradeName, setGradeName] = useState<string>('')
const [className, setClassName] = useState<string>('')
const [phone, setPhone] = useState<string>('')
const [classList, setClassList] = useState<any[]>()
const [gradeList, setGradeList] = useState<any[]>()
const [present, setPresent] = useState<DictData[]>()
const [isVisibleClass, setIsVisibleClass] = useState(false)
const [isVisibleGrade, setIsVisibleGrade] = useState(false)
const [FormData, setFormData] = useState<BszxBm>(
{
type: 0,
name: undefined,
sex: undefined,
phone: undefined,
className: undefined,
gradeName: undefined,
address: undefined,
workUnit: undefined,
position: undefined,
present: undefined,
formId: undefined,
comments: undefined
}
)
// 提交表单
const submitSucceed = (values: any) => {
addBszxBm({
formId,
name: values.name || FormData.name,
sex: values.sex || FormData.sex,
phone: phone,
type: values.type || FormData.type,
className: className || FormData.className,
gradeName: gradeName || FormData.gradeName,
address: values.address || FormData.address,
workUnit: values.workUnit || FormData.workUnit,
position: values.position || FormData.position,
present: values.present ? '能' : '',
comments: values.comments || FormData.comments
}).then((data) => {
if (data) {
console.log(data,'data')
Taro.showToast({title: `报名成功`, icon: 'success'})
// setFormData();
Taro.setStorageSync('NickName', values.name || FormData.name)
setTimeout(() => {
Taro.navigateTo({
url: '/bszx/bm-cert/bm-cert'
})
}, 1000)
}
}).catch(() => {
Taro.showToast({
title: '请勿重复报名',
icon: 'error'
});
})
}
const submitFailed = (error: any) => {
console.log(error, 'err...')
// Taro.showToast({ title: error[0].message, icon: 'error' })
}
const classConfirmPicker = (
options: any[]
) => {
let description = ''
options.forEach((option: any) => {
description += ` ${option.text}`
})
setClassName(description)
}
const gradeNameConfirmPicker = (
options: any[]
) => {
let description = ''
options.forEach((option: any) => {
description += ` ${option.text}`
})
setGradeName(description)
}
const changePicker = (options: any[], values: any, columnIndex: number) => {
form.setFieldValue('className', values)
console.log(options, columnIndex)
}
const [navBarState, setNavBarState] = useState({
visible: false
})
const changeNarBar = (visible) => {
setNavBarState({
visible
})
}
const onClassChange = (gradeName: string, className: string) => {
console.log(gradeName,className)
setGradeName(gradeName);
setClassName(className);
setFormData({
...FormData,
gradeName,
className
})
setNavBarState({
visible: false
})
}
const reload = () => {
if (!Taro.getStorageSync('access_token')) {
Taro.showModal({
title: '提示',
content: '请先登录',
showCancel: false,
success: function (res) {
if (res.confirm) {
Taro.switchTab({
url: '/pages/user/user'
})
}
}
})
return false;
}
myPageBszxBm().then(res => {
const item = res.list[0];
console.log(item, 'myPageBszxBm')
if (item) {
setFormData(item)
if (item.gradeName) {
setGradeName(item.gradeName)
}
if (item.className) {
setClassName(item.className)
}
}
})
getBszxClassForTree().then(res => {
console.log(res, 'setClassList');
setClassList(res);
})
pageDictData({limit: 200}).then(res => {
setSex(res?.list.filter((item) => item.dictCode === 'sex'))
setPresent(res?.list.filter((item) => item.dictCode === 'present'))
// setClassList([res?.list.filter((item) => item.dictCode === 'Class')])
setGradeList(res?.list.filter((item) => {
if (item.dictCode === 'Grade') {
item.value = item.dictDataCode;
item.text = item.dictDataName;
return item
}
}))
})
setPhone(Taro.getStorageSync('Phone'))
form.setFieldValue('phone', Taro.getStorageSync('Phone')) // 确保 form 已经初始化
}
useEffect(() => {
reload()
}, [form]) // 确保 form 已经初始化
return (
<>
{/*<Banner/>*/}
{/*<div className={'fixed top-14 left-4 text-white'}>*/}
{/* <image src={{ 'https://oss.wsdns.cn/20250224/5187dab5bc5047bda4521069696fd4dd.png' }} />*/}
{/* <span>返回</span>*/}
{/*</div>*/}
<div className={'p-4'}>
<Form
divider
initialValues={FormData}
labelPosition="left"
onFinish={(values) => submitSucceed(values)}
onFinishFailed={(errors) => submitFailed(errors)}
footer={
<div
style={{
display: 'flex',
justifyContent: 'center',
width: '100%',
}}
>
<Button nativeType="submit" block type="primary">
</Button>
</div>
}
>
<Form.Item
label="类型"
name="type"
style={{marginTop: '10px'}}
required
initialValue={FormData.type}
>
<Radio.Group defaultValue={FormData.type} direction="horizontal">
<Radio value={0}></Radio>
<Radio value={1}></Radio>
</Radio.Group>
</Form.Item>
<Form.Item
label="姓名"
name="name"
required
initialValue={FormData.name}
rules={[{message: '请输入你的真实姓名'}]}
>
<Input placeholder="请输入姓名" type="text"/>
</Form.Item>
<Form.Item
label="性别"
name="sex"
required
initialValue={FormData.sex}
rules={[
{message: '请选择性别'}
]}
>
<Radio.Group defaultValue={FormData.sex} direction="horizontal">
{
sex?.map((item, index) => (
<Radio key={index} value={item.dictDataCode}>
{item.dictDataName}
</Radio>
))
}
</Radio.Group>
</Form.Item>
<Form.Item
label="手机号码"
name="phone"
required
initialValue={phone}
rules={[{max: 11, message: '请输入手机号码'}]}
>
<Input placeholder="手机号码" maxLength={11} type="number"/>
</Form.Item>
<Form.Item
label="所在班级"
name="className"
required
initialValue={FormData.className}
rules={[{message: '请输入您的年级'}]}
>
<Button onClick={() => changeNarBar(true)}>{ className ? gradeName + ' ' + className : '请选择班级' }</Button>
</Form.Item>
<Form.Item
label="居住地"
name="address"
initialValue={FormData.address}
rules={[{message: '请输入现居住地'}]}
>
<Input placeholder="请输入现居住地" type="text"/>
</Form.Item>
<Form.Item
label="工作单位"
name="workUnit"
required
initialValue={FormData.workUnit}
rules={[{message: '请输入您就职工作单位'}]}
>
<Input placeholder="请输入您就职工作单位" type="text"/>
</Form.Item>
<Form.Item
label="职务"
name="position"
required
initialValue={FormData.position}
rules={[{message: '请输入您的职务'}]}
>
<Input placeholder="请输入您的职务" type="text"/>
</Form.Item>
<Form.Item
label="是否能到场"
name="present"
required
rules={[{required: true, message: '请选择是否能到场'}]}
>
<Radio.Group direction="horizontal">
{
present?.map((item, index) => (
<Radio key={index} value={item.dictDataCode}>
{item.dictDataName}
</Radio>
))
}
</Radio.Group>
</Form.Item>
<Form.Item
label="备注信息"
name="comments"
initialValue={FormData.comments}
rules={[{message: '备注信息'}]}
>
<Input placeholder="如找不到或忘记所在班级请备注一下" type="text"/>
</Form.Item>
</Form>
<Picker
visible={isVisibleClass}
options={classList}
onConfirm={(list) => classConfirmPicker(list)}
defaultValue={[2]}
threeDimensional={false}
duration={1000}
onClose={() => setIsVisibleClass(false)}
onChange={changePicker}
/>
<Picker
visible={isVisibleGrade}
options={gradeList}
onConfirm={(list) => gradeNameConfirmPicker(list)}
defaultValue={[2]}
threeDimensional={false}
duration={1000}
onClose={() => setIsVisibleGrade(false)}
onChange={changePicker}
/>
<SideNavBar
title="选择班级"
visible={navBarState.visible}
onClose={() => {
changeNarBar(false)
}}
>
{
classList?.map((item) => {
return (
<SubSideNavBar title={item.name} open={false} value={item.id}>
{
item.children?.map((sub) => {
return (
<>
<SubSideNavBar title={sub.name} open={false} value={item.id + '-' + sub.id}>
{
sub.children?.map((sub2) => {
return (
<SideNavBarItem title={sub2.name} value={item.id + '-' + sub.id + '-' + sub2.id} onClick={() => {
onClassChange(sub.name,sub2.name)
}}/>
)
})
}
</SubSideNavBar>
</>
)
})
}
</SubSideNavBar>
);
})
}
</SideNavBar>
</div>
</>
)
}
export default Bm

View File

@@ -0,0 +1,13 @@
.cert-bg{
width: 720px;
height: 1320px;
margin: 15px auto;
background: url("https://oss.wsdns.cn/20250127/cb1088c3b1354a118477a0b1a3cdac41.png") no-repeat;
background-size: 100%;
}
.cert-content{
width: 480px;
height: 720px;
font-size: 24px;
padding-top: 450px;
}

View File

@@ -0,0 +1,4 @@
export default definePageConfig({
navigationBarTitleText: '捐款凭证',
navigationBarBackgroundColor: '#ffe0e0'
})

View File

@@ -0,0 +1,39 @@
import './pay-cert.scss'
import {useEffect, useState} from "react";
import {useRouter} from '@tarojs/taro'
import dayjs from 'dayjs'
import {getBszxPay} from "@/api/bszx/bszxPay";
import {BszxPay} from "@/api/bszx/bszxPay/model";
function CertQuery() {
const {params} = useRouter();
const [nickName, setNickName] = useState<string>()
const [payLog, setPayLog] = useState<BszxPay>()
const reload = () => {
if (params.id) {
getBszxPay(Number(params.id)).then(res => {
if(res){
setNickName(res.name)
setPayLog(res);
console.log(nickName)
console.log(res,'log.....')
}
})
}
}
useEffect(() => {
reload()
}, []);
return (
<div className={'cert-bg flex justify-center'}>
<div className={'cert-content'}>
<p className={'py-2'}> { payLog?.name || '匿名'} :</p>
!:{payLog?.price || 0}:{payLog?.id}:{payLog?.createTime}!
<p className={'mt-2 text-right'}></p>
<p className={'text-right'}>{dayjs(payLog?.createTime).format('YYYY-MM-DD')}</p>
</div>
</div>
)
}
export default CertQuery;

4
src/bszx/flash.config.ts Normal file
View File

@@ -0,0 +1,4 @@
export default definePageConfig({
navigationBarTitleText: '烟花特效',
navigationBarBackgroundColor: '#000000'
})

58
src/bszx/flash.scss Normal file
View File

@@ -0,0 +1,58 @@
.fireworks-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 40%;
overflow: hidden;
pointer-events: none;
}
.firework {
position: absolute;
left: var(--x);
top: var(--y);
}
.particle {
position: absolute;
width: 6px;
height: 6px;
border-radius: 50%;
background-color: var(--color);
transform-origin: center;
animation: explode 1.5s ease-out forwards;
opacity: 0;
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: 50%;
background-color: var(--color);
animation: particle-glow 1.5s ease-out forwards;
}
}
@keyframes explode {
0% {
transform: rotate(var(--angle)) translateX(0) scale(1);
opacity: 1;
}
100% {
transform: rotate(var(--angle)) translateX(100px) scale(0);
opacity: 0;
}
}
@keyframes particle-glow {
0% {
filter: brightness(1) blur(0);
}
100% {
filter: brightness(2) blur(3px);
}
}

101
src/bszx/flash.tsx Normal file
View File

@@ -0,0 +1,101 @@
import { useEffect, useState } from 'react'
import './flash.scss'
import {CmsAd} from "@/api/cms/cmsAd/model";
import {getCmsAd} from "@/api/cms/cmsAd";
import { Swiper } from '@nutui/nutui-react-taro'
interface Firework {
id: number
x: number
y: number
color: string
}
const MyPage = () => {
const [item, setItem] = useState<CmsAd>()
const reload = () => {
getCmsAd(366).then(data => {
setItem(data)
})
}
const [fireworks, setFireworks] = useState<Firework[]>([])
// 生成随机颜色
const getRandomColor = () => {
const colors = [
'#ff0000', '#00ff00', '#0000ff', '#ffff00',
'#ff00ff', '#00ffff', '#ff9900', '#ff0099'
]
return colors[Math.floor(Math.random() * colors.length)]
}
// 创建新烟花
const createFirework = () => {
const firework: Firework = {
id: Date.now(),
x: Math.random() * 100, // 随机横向位置 (0-100%)
y: Math.random() * 40, // 修改为随机纵向位置 (0-40%)
color: getRandomColor()
}
setFireworks(prev => [...prev, firework])
// 1.5秒后移除烟花
setTimeout(() => {
setFireworks(prev => prev.filter(f => f.id !== firework.id))
}, 1500)
}
useEffect(() => {
reload();
// 初始创建更多烟花
for (let i = 0; i < 6; i++) { // 修改初始烟花数量为6个
createFirework()
}
// 缩短创建间隔,增加密度
const interval = setInterval(() => {
createFirework()
}, 500) // 修改为500ms创建一次
return () => clearInterval(interval)
}, [])
return (
<>
<Swiper defaultValue={0} height={279} indicator style={{ height: '280px' }}>
{item?.imageList?.map((item) => (
<Swiper.Item key={item}>
<img width="100%" height="100%" src={item.url} alt="" style={{ height: '280px' }} />
</Swiper.Item>
))}
</Swiper>
<div className="fireworks-container">
{fireworks.map(firework => (
<div
key={firework.id}
className="firework"
style={{
'--x': `${firework.x}%`,
'--y': `${firework.y}%`,
'--color': firework.color
} as React.CSSProperties}
>
{/* 生成30个粒子 */}
{Array.from({ length: 30 }).map((_, i) => (
<div
key={i}
className="particle"
style={{
'--angle': `${(i * 12)}deg`,
'--delay': `${Math.random() * 0.2}s`
} as React.CSSProperties}
/>
))}
</div>
))}
</div>
</>
)
}
export default MyPage

4
src/bszx/item.config.ts Normal file
View File

@@ -0,0 +1,4 @@
export default definePageConfig({
navigationBarTitleText: '文章详情',
navigationBarBackgroundColor: '#ffe0e0'
})

11
src/bszx/item.scss Normal file
View File

@@ -0,0 +1,11 @@
.content{
padding: 32px;
line-height: 2.4rem;
}
.content p{
text-indent: 2rem;
}
.nr-bg{
background: linear-gradient(to bottom, #ffe0e0, #ffffff);
}

67
src/bszx/item.tsx Normal file
View File

@@ -0,0 +1,67 @@
import {useEffect, useState} from 'react'
import {Tag} from '@nutui/nutui-react-taro'
import {useRouter, useShareAppMessage, useShareTimeline} from '@tarojs/taro'
import {CmsArticle} from "@/api/cms/cmsArticle/model"
import {Eye} from '@nutui/icons-react-taro'
// 显示html富文本
import {View, RichText} from '@tarojs/components'
import './item.scss'
import {wxParse} from "@/utils/common";
import {getCmsArticle} from "@/api/cms/cmsArticle";
function Item() {
const {params} = useRouter();
// 文章详情
const [item, setItem] = useState<CmsArticle>()
// 浏览量
const [views, setViews] = useState<number>()
const reload = () => {
getCmsArticle(Number(params.id)).then(data => {
if(data){
data.content = wxParse(data.content)
setItem(data)
setViews(data.actualViews)
}
})
}
useShareTimeline(() => {
return {
title: item?.title,
path: `/bszx/item?id=${item?.articleId}`
};
});
useShareAppMessage(() => {
return {
title: item?.title,
path: `/bszx/item?id=${item?.articleId}`,
success: function (res) {
console.log('分享成功', res);
},
fail: function (res) {
console.log('分享失败', res);
}
};
});
useEffect(() => {
reload();
}, []);
return (
<div className={'bg-white nr-bg'}>
<div className={'p-3 font-bold text-lg'}>{item?.title}</div>
<div className={'flex justify-between px-3'}>
<Tag type={'success'}>{item?.categoryName}</Tag>
<div className={'flex items-center gap-2 text-sm text-gray-400'}><Eye size={14}/>{views}</div>
</div>
<View className={'content text-gray-700 text-sm'}>
<RichText nodes={item?.content}/>
</View>
</div>
)
}
export default Item

View File

@@ -0,0 +1,4 @@
export default definePageConfig({
navigationBarTitleText: '捐款凭证',
navigationBarBackgroundColor: '#ffe0e0'
})

View File

@@ -0,0 +1,13 @@
.cert-bg{
width: 750px;
height: 100vh;
margin: 0 auto;
background: url("https://oss.wsdns.cn/20250127/cb1088c3b1354a118477a0b1a3cdac41.png") no-repeat;
background-size: 100%;
}
.cert-content{
width: 480px;
height: 720px;
font-size: 24px;
padding-top: 450px;
}

View File

@@ -0,0 +1,84 @@
import './pay-cert.scss'
import {useEffect, useState} from "react";
import {useRouter} from '@tarojs/taro'
import Taro from '@tarojs/taro'
import {getPayCert} from "@/api/bszx/bszxPay";
import {ConfigProvider, Loading} from "@nutui/nutui-react-taro";
function PayCert() {
const {params} = useRouter();
const [poster, setPoster] = useState<string>('')
const [loading, setLoading] = useState<boolean>(true)
const generatePayCert = () => {
if (process.env.NODE_ENV === 'development') {
// setPoster("https://oss.wsdns.cn/20250304/8a9aac182ac94494a806c4bda5766fee.png")
}
// 保存到临时地址
Taro.downloadFile({
url: poster,
success: function (res) {
Taro.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success: function () {
Taro.showToast({
title: '保存成功',
icon: 'success',
duration: 2000
});
},
fail: function (err) {
if (err.errMsg === 'saveImageToPhotosAlbum:fail auth deny') {
}
}
})
}
})
}
const reload = () => {
if (params.id) {
getPayCert(Number(params.id)).then(img => {
console.log(img, 'img....')
setPoster(`${img}`)
setTimeout(() => {
setLoading(false)
},1000)
if (process.env.NODE_ENV === 'development') {
// setPoster("https://oss.wsdns.cn/20250304/8a9aac182ac94494a806c4bda5766fee.png")
}
})
}
}
useEffect(() => {
reload()
}, []);
return (
<>
{loading ?
<div className={'bg-white w-full flex justify-center items-center'} style={{height: '100vh'}}>
<ConfigProvider theme={{nutuiLoadingIconSize: '28px'}}>
<Loading type="spinner"></Loading>
</ConfigProvider>
</div> :
<div className={'flex justify-center'}>
<img src={poster} width={'750rpx'} height={'1060rpx'}/>
<div className={'fixed bottom-10'}>
{/*<Button />*/}
<button
className={'item px-20 py-3 bg-yellow-300 text-black flex items-center gap-2 text-nowrap whitespace-nowrap'}
onClick={() => {
generatePayCert()
}}>
</button>
</div>
</div>
}
</>
)
}
export default PayCert;

View File

@@ -0,0 +1,4 @@
export default definePageConfig({
navigationBarTitleText: '捐款记录',
navigationBarBackgroundColor: '#ffe0e0'
})

View File

@@ -0,0 +1,104 @@
import {Avatar, Button, Cell, Space} from '@nutui/nutui-react-taro'
import {useEffect, useState, CSSProperties} from "react";
import {BszxPay} from "@/api/bszx/bszxPay/model";
import {myPageBszxPay} from "@/api/bszx/bszxPay";
import {InfiniteLoading} from '@nutui/nutui-react-taro'
import dayjs from "dayjs";
import navTo from "@/utils/common";
const InfiniteUlStyle: CSSProperties = {
height: '80vh',
width: '100%',
padding: '0',
overflowY: 'auto',
overflowX: 'hidden',
}
function PayRecord() {
const [list, setList] = useState<BszxPay[]>([])
const [page, setPage] = useState(1)
const [hasMore, setHasMore] = useState(true)
const reload = async () => {
myPageBszxPay({page}).then(res => {
if (res?.list && res?.list.length > 0) {
const newList = list?.concat(res.list)
setList(newList);
setHasMore(true)
} else {
setHasMore(false)
}
})
}
const reloadMore = async () => {
setPage(page + 1)
reload();
}
useEffect(() => {
setPage(2)
reload()
}, [])
return (
<div className={'px-2'}>
<Cell>
<ul style={InfiniteUlStyle} id="scroll">
<InfiniteLoading
target="scroll"
hasMore={hasMore}
onLoadMore={reloadMore}
onScroll={() => {
console.log('onScroll')
}}
onScrollToUpper={() => {
console.log('onScrollToUpper')
}}
loadingText={
<>
</>
}
loadMoreText={
<>
</>
}
>
{list?.map(item => {
return (
<Cell style={{padding: '0'}}>
<div className={'flex w-full justify-between items-center'}>
<div className={'flex'}>
<Space>
<Avatar
src={item.avatar}
/>
<div className={'flex flex-col'}>
<div className={'real-name text-lg'}>
{item.name || '匿名'}
</div>
<div style={{maxWidth: '200px'}} className={'text-gray-400'}>{item.formName}{dayjs(item.createTime).format('YYYY-MM-DD HH:mm')}</div>
<div className={'text-green-600 my-1'}>{item.comments}</div>
</div>
</Space>
</div>
<div className={'price'}>
<span className={' text-red-500 text-xl text-center font-bold'}>{item.price}</span>
<Button className={'whitespace-nowrap'} onClick={() => {
navTo('/bszx/pay-cert/pay-cert?id=' + item.id, true)
}}>
</Button>
</div>
</div>
</Cell>
)
})}
</InfiniteLoading>
</ul>
</Cell>
</div>
)
}
export default PayRecord

View File

@@ -0,0 +1,4 @@
export default definePageConfig({
navigationBarTitleText: '善款明细',
navigationBarBackgroundColor: '#ffe0e0'
})

View File

@@ -0,0 +1,118 @@
import {Avatar, Cell, Space} from '@nutui/nutui-react-taro'
import {useEffect, useState, CSSProperties} from "react";
import {BszxPay} from "@/api/bszx/bszxPay/model";
import {getCount, pageBszxPay} from "@/api/bszx/bszxPay";
import {InfiniteLoading} from '@nutui/nutui-react-taro'
import dayjs from "dayjs";
const InfiniteUlStyle: CSSProperties = {
height: '70vh',
width: '100%',
padding: '0',
overflowY: 'auto',
overflowX: 'hidden',
}
function PayRecord() {
const [list, setList] = useState<BszxPay[]>([])
const [page, setPage] = useState(1)
const [hasMore, setHasMore] = useState(true)
const [totalMoney, setTotalMoney] = useState()
const [numbers, setNumbers] = useState()
const reload = async () => {
pageBszxPay({page}).then(res => {
let newList: BszxPay[] | undefined = []
if (res?.list && res?.list.length > 0) {
newList = list?.concat(res.list)
setHasMore(true)
} else {
newList = res?.list
setHasMore(false)
}
setList(newList || []);
})
getCount().then(res => {
setNumbers(res.numbers);
setTotalMoney(res.totalMoney);
})
}
const reloadMore = async () => {
setPage(page + 1)
reload();
}
useEffect(() => {
setPage(2)
reload()
}, [])
return (
<div className={'px-2'}>
<Cell>
<div className={'flex w-full text-center justify-around'}>
<div className={'item py-1'}>
<span className={'text-gray-400'}>()</span>
<span className={'text-xl py-1 font-bold'}>{totalMoney}</span>
</div>
<div className={'item py-1'}>
<span className={'text-gray-400'}></span>
<span className={'text-xl py-1 font-bold'}>{numbers}</span>
</div>
</div>
</Cell>
<Cell>
<ul style={InfiniteUlStyle} id="scroll">
<InfiniteLoading
target="scroll"
hasMore={hasMore}
onLoadMore={reloadMore}
onScroll={() => {
console.log('onScroll')
}}
onScrollToUpper={() => {
console.log('onScrollToUpper')
}}
loadingText={
<>
</>
}
loadMoreText={
<>
</>
}
>
{list?.map(item => {
return (
<Cell style={{padding: '0'}}>
<div className={'flex w-full justify-between items-center'}>
<div className={'flex'}>
<Space>
<Avatar
src={item.avatar}
/>
<div className={'flex flex-col'}>
<div className={'real-name text-lg'}>
{item.name || '匿名'}
</div>
<div style={{maxWidth: '240px'}} className={'text-gray-400'}>{item.formName}{dayjs(item.createTime).format('YYYY-MM-DD HH:mm')}</div>
<div className={'text-green-600 my-1'}>{item.comments}</div>
</div>
</Space>
</div>
<div className={'price text-red-500 text-xl font-bold'}>
{item.price}
</div>
</div>
</Cell>
)
})}
</InfiniteLoading>
</ul>
</Cell>
</div>
)
}
export default PayRecord

View File

@@ -0,0 +1,6 @@
export default definePageConfig({
navigationBarTitleText: '捐款详情',
enableShareTimeline: true,
enableShareAppMessage: true,
navigationBarBackgroundColor: '#ffe0e0'
})

18
src/bszx/pay/detail.scss Normal file
View File

@@ -0,0 +1,18 @@
.content {
padding: 32px;
line-height: 2.4rem;
}
.content-bg{
background-image: url("https://oss.wsdns.cn/20250224/7230c35b276f420a8527d08358759d8c.jpg");
background-repeat: no-repeat;
background-size: 100%;
background-position: bottom;
}
page{
background-color: #fff2ee !important;
}
.nr-bg{
background: linear-gradient(to bottom, #ffe0e0, #ffffff);
}

173
src/bszx/pay/detail.tsx Normal file
View File

@@ -0,0 +1,173 @@
import {useEffect, useState} from 'react'
import {Image, Tag} from '@nutui/nutui-react-taro'
import {useRouter} from '@tarojs/taro'
import {Divider} from '@nutui/nutui-react-taro'
import dayjs from 'dayjs'
import Taro, {
useLoad,
useShareAppMessage,
useShareTimeline,
} from '@tarojs/taro';
import {CmsArticle} from "@/api/cms/cmsArticle/model"
import {Eye, Clock, PickedUp, Purse, Coupon} from '@nutui/icons-react-taro'
import AddCartBar from "@/components/AddCartBar";
// 显示html富文本
import {View, RichText} from '@tarojs/components'
import './detail.scss'
import Line from "@/components/Gap";
import {wxParse} from "@/utils/common";
import {getCmsArticle} from "@/api/cms/cmsArticle";
import {pageBszxPay} from "@/api/bszx/bszxPay";
import {getUserInfo, getWxOpenId} from "@/api/layout";
function Detail() {
const {params} = useRouter();
// 文章详情
const [item, setItem] = useState<CmsArticle>()
// 浏览量
const [views, setViews] = useState<number>()
// 报名人数
const [bmUsers, setBmUsers] = useState<number>()
// 是否登录
const [isLogin, setIsLogin] = useState<boolean>(false)
const reload = () => {
getCmsArticle(Number(params.id)).then(data => {
if (data) {
data.content = wxParse(data.content)
setItem(data)
setViews(data.actualViews)
}
})
pageBszxPay({
formId: Number(params.id),
}).then(res => {
if (res) {
setBmUsers(res.count)
}
})
// 登录
Taro.getUserInfo({
success: (res) => {
console.log(res.userInfo.avatarUrl)
getUserInfo().then((data) => {
if (data) {
setIsLogin(true);
console.log(isLogin,'isLogin')
Taro.setStorageSync('UserId', data.userId)
// 获取openId
if (!data.openid) {
Taro.login({
success: (res) => {
getWxOpenId({code: res.code}).then(() => {
})
}
})
}
}
}).catch(() => {
console.log('未登录')
});
}
});
}
useLoad(async () => {
//获取进入页面参数 scene为1154===朋友圈进入
const data = Taro.getLaunchOptionsSync();
console.log(data, 'data')
//开启分享
Taro.showShareMenu({
withShareTicket: true,
});
})
useShareTimeline(() => {
return {
title: item?.title,
path: `/bszx/pay/detail?id=${item?.articleId}`,
imageUrl: item?.image,
};
});
useShareAppMessage(() => {
return {
title: item?.title,
path: `/bszx/pay/detail?id=${item?.articleId}`,
imageUrl: item?.image,
success: function (res) {
console.log('分享成功', res);
},
fail: function (res) {
console.log('分享失败', res);
}
};
});
useEffect(() => {
reload();
}, []);
return (
<>
<Image src={item?.image} height={375}/>
{/*linear-gradient(to bottom, #ffe0e0, #ffb6c1)*/}
<div className={'pb-5 nr-bg'}>
<div className={'p-3 font-bold text-lg'}>{item?.title}</div>
<div className={'flex justify-between px-3'}>
<Tag type={'success'}>{item?.categoryName}</Tag>
<div className={'flex items-center gap-2 text-sm text-gray-500'}><Eye size={14}/>{views}</div>
</div>
<Divider/>
{
item?.model == 'bm' ? '' :
<div>
{
!item?.endTime ? '' :
<>
<div className={'flex px-3 items-center gap-2'}>
<Clock size={14}/>
<div
className={'text-sm font-bold'}>{dayjs(item?.startTime).format('YYYY-MM-DD')} {dayjs(item?.endTime).format('YYYY-MM-DD')}</div>
</div>
<Divider/>
</>
}
{
!item?.price ? '' :
<>
<div className={'flex px-3 items-center gap-2'}>
<Purse size={14}/>
<div
className={'text-sm font-bold text-orange-500'}>{item?.price == 0 ? '线下收费' : '¥' + item?.price}
</div>
</div>
<Divider/>
</>
}
<div className={'flex px-3 items-center gap-2'}>
<PickedUp size={14}/>
<div
className={'text-sm font-bold'}> {bmUsers}
</div>
</div>
</div>
}
<Divider/>
<div className={'flex px-3 items-center gap-2'}>
<Coupon size={14}/>
<div
className={'text-sm font-bold'}>
</div>
</div>
<View className={'content text-gray-700 text-sm relative content-bg'}>
<RichText nodes={item?.content}/>
</View>
<Line height={44}/>
</div>
<AddCartBar/>
</>
)
}
export default Detail

View File

@@ -0,0 +1,4 @@
export default definePageConfig({
navigationBarTitleText: '我要捐款',
navigationBarBackgroundColor: '#ffe0e0'
})

212
src/bszx/pay/pay.tsx Normal file
View File

@@ -0,0 +1,212 @@
import {useEffect, useState} from 'react'
import Taro, {getCurrentInstance} from '@tarojs/taro'
import {
Form,
Button,
Input,
RadioGroup,
Radio
} from '@nutui/nutui-react-taro'
import {navigateTo} from '@tarojs/taro'
import {addBszxPay,myPageBszxPay} from "@/api/bszx/bszxPay";
import {BszxPay} from "@/api/bszx/bszxPay/model";
import {TenantId} from "@/utils/config";
import {myPageBszxBm} from "@/api/bszx/bszxBm";
const Bm = () => {
const { router } = getCurrentInstance();
const [formId, setFormId] = useState<number>()
const [gradeName, setGradeName] = useState('')
const [className, setClassName] = useState('')
const [FormData, setFormData] = useState<BszxPay>(
{
name: undefined,
sex: undefined,
phone: undefined,
className: undefined,
gradeName: undefined,
address: undefined,
workUnit: undefined,
position: undefined,
present: undefined,
formId: undefined
}
)
// 提交表单
const submitSucceed = (values: any) => {
// if(values.price < 10){
// Taro.showToast({
// title: '捐款金额不能低于10元',
// icon: 'none'
// })
// return false;
// }
Taro.request({
url: 'https://cms-api.websoft.top/api/shop/shop-order',
method: 'POST',
header: {
'content-type': 'application/json',
'Authorization': Taro.getStorageSync('access_token'),
TenantId
},
data: {
totalPrice: values.price,
payPrice: values.price,
tenantId: TenantId,
payType: 1,
comments: values.comments,
name: values.name || FormData.name,
sex: values.sex || FormData.sex,
phone: values.phone || FormData.phone,
className: className || FormData.className,
gradeName: gradeName || FormData.gradeName,
address: values.address || FormData.address,
workUnit: values.workUnit || FormData.workUnit,
position: values.position || FormData.position,
formId
},
success: function (res) {
const data = res.data.data
if(data){
Taro.requestPayment({
timeStamp: data.timeStamp,
nonceStr: data.nonceStr,
package: data.package,
signType: data.signType,
paySign: data.paySign,
success: function (res) {
if(res.errMsg == "requestPayment:ok"){
console.log('捐款成功')
addBszxPay({
formId,
orderNo: data.orderNo,
name: values.name || FormData.name,
sex: values.sex || FormData.sex,
phone: values.phone || FormData.phone,
className: className || FormData.className,
gradeName: gradeName || FormData.gradeName,
address: values.address || FormData.address,
workUnit: values.workUnit || FormData.workUnit,
position: values.position || FormData.position,
present: values.present === '能',
price: values.price || FormData.price,
comments: values.comments,
}).then((data) => {
console.log(data,'payRes')
console.log('跳转证书页面')
setTimeout(() => {
navigateTo({url: `/bszx/pay-cert/pay-cert?id=${data?.id}&orderNo=${data?.orderNo}`});
}, 1000)
}).catch(err => {
console.log(err,'errr....')
})
}
},
fail: function (res) {
console.log(res)
}
})
}
}
})
}
const submitFailed = (error: any) => {
console.log(error,'err...')
// Taro.showToast({ title: error[0].message, icon: 'error' })
}
const reload = () => {
myPageBszxBm().then(res => {
if(res.count == 0){
Taro.showToast({
title: '请先完善个人资料',
icon: 'error'
})
setTimeout(() => {
Taro.navigateTo({
url: '/user/profile/profile'
})
}, 1000)
}
})
myPageBszxPay({limit: 1}).then(res => {
const item = res.list[0];
if(item){
setFormData(item)
if(item.gradeName){
setGradeName(item.gradeName)
}
if(item.className){
setClassName(item.className)
}
}
})
}
useEffect(() => {
const id = router?.params.id as number | undefined;
setFormId(id)
reload()
}, [])
return (
<div className={'p-4'}>
<Form
divider
initialValues={FormData}
labelPosition="left"
onFinish={(values) => submitSucceed(values)}
onFinishFailed={(errors) => submitFailed(errors)}
footer={
<div
style={{
display: 'flex',
justifyContent: 'center',
width: '100%',
}}
>
<Button nativeType="submit" block type="primary">
</Button>
</div>
}
>
<Form.Item
name="price"
initialValue={FormData.price}
rules={[{message: '请选您要择捐款的金额'}]}
>
<RadioGroup value={FormData.price} direction="horizontal">
<Radio shape="button" value="100"><span className={'font-bold text-sm'}>100</span></Radio>
<Radio shape="button" value="200"><span className={'font-bold text-sm'}>200</span></Radio>
<Radio shape="button" value="300"><span className={'font-bold text-sm'}>300</span></Radio>
<Radio shape="button" value="500"><span className={'font-bold text-sm'}>500</span></Radio>
<Radio shape="button" value="1000"><span className={'font-bold text-sm'}>1000</span></Radio>
<Radio shape="button" value="2000"><span className={'font-bold text-sm'}>2000</span></Radio>
<Radio shape="button" value="3000"><span className={'font-bold text-sm'}>3000</span></Radio>
<Radio shape="button" value="5000"><span className={'font-bold text-sm'}>5000</span></Radio>
<Radio shape="button" value="10000"><span className={'font-bold text-sm'}>10000</span></Radio>
</RadioGroup>
</Form.Item>
<Form.Item
label="捐款金额"
name="price"
required
rules={[{ required: true,message: '请输入捐款数额' }]}
>
<Input placeholder="请输入捐款数额" />
</Form.Item>
<Form.Item
label="您的心愿"
name="comments"
rules={[{ message: '请填写您的心意' }]}
>
<Input placeholder="请填写您的心意" />
</Form.Item>
</Form>
</div>
)
}
export default Bm

4
src/bszx/pdf.config.ts Normal file
View File

@@ -0,0 +1,4 @@
export default definePageConfig({
navigationBarTitleText: '文章详情',
navigationBarBackgroundColor: '#ffe0e0'
})

8
src/bszx/pdf.scss Normal file
View File

@@ -0,0 +1,8 @@
.content{
padding: 32px;
line-height: 2.4rem;
}
.nr-bg{
background: linear-gradient(to bottom, #ffe0e0, #ffffff);
}

45
src/bszx/pdf.tsx Normal file
View File

@@ -0,0 +1,45 @@
import {useEffect, useState} from 'react'
import {useRouter} from '@tarojs/taro'
import {CmsArticle} from "@/api/cms/cmsArticle/model"
import {Eye} from '@nutui/icons-react-taro'
// 显示html富文本
import {View, RichText} from '@tarojs/components'
import './pdf.scss'
import {wxParse} from "@/utils/common";
import {getCmsArticle} from "@/api/cms/cmsArticle";
function Item() {
const {params} = useRouter();
// 文章详情
const [item, setItem] = useState<CmsArticle>()
// 浏览量
const [views, setViews] = useState<number>()
const reload = () => {
getCmsArticle(Number(params.id)).then(data => {
if(data){
data.content = wxParse(data.content)
setItem(data)
setViews(data.actualViews)
}
})
}
useEffect(() => {
reload();
}, []);
return (
<div className={'bg-white nr-bg'}>
<div className={'p-3 font-bold text-lg'}>{item?.title}</div>
<div className={'flex justify-between px-3'}>
<div className={'flex items-center gap-2 text-sm text-gray-400'}><Eye size={14}/>{views}</div>
</div>
<View className={'content text-gray-700 text-sm'}>
<RichText nodes={item?.content}/>
</View>
</div>
)
}
export default Item