forked from imbytecat/fullstack-starter
feat: 添加许可证停用功能及确认弹窗
- 添加反激活许可证的确认弹窗功能,包含二次确认提示和操作反馈。 - 添加停用许可证的接口合约定义。 - 添加许可证停用功能,确保激活记录存在后将其许可证信息和激活时间清空。
This commit is contained in:
@@ -14,6 +14,7 @@ export const Route = createFileRoute('/license')({
|
||||
function License() {
|
||||
const [licenseInput, setLicenseInput] = useState('')
|
||||
const [copySuccess, setCopySuccess] = useState(false)
|
||||
const [showDeactivateConfirm, setShowDeactivateConfirm] = useState(false)
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
// 获取激活状态
|
||||
@@ -32,11 +33,26 @@ function License() {
|
||||
},
|
||||
})
|
||||
|
||||
// 反激活 mutation
|
||||
const deactivateMutation = useMutation({
|
||||
...orpc.license.deactivate.mutationOptions(),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: orpc.license.getActivation.key(),
|
||||
})
|
||||
setShowDeactivateConfirm(false)
|
||||
},
|
||||
})
|
||||
|
||||
const handleActivate = () => {
|
||||
if (!licenseInput.trim()) return
|
||||
activateMutation.mutate({ license: licenseInput.trim() })
|
||||
}
|
||||
|
||||
const handleDeactivate = () => {
|
||||
deactivateMutation.mutate()
|
||||
}
|
||||
|
||||
const handleCopyFingerprint = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(data.fingerprint)
|
||||
@@ -352,6 +368,62 @@ function License() {
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{!showDeactivateConfirm ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowDeactivateConfirm(true)}
|
||||
className="mt-4 px-4 py-2 bg-red-500 text-white rounded-lg text-sm font-medium hover:bg-red-600 transition-colors shadow-sm"
|
||||
>
|
||||
反激活
|
||||
</button>
|
||||
) : (
|
||||
<div className="mt-4 p-4 bg-red-50 rounded-lg border border-red-200">
|
||||
<p className="text-red-700 text-sm mb-3 flex items-center gap-2">
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z" />
|
||||
<path d="M12 9v4" />
|
||||
<path d="M12 17h.01" />
|
||||
</svg>
|
||||
确定要反激活吗?此操作会清除当前 License。
|
||||
</p>
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleDeactivate}
|
||||
disabled={deactivateMutation.isPending}
|
||||
className="px-4 py-2 bg-red-600 text-white rounded-md text-sm font-medium hover:bg-red-700 disabled:bg-gray-300 transition-colors"
|
||||
>
|
||||
{deactivateMutation.isPending
|
||||
? '反激活中...'
|
||||
: '确认反激活'}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowDeactivateConfirm(false)}
|
||||
className="px-4 py-2 bg-gray-200 text-gray-700 rounded-md text-sm font-medium hover:bg-gray-300 transition-colors"
|
||||
>
|
||||
取消
|
||||
</button>
|
||||
</div>
|
||||
{deactivateMutation.isError && (
|
||||
<p className="text-red-500 mt-2 text-xs">
|
||||
反激活失败,请重试
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-start gap-3">
|
||||
|
||||
@@ -12,3 +12,7 @@ export const getActivation = oc.input(z.void()).output(
|
||||
export const activate = oc
|
||||
.input(z.object({ license: z.string().min(1) }))
|
||||
.output(z.object({ success: z.boolean() }))
|
||||
|
||||
export const deactivate = oc
|
||||
.input(z.void())
|
||||
.output(z.object({ success: z.boolean() }))
|
||||
|
||||
@@ -43,3 +43,25 @@ export const activate = os.license.activate
|
||||
|
||||
return { success: true }
|
||||
})
|
||||
|
||||
export const deactivate = os.license.deactivate
|
||||
.use(dbProvider)
|
||||
.handler(async ({ context }) => {
|
||||
await ensureLicenseActivationInitialized()
|
||||
|
||||
const record = await context.db.query.licenseActivationTable.findFirst()
|
||||
|
||||
if (!record) {
|
||||
throw new Error('License activation record not found')
|
||||
}
|
||||
|
||||
await context.db
|
||||
.update(licenseActivationTable)
|
||||
.set({
|
||||
license: null,
|
||||
licenseActivatedAt: null,
|
||||
})
|
||||
.where(eq(licenseActivationTable.id, record.id))
|
||||
|
||||
return { success: true }
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user