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