更新navigation里的权限控制 & 更新客户认领查询逻辑
This commit is contained in:
@@ -18,3 +18,7 @@ export async function bindAdmin(params: {
|
||||
user_id: params.id,
|
||||
})
|
||||
}
|
||||
|
||||
export async function getPageUser(params: object) {
|
||||
return callByUser<User>("/api/admin/user/get", params)
|
||||
}
|
||||
|
||||
@@ -112,6 +112,7 @@ export default function Appbar(props: { admin: Admin }) {
|
||||
admin: "管理员",
|
||||
permissions: "权限列表",
|
||||
discount: "折扣管理",
|
||||
statistics: "数据统计",
|
||||
}
|
||||
|
||||
return labels[path] || path
|
||||
|
||||
@@ -23,6 +23,7 @@ import Link from "next/link"
|
||||
import { usePathname } from "next/navigation"
|
||||
import { createContext, type ReactNode, useContext, useState } from "react"
|
||||
import { twJoin } from "tailwind-merge"
|
||||
import { Auth } from "@/components/auth"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { ScrollArea } from "@/components/ui/scroll-area"
|
||||
import { Separator } from "@/components/ui/separator"
|
||||
@@ -32,6 +33,21 @@ import {
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from "@/components/ui/tooltip"
|
||||
import {
|
||||
ScopeAdminRead,
|
||||
ScopeAdminRoleRead,
|
||||
ScopeBatchRead,
|
||||
ScopeBillRead,
|
||||
ScopeChannelRead,
|
||||
ScopeCouponRead,
|
||||
ScopeDiscountRead,
|
||||
ScopePermissionRead,
|
||||
ScopeProductRead,
|
||||
ScopeResourceRead,
|
||||
ScopeTradeRead,
|
||||
ScopeUserRead,
|
||||
ScopeUserReadOne,
|
||||
} from "@/lib/scopes"
|
||||
|
||||
// Navigation Context
|
||||
interface NavigationContextType {
|
||||
@@ -78,13 +94,16 @@ interface NavItemProps {
|
||||
href: string
|
||||
icon: LucideIcon
|
||||
label: string
|
||||
requiredScope?: string
|
||||
}
|
||||
|
||||
function NavItem({ href, icon: Icon, label }: NavItemProps) {
|
||||
function NavItem({ href, icon: Icon, label, requiredScope }: NavItemProps) {
|
||||
// console.log(requiredScope, "requiredScope")
|
||||
|
||||
const { collapsed, isActive } = useNavigation()
|
||||
const active = isActive(href)
|
||||
|
||||
const linkContent = (
|
||||
let linkContent = (
|
||||
<Link
|
||||
href={href}
|
||||
className={`flex items-center ${
|
||||
@@ -101,19 +120,25 @@ function NavItem({ href, icon: Icon, label }: NavItemProps) {
|
||||
)
|
||||
|
||||
if (collapsed) {
|
||||
return (
|
||||
<li>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>{linkContent}</TooltipTrigger>
|
||||
<TooltipContent side="right">
|
||||
<p>{label}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</li>
|
||||
linkContent = (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>{linkContent}</TooltipTrigger>
|
||||
<TooltipContent side="right">
|
||||
<p>{label}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
|
||||
return <li>{linkContent}</li>
|
||||
if (requiredScope) {
|
||||
linkContent = (
|
||||
<Auth scope={requiredScope}>
|
||||
<li>{linkContent}</li>
|
||||
</Auth>
|
||||
)
|
||||
}
|
||||
|
||||
return linkContent
|
||||
}
|
||||
|
||||
// NavSeparator Component
|
||||
@@ -129,6 +154,109 @@ function NavSeparator() {
|
||||
)
|
||||
}
|
||||
|
||||
const menuSections: { title: string; items: NavItemProps[] }[] = [
|
||||
{
|
||||
title: "概览",
|
||||
items: [
|
||||
{ href: "/", icon: Home, label: "首页" },
|
||||
{ href: "/statistics", icon: BarChart3, label: "数据统计" },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "客户",
|
||||
items: [
|
||||
{
|
||||
href: "/user",
|
||||
icon: Users,
|
||||
label: "客户认领",
|
||||
requiredScope: ScopeUserReadOne,
|
||||
},
|
||||
{
|
||||
href: "/cust",
|
||||
icon: ContactRound,
|
||||
label: "客户管理",
|
||||
requiredScope: ScopeUserRead,
|
||||
},
|
||||
{
|
||||
href: "/trade",
|
||||
icon: Activity,
|
||||
label: "交易明细",
|
||||
requiredScope: ScopeTradeRead,
|
||||
},
|
||||
{
|
||||
href: "/billing",
|
||||
icon: DollarSign,
|
||||
label: "账单详情",
|
||||
requiredScope: ScopeBillRead,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "运营",
|
||||
items: [
|
||||
{
|
||||
href: "/product",
|
||||
icon: ShoppingBag,
|
||||
label: "产品管理",
|
||||
requiredScope: ScopeProductRead,
|
||||
},
|
||||
{
|
||||
href: "/discount",
|
||||
icon: SquarePercent,
|
||||
label: "折扣管理",
|
||||
requiredScope: ScopeDiscountRead,
|
||||
},
|
||||
{
|
||||
href: "/coupon",
|
||||
icon: TicketPercent,
|
||||
label: "优惠券",
|
||||
requiredScope: ScopeCouponRead,
|
||||
},
|
||||
{
|
||||
href: "/resources",
|
||||
icon: Package,
|
||||
label: "套餐管理",
|
||||
requiredScope: ScopeResourceRead,
|
||||
},
|
||||
{
|
||||
href: "/batch",
|
||||
icon: ClipboardList,
|
||||
label: "提取记录",
|
||||
requiredScope: ScopeBatchRead,
|
||||
},
|
||||
{
|
||||
href: "/channel",
|
||||
icon: Code,
|
||||
label: "IP管理",
|
||||
requiredScope: ScopeChannelRead,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "系统",
|
||||
items: [
|
||||
{
|
||||
href: "/admin",
|
||||
icon: Shield,
|
||||
label: "管理员",
|
||||
requiredScope: ScopeAdminRead,
|
||||
},
|
||||
{
|
||||
href: "/roles",
|
||||
icon: KeyRound,
|
||||
label: "角色列表",
|
||||
requiredScope: ScopeAdminRoleRead,
|
||||
},
|
||||
{
|
||||
href: "/permissions",
|
||||
icon: Shield,
|
||||
label: "权限列表",
|
||||
requiredScope: ScopePermissionRead,
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
// Main Navigation Component
|
||||
export default function Navigation() {
|
||||
const [collapsed, setCollapsed] = useState(false)
|
||||
@@ -168,56 +296,16 @@ export default function Navigation() {
|
||||
{/* Navigation Menu */}
|
||||
<ScrollArea className="flex-1 py-3">
|
||||
<nav className="space-y-3">
|
||||
{/* 概览 */}
|
||||
<NavGroup title="概览">
|
||||
<NavItem href="/" icon={Home} label="首页" />
|
||||
<NavItem href="/statistics" icon={BarChart3} label="数据统计" />
|
||||
</NavGroup>
|
||||
|
||||
{/*<NavSeparator />*/}
|
||||
|
||||
{/* IP 资源 */}
|
||||
{/*<NavGroup title="IP 资源">
|
||||
<NavItem href="/proxy/nodes" icon={Globe} label="节点列表" />
|
||||
<NavItem href="/proxy/pools" icon={Server} label="IP池管理" />
|
||||
</NavGroup>*/}
|
||||
|
||||
<NavSeparator />
|
||||
|
||||
{/* 客户 */}
|
||||
<NavGroup title="客户">
|
||||
<NavItem href="/user" icon={Users} label="客户认领" />
|
||||
<NavItem href="/cust" icon={ContactRound} label="客户管理" />
|
||||
<NavItem href="/trade" icon={Activity} label="交易明细" />
|
||||
<NavItem href="/billing" icon={DollarSign} label="账单详情" />
|
||||
</NavGroup>
|
||||
|
||||
<NavSeparator />
|
||||
|
||||
{/* 运营 */}
|
||||
<NavGroup title="运营">
|
||||
<NavItem href="/product" icon={ShoppingBag} label="产品管理" />
|
||||
<NavItem
|
||||
href="/discount"
|
||||
icon={SquarePercent}
|
||||
label="折扣管理"
|
||||
/>
|
||||
<NavItem href="/coupon" icon={TicketPercent} label="优惠券" />
|
||||
<NavItem href="/resources" icon={Package} label="套餐管理" />
|
||||
<NavItem href="/batch" icon={ClipboardList} label="提取记录" />
|
||||
<NavItem href="/channel" icon={Code} label="IP管理" />
|
||||
</NavGroup>
|
||||
|
||||
<NavSeparator />
|
||||
|
||||
{/* 系统 */}
|
||||
<NavGroup title="系统">
|
||||
{/*<NavItem href="/settings" icon={Settings} label="系统设置" />*/}
|
||||
<NavItem href="/admin" icon={Shield} label="管理员" />
|
||||
<NavItem href="/roles" icon={KeyRound} label="角色列表" />
|
||||
<NavItem href="/permissions" icon={Shield} label="权限列表" />
|
||||
{/*<NavItem href="/logs" icon={FileText} label="系统日志" />*/}
|
||||
</NavGroup>
|
||||
{menuSections.map((section, idx) => (
|
||||
<div key={section.title}>
|
||||
<NavGroup title={section.title}>
|
||||
{section.items.map(item => (
|
||||
<NavItem key={item.label} {...item} />
|
||||
))}
|
||||
</NavGroup>
|
||||
{idx !== menuSections.length - 1 && <NavSeparator />}
|
||||
</div>
|
||||
))}
|
||||
</nav>
|
||||
</ScrollArea>
|
||||
|
||||
|
||||
@@ -3,9 +3,10 @@ import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { format } from "date-fns"
|
||||
import { Suspense, useCallback, useState } from "react"
|
||||
import { Controller, useForm } from "react-hook-form"
|
||||
import { toast } from "sonner"
|
||||
import { z } from "zod"
|
||||
import { bindAdmin, getPageUsers } from "@/actions/user"
|
||||
import { DataTable, useDataTable } from "@/components/data-table"
|
||||
import { bindAdmin, getPageUser } from "@/actions/user"
|
||||
import { DataTable } from "@/components/data-table"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import {
|
||||
@@ -15,66 +16,84 @@ import {
|
||||
FieldLabel,
|
||||
} from "@/components/ui/field"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { useFetch } from "@/hooks/data"
|
||||
import type { User } from "@/models/user"
|
||||
|
||||
type FilterValues = {
|
||||
interface UserQueryParams {
|
||||
account?: string
|
||||
name?: string
|
||||
identified?: boolean
|
||||
enabled?: boolean
|
||||
assigned?: boolean
|
||||
}
|
||||
|
||||
const filterSchema = z.object({
|
||||
account: z.string().optional(),
|
||||
name: z.string().optional(),
|
||||
identified: z.string().optional(),
|
||||
enabled: z.string().optional(),
|
||||
assigned: z.string().optional(),
|
||||
})
|
||||
|
||||
type FormValues = z.infer<typeof filterSchema>
|
||||
|
||||
export default function UserPage() {
|
||||
const [filters, setFilters] = useState<FilterValues>({})
|
||||
const [userList, setUserList] = useState<User[]>([])
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
const { control, handleSubmit, reset } = useForm<FormValues>({
|
||||
resolver: zodResolver(filterSchema),
|
||||
defaultValues: {
|
||||
account: "",
|
||||
name: "",
|
||||
identified: "all",
|
||||
enabled: "all",
|
||||
assigned: "all",
|
||||
},
|
||||
})
|
||||
|
||||
const fetchUsers = useCallback(
|
||||
(page: number, size: number) => getPageUsers({ page, size, ...filters }),
|
||||
[filters],
|
||||
const fetchUsers = useCallback(async (filters: UserQueryParams = {}) => {
|
||||
setLoading(true)
|
||||
try {
|
||||
const res = await getPageUser(filters)
|
||||
console.log(res, "res")
|
||||
|
||||
if (res.success) {
|
||||
const req = [{ ...res.data }]
|
||||
setUserList(req)
|
||||
} else {
|
||||
toast.error(res.message || "获取用户失败")
|
||||
setUserList([])
|
||||
}
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : error
|
||||
toast.error(`获取管理员失败: ${message}`)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}, [])
|
||||
|
||||
const bind = useCallback(
|
||||
async (id: number) => {
|
||||
try {
|
||||
const res = await bindAdmin({ id })
|
||||
if (res.success) {
|
||||
toast.success("用户已认领")
|
||||
fetchUsers()
|
||||
} else {
|
||||
toast.error(res.message || "认领失败")
|
||||
}
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : error
|
||||
toast.error(`认领请求失败: ${message}`)
|
||||
}
|
||||
},
|
||||
[fetchUsers],
|
||||
)
|
||||
|
||||
const table = useDataTable<User>(fetchUsers)
|
||||
const bind = useFetch(table, (id: number) => bindAdmin({ id }), {
|
||||
done: "用户已认领",
|
||||
fail: "用户认领失败",
|
||||
})
|
||||
const onFilter = handleSubmit((data: FormValues) => {
|
||||
const params: UserQueryParams = {}
|
||||
|
||||
const onFilter = handleSubmit(data => {
|
||||
const result: FilterValues = {}
|
||||
|
||||
if (data.account) result.account = data.account
|
||||
if (data.name) result.name = data.name
|
||||
if (data.identified && data.identified !== "all")
|
||||
result.identified = data.identified === "1"
|
||||
if (data.enabled && data.enabled !== "all")
|
||||
result.enabled = data.enabled === "1"
|
||||
if (data.assigned && data.assigned !== "all")
|
||||
result.assigned = data.assigned === "has"
|
||||
|
||||
setFilters(result)
|
||||
table.pagination.onPageChange(1)
|
||||
if (data.account?.trim()) params.account = data.account.trim()
|
||||
if (data.name?.trim()) params.name = data.name.trim()
|
||||
const hasValidFilter = Object.keys(params).length > 0
|
||||
if (hasValidFilter) {
|
||||
fetchUsers(params)
|
||||
} else {
|
||||
setUserList([])
|
||||
setLoading(false)
|
||||
toast.info("请输入筛选条件后再查询")
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
@@ -110,69 +129,6 @@ export default function UserPage() {
|
||||
</Field>
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* <Controller
|
||||
name="identified"
|
||||
control={control}
|
||||
render={({ field, fieldState }) => (
|
||||
<Field data-invalid={fieldState.invalid} className="w-24">
|
||||
<FieldLabel>实名状态</FieldLabel>
|
||||
<Select value={field.value} onValueChange={field.onChange}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="全部" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="all">全部</SelectItem>
|
||||
<SelectItem value="1">已认证</SelectItem>
|
||||
<SelectItem value="0">未认证</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Controller
|
||||
name="enabled"
|
||||
control={control}
|
||||
render={({ field, fieldState }) => (
|
||||
<Field data-invalid={fieldState.invalid} className="w-24">
|
||||
<FieldLabel>账号状态</FieldLabel>
|
||||
<Select value={field.value} onValueChange={field.onChange}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="全部" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="all">全部</SelectItem>
|
||||
<SelectItem value="1">正常</SelectItem>
|
||||
<SelectItem value="0">禁用</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Controller
|
||||
name="assigned"
|
||||
control={control}
|
||||
render={({ field, fieldState }) => (
|
||||
<Field data-invalid={fieldState.invalid} className="w-24">
|
||||
<FieldLabel>认领状态</FieldLabel>
|
||||
<Select value={field.value} onValueChange={field.onChange}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="全部" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="all">全部</SelectItem>
|
||||
<SelectItem value="has">已认领</SelectItem>
|
||||
<SelectItem value="none">未认领</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
/> */}
|
||||
</div>
|
||||
|
||||
<FieldGroup className="flex-row justify-start mt-4 gap-2">
|
||||
@@ -181,15 +137,9 @@ export default function UserPage() {
|
||||
type="button"
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
reset({
|
||||
account: "",
|
||||
name: "",
|
||||
identified: "all",
|
||||
enabled: "all",
|
||||
assigned: "all",
|
||||
})
|
||||
setFilters({})
|
||||
table.pagination.onPageChange(1)
|
||||
reset()
|
||||
setUserList([])
|
||||
setLoading(false)
|
||||
}}
|
||||
>
|
||||
重置
|
||||
@@ -199,7 +149,8 @@ export default function UserPage() {
|
||||
|
||||
<Suspense>
|
||||
<DataTable<User>
|
||||
{...table}
|
||||
data={userList || []}
|
||||
status={loading ? "load" : "done"}
|
||||
columns={[
|
||||
{ header: "ID", accessorKey: "id" },
|
||||
{ header: "账号", accessorKey: "username" },
|
||||
|
||||
@@ -23,7 +23,7 @@ export type DataTableProps<T> = {
|
||||
data: T[]
|
||||
status: "load" | "done" | "fail"
|
||||
columns: ColumnDef<T>[]
|
||||
pagination: PaginationProps
|
||||
pagination?: PaginationProps
|
||||
classNames?: {
|
||||
root?: string
|
||||
headRow?: string
|
||||
@@ -39,9 +39,9 @@ export function DataTable<T extends Record<string, unknown>>(
|
||||
columns: props.columns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
manualPagination: true,
|
||||
rowCount: props.pagination.total,
|
||||
rowCount: props.pagination?.total,
|
||||
state: {
|
||||
pagination: {
|
||||
pagination: props.pagination && {
|
||||
pageIndex: props.pagination.page,
|
||||
pageSize: props.pagination.size,
|
||||
},
|
||||
@@ -160,7 +160,7 @@ export function DataTable<T extends Record<string, unknown>>(
|
||||
</div>
|
||||
|
||||
{/* 分页器 */}
|
||||
<Pagination {...props.pagination} />
|
||||
{props.pagination && <Pagination {...props.pagination} />}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,65 +1,67 @@
|
||||
// 权限
|
||||
export const ScopePermission = "permission";
|
||||
export const ScopePermissionRead = "permission:read"; // 读取权限列表
|
||||
export const ScopePermissionWrite = "permission:write"; // 写入权限
|
||||
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 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 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 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 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 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 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 ScopeUser = "user"
|
||||
export const ScopeUserRead = "user:read" // 读取用户列表
|
||||
export const ScopeUserWrite = "user:write" // 写入用户
|
||||
export const ScopeUserWriteBalance = "user:write:balance" // 写入用户余额
|
||||
export const ScopeUserReadOne = "user:read:one" // 读取单个用户
|
||||
export const ScopeUserWriteBind = "user:write:bind" // 认领用户
|
||||
|
||||
// 优惠券
|
||||
export const ScopeCoupon = "coupon";
|
||||
export const ScopeCouponRead = "coupon:read"; // 读取优惠券列表
|
||||
export const ScopeCouponWrite = "coupon:write"; // 写入优惠券
|
||||
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"; // 写入批次
|
||||
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 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 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"; // 写入账单
|
||||
export const ScopeBill = "bill"
|
||||
export const ScopeBillRead = "bill:read" // 读取账单列表
|
||||
export const ScopeBillWrite = "bill:write" // 写入账单
|
||||
|
||||
Reference in New Issue
Block a user