实现权限列表与权限渲染组件
This commit is contained in:
3
bun.lock
3
bun.lock
@@ -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=="],
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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(" ") || [],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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("登录失败", {
|
||||
|
||||
13
src/components/auth/index.ts
Normal file
13
src/components/auth/index.ts
Normal 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
65
src/lib/scopes.ts
Normal 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
5
src/lib/stores/scopes.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { atom } from "jotai"
|
||||
|
||||
const scopesAtom = atom<string[]>([])
|
||||
|
||||
export { scopesAtom }
|
||||
Reference in New Issue
Block a user