实现权限列表与权限渲染组件

This commit is contained in:
2026-03-31 10:56:01 +08:00
parent 2c7970796f
commit 12b60e74df
7 changed files with 94 additions and 3 deletions

View File

@@ -18,6 +18,7 @@
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"date-fns": "^4.1.0",
"jotai": "^2.19.0",
"lucide-react": "^0.562.0",
"next": "^16.0.10",
"next-themes": "^0.4.6",
@@ -359,6 +360,8 @@
"jiti": ["jiti@2.6.1", "https://registry.npmmirror.com/jiti/-/jiti-2.6.1.tgz", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
"jotai": ["jotai@2.19.0", "https://registry.npmmirror.com/jotai/-/jotai-2.19.0.tgz", { "peerDependencies": { "@babel/core": ">=7.0.0", "@babel/template": ">=7.0.0", "@types/react": ">=17.0.0", "react": ">=17.0.0" }, "optionalPeers": ["@babel/core", "@babel/template", "@types/react", "react"] }, "sha512-r2wwxEXP1F2JteDLZEOPoIpAHhV89paKsN5GWVYndPNMMP/uVZDcC+fNj0A8NjKgaPWzdyO8Vp8YcYKe0uCEqQ=="],
"lightningcss": ["lightningcss@1.30.2", "https://registry.npmmirror.com/lightningcss/-/lightningcss-1.30.2.tgz", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.30.2", "lightningcss-darwin-arm64": "1.30.2", "lightningcss-darwin-x64": "1.30.2", "lightningcss-freebsd-x64": "1.30.2", "lightningcss-linux-arm-gnueabihf": "1.30.2", "lightningcss-linux-arm64-gnu": "1.30.2", "lightningcss-linux-arm64-musl": "1.30.2", "lightningcss-linux-x64-gnu": "1.30.2", "lightningcss-linux-x64-musl": "1.30.2", "lightningcss-win32-arm64-msvc": "1.30.2", "lightningcss-win32-x64-msvc": "1.30.2" } }, "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ=="],
"lightningcss-android-arm64": ["lightningcss-android-arm64@1.30.2", "https://registry.npmmirror.com/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz", { "os": "android", "cpu": "arm64" }, "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A=="],

View File

@@ -21,6 +21,7 @@
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"date-fns": "^4.1.0",
"jotai": "^2.19.0",
"lucide-react": "^0.562.0",
"next": "^16.0.10",
"next-themes": "^0.4.6",

View File

@@ -16,7 +16,7 @@ export async function login(params: {
username: string
password: string
remember: boolean
}): Promise<ApiResponse> {
}): Promise<ApiResponse<string[]>> {
const resp = await callByDevice<TokenResp>("/api/auth/token", {
grant_type: "password",
login_type: "password",
@@ -43,7 +43,7 @@ export async function login(params: {
return {
success: true,
data: undefined,
data: data.scope?.split(" ") || [],
}
}

View File

@@ -1,5 +1,6 @@
"use client"
import { zodResolver } from "@hookform/resolvers/zod"
import { useSetAtom } from "jotai"
import { useRouter } from "next/navigation"
import { Controller, useForm } from "react-hook-form"
import { toast } from "sonner"
@@ -16,6 +17,7 @@ import {
FieldLegend,
} from "@/components/ui/field"
import { Input } from "@/components/ui/input"
import { scopesAtom } from "@/lib/stores/scopes"
const schema = z.object({
username: z.string().min(4).max(50),
@@ -36,7 +38,7 @@ export default function LoginPage() {
})
const router = useRouter()
const setScopes = useSetAtom(scopesAtom)
const onSubmit = async (data: Schema) => {
try {
const resp = await login(data)
@@ -45,6 +47,8 @@ export default function LoginPage() {
}
// 登录成功后跳转到首页
console.log("用户权限列表", resp.data)
setScopes(resp.data)
router.push("/")
} catch (e) {
toast.error("登录失败", {

View File

@@ -0,0 +1,13 @@
import { useAtomValue } from "jotai"
import type { ReactNode } from "react"
import { scopesAtom } from "@/lib/stores/scopes"
export function Auth(props: { scope: string; children: ReactNode }) {
const scopes = useAtomValue(scopesAtom)
if (!scopes.length) return props.children
const hasScope = scopes.some(s => props.scope.startsWith(s))
if (!hasScope) return null
return props.children
}

65
src/lib/scopes.ts Normal file
View File

@@ -0,0 +1,65 @@
// 权限
export const ScopePermission = "permission";
export const ScopePermissionRead = "permission:read"; // 读取权限列表
export const ScopePermissionWrite = "permission:write"; // 写入权限
// 管理员角色
export const ScopeAdminRole = "admin_role";
export const ScopeAdminRoleRead = "admin_role:read"; // 读取管理员角色列表
export const ScopeAdminRoleWrite = "admin_role:write"; // 写入管理员角色
// 管理员
export const ScopeAdmin = "admin";
export const ScopeAdminRead = "admin:read"; // 读取管理员列表
export const ScopeAdminWrite = "admin:write"; // 写入管理员
// 产品
export const ScopeProduct = "product";
export const ScopeProductRead = "product:read"; // 读取产品列表
export const ScopeProductWrite = "product:write"; // 写入产品
// 产品套餐
export const ScopeProductSku = "product_sku";
export const ScopeProductSkuRead = "product_sku:read"; // 读取产品套餐列表
export const ScopeProductSkuWrite = "product_sku:write"; // 写入产品套餐
// 折扣
export const ScopeDiscount = "discount";
export const ScopeDiscountRead = "discount:read"; // 读取折扣列表
export const ScopeDiscountWrite = "discount:write"; // 写入折扣
// 用户套餐
export const ScopeResource = "resource";
export const ScopeResourceRead = "resource:read"; // 读取用户套餐列表
export const ScopeResourceWrite = "resource:write"; // 写入用户套餐
// 用户
export const ScopeUser = "user";
export const ScopeUserRead = "user:read"; // 读取用户列表
export const ScopeUserWrite = "user:write"; // 写入用户
export const ScopeUserWriteBalance = "user:write:balance"; // 写入用户余额
// 优惠券
export const ScopeCoupon = "coupon";
export const ScopeCouponRead = "coupon:read"; // 读取优惠券列表
export const ScopeCouponWrite = "coupon:write"; // 写入优惠券
// 批次
export const ScopeBatch = "batch";
export const ScopeBatchRead = "batch:read"; // 读取批次列表
export const ScopeBatchWrite = "batch:write"; // 写入批次
// IP
export const ScopeChannel = "channel";
export const ScopeChannelRead = "channel:read"; // 读取 IP 列表
export const ScopeChannelWrite = "channel:write"; // 写入 IP
// 交易
export const ScopeTrade = "trade";
export const ScopeTradeRead = "trade:read"; // 读取交易列表
export const ScopeTradeWrite = "trade:write"; // 写入交易
// 账单
export const ScopeBill = "bill";
export const ScopeBillRead = "bill:read"; // 读取账单列表
export const ScopeBillWrite = "bill:write"; // 写入账单

5
src/lib/stores/scopes.ts Normal file
View File

@@ -0,0 +1,5 @@
import { atom } from "jotai"
const scopesAtom = atom<string[]>([])
export { scopesAtom }