个人中心页面按钮跳转与样式修复;实名认证页面界面与文字调整;提取 IP 页面浮动组件复制问题修复

This commit is contained in:
2025-04-30 10:42:47 +08:00
parent 09d6255bd5
commit 5ef673bacb
5 changed files with 63 additions and 41 deletions

View File

@@ -1,29 +1,22 @@
## TODO ## TODO
验证码读秒用 store 保存到本地,(全局共享读秒时间) - 验证码读秒用 store 保存到本地,(全局共享读秒时间)
- 网页标题根据实际页面变化
- 检查时间范围选择,限定到一定范围内
- 将翻页操作反映在路由历史中,可以通过后退返回到上一个翻页状态
- 使用 pure js 的包代替 canvas加快编译速度
- 提取后刷新提取页套餐可用余量
- 白名单复用表格组件
- 首次登录弹窗:需要设置初始密码,展示 实名->购买->提取 的流程介绍
- 提取页表单性能优化,树组件性能优化
- 检查页面,为后端请求标记 wait 实现防抖机制
- 页面切换动效
- 后台页面:
- 总览
- 提取记录
- 使用记录
网页标题根据实际页面变化 ### 长期
检查时间范围选择,限定到一定范围内
将翻页操作反映在路由历史中,可以通过后退返回到上一个翻页状态
使用 pure js 的包代替 canvas加快编译速度
提取后刷新提取页套餐可用余量
白名单复用表格组件
首次登录弹窗:需要设置初始密码,展示 实名->购买->提取 的流程介绍
提取页表单性能优化,树组件性能优化
后台页面:
- [ ] 总览
- [ ] 个人中心
- [ ] IP 管理
- [ ] 提取记录
- [ ] 使用记录
检查扩大服务端组件边界 检查扩大服务端组件边界

View File

@@ -19,7 +19,7 @@ import step1 from './_assets/step1.webp'
import step2 from './_assets/step2.webp' import step2 from './_assets/step2.webp'
import step3 from './_assets/step3.webp' import step3 from './_assets/step3.webp'
import {Card, CardContent, CardDescription, CardHeader, CardTitle} from '@/components/ui/card' import {Card, CardContent, CardDescription, CardHeader, CardTitle} from '@/components/ui/card'
import {WorkflowIcon} from 'lucide-react' import {CheckCircle, CheckCircleIcon, WorkflowIcon} from 'lucide-react'
export type IdentifyPageProps = {} export type IdentifyPageProps = {}
@@ -120,12 +120,15 @@ export default function IdentifyPage(props: IdentifyPageProps) {
<Image src={personal} alt={`个人认证`}/> <Image src={personal} alt={`个人认证`}/>
<div> <div>
<h3 className={`text-center text-lg font-bold`}></h3> <h3 className={`text-center text-lg font-bold`}></h3>
<p className={`text-sm text-gray-600`}></p> <p className={`text-sm text-gray-600`}>
</p>
</div> </div>
{profile?.id_token ? ( {profile?.id_token ? (
<div className={`flex flex-col gap-4`}> <p className={`flex gap-2 items-center`}>
<p className={`text-sm text-gray-600`}></p> <CheckCircleIcon className={`text-done`}/>
</div> <span></span>
</p>
) : ( ) : (
<Dialog open={openDialog} onOpenChange={setOpenDialog}> <Dialog open={openDialog} onOpenChange={setOpenDialog}>
<DialogTrigger asChild> <DialogTrigger asChild>

View File

@@ -26,11 +26,13 @@ import {
} from '@/components/ui/dialog' } from '@/components/ui/dialog'
import {sendSMS} from '@/actions/verify' import {sendSMS} from '@/actions/verify'
import RechargeModal from '@/components/composites/recharge' import RechargeModal from '@/components/composites/recharge'
import {useRouter} from 'next/navigation'
export type ProfilePageProps = {} export type ProfilePageProps = {}
export default function ProfilePage(props: ProfilePageProps) { export default function ProfilePage(props: ProfilePageProps) {
const router = useRouter()
const profile = useProfileStore(store => store.profile) const profile = useProfileStore(store => store.profile)
// ====================== // ======================
@@ -67,7 +69,9 @@ export default function ProfilePage(props: ProfilePageProps) {
</CardHeader> </CardHeader>
<CardContent className={`flex-auto flex justify-between items-center px-8`}> <CardContent className={`flex-auto flex justify-between items-center px-8`}>
<p className={`text-xl`}>{profile?.balance}</p> <p className={`text-xl`}>{profile?.balance}</p>
<RechargeModal/> <RechargeModal classNames={{
trigger: `h-10 px-6`,
}}/>
</CardContent> </CardContent>
</Card> </Card>
@@ -79,7 +83,7 @@ export default function ProfilePage(props: ProfilePageProps) {
{!profile?.id_token {!profile?.id_token
? <> ? <>
<p className={`text-sm`}>使</p> <p className={`text-sm`}>使</p>
<Button theme={`outline`} className={`mx-16 w-24`}></Button> <Button theme={`outline`} className={`mx-16 w-24`} onClick={() => router.push('/admin/identify')}></Button>
</> </>
: <> : <>
<p className={`flex flex-col gap-1`}> <p className={`flex flex-col gap-1`}>
@@ -423,7 +427,7 @@ function PasswordForm(props: {
<Button theme={`outline`} type={`button`} className={`w-36`} onClick={() => sendVerifier()}> <Button theme={`outline`} type={`button`} className={`w-36`} onClick={() => sendVerifier()}>
{captchaWait > 0 {captchaWait > 0
? `重新发送(${captchaWait})` ? `重新发送(${captchaWait})`
: `获取验证码` : `获取短信令牌`
} }
</Button> </Button>
</div> </div>

View File

@@ -94,20 +94,37 @@ export default function Extract(props: ExtractProps) {
const type = useRef<'copy' | 'open'>('open') const type = useRef<'copy' | 'open'>('open')
const onSubmit = async (values: z.infer<typeof schema>) => { const onSubmit = async (values: z.infer<typeof schema>) => {
console.log('222', type.current)
switch (type.current) { switch (type.current) {
case 'copy': case 'copy':
console.log('copy')
const url = new URL(window.location.href).origin const url = new URL(window.location.href).origin
await navigator.clipboard.writeText(`${url}${params}`) const text = `${url}${params}`
// 使用 clipboard API 复制链接
let copied = false
try {
await navigator.clipboard.writeText(text)
copied = true
}
catch (e) {
console.log('剪贴板 API 调用失败,尝试备选方案')
}
// 使用 document.execCommand 作为备选方案
if (!copied) {
const textarea = document.createElement('textarea')
textarea.value = text
document.body.appendChild(textarea)
textarea.select()
document.execCommand('copy')
document.body.removeChild(textarea)
}
toast.success('链接已复制到剪贴板') toast.success('链接已复制到剪贴板')
break break
case 'open': case 'open':
console.log('open')
window.open(params, '_blank') window.open(params, '_blank')
break break
} }
console.log('333')
} }
const getResources = async () => { const getResources = async () => {
@@ -451,11 +468,11 @@ export default function Extract(props: ExtractProps) {
</div> </div>
<div className={merge( <div className={merge(
`flex flex-col gap-4 sticky bottom-0 bg-white py-4`, `flex flex-col gap-4 sticky bottom-0 bg-muted p-4`,
`border-t`, `rounded-lg`,
)}> )}>
{/* 展示链接地址 */} {/* 展示链接地址 */}
<div className={`bg-card text-card-foreground p-4 rounded-md break-all`}> <div className={`bg-neutral-900 text-white p-4 rounded-md break-all`}>
{params} {params}
</div> </div>

View File

@@ -22,6 +22,7 @@ import {Loader} from 'lucide-react'
import {RechargeByAlipay, RechargeByAlipayConfirm, RechargeByWechat, RechargeByWechatConfirm} from '@/actions/user' import {RechargeByAlipay, RechargeByAlipayConfirm, RechargeByWechat, RechargeByWechatConfirm} from '@/actions/user'
import * as qrcode from 'qrcode' import * as qrcode from 'qrcode'
import {useProfileStore} from '@/components/providers/StoreProvider' import {useProfileStore} from '@/components/providers/StoreProvider'
import {merge} from '@/lib/utils'
const schema = zod.object({ const schema = zod.object({
method: zod.enum(['alipay', 'wechat']), method: zod.enum(['alipay', 'wechat']),
@@ -30,7 +31,11 @@ const schema = zod.object({
type Schema = zod.infer<typeof schema> type Schema = zod.infer<typeof schema>
export type RechargeModelProps = {} export type RechargeModelProps = {
classNames?: {
trigger?: string
}
}
export default function RechargeModal(props: RechargeModelProps) { export default function RechargeModal(props: RechargeModelProps) {
@@ -144,7 +149,7 @@ export default function RechargeModal(props: RechargeModelProps) {
return ( return (
<Dialog open={open} onOpenChange={setOpen}> <Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild> <DialogTrigger asChild>
<Button theme={`accent`} type={`button`} className={`px-4 h-8`}></Button> <Button theme={`accent`} type={`button`} className={merge(`px-4 h-8`, props.classNames?.trigger)}></Button>
</DialogTrigger> </DialogTrigger>
<DialogContent> <DialogContent>