Skip to content
Merged

mr #2

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion controller/channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -1218,7 +1218,7 @@ func CopyChannel(c *gin.Context) {
}

// insert
if err := model.BatchInsertChannels([]model.Channel{clone}); err != nil {
if err := clone.Insert(); err != nil {
common.SysError("failed to clone channel: " + err.Error())
c.JSON(http.StatusOK, gin.H{"success": false, "message": "复制渠道失败,请稍后重试"})
return
Expand Down
1 change: 1 addition & 0 deletions controller/misc.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ func GetStatus(c *gin.Context) {
"demo_site_enabled": operation_setting.DemoSiteEnabled,
"self_use_mode_enabled": operation_setting.SelfUseModeEnabled,
"register_enabled": common.RegisterEnabled,
"password_login_enabled": common.PasswordLoginEnabled,
"password_register_enabled": common.PasswordRegisterEnabled,
"default_use_auto_group": setting.DefaultUseAutoGroup,

Expand Down
14 changes: 13 additions & 1 deletion controller/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,8 +251,20 @@ func GetAllUsers(c *gin.Context) {
func SearchUsers(c *gin.Context) {
keyword := c.Query("keyword")
group := c.Query("group")
var role *int
if roleStr := c.Query("role"); roleStr != "" {
if parsed, err := strconv.Atoi(roleStr); err == nil {
role = &parsed
}
}
var status *int
if statusStr := c.Query("status"); statusStr != "" {
if parsed, err := strconv.Atoi(statusStr); err == nil {
status = &parsed
}
}
pageInfo := common.GetPageQuery(c)
users, total, err := model.SearchUsers(keyword, group, pageInfo.GetStartIdx(), pageInfo.GetPageSize())
users, total, err := model.SearchUsers(keyword, group, role, status, pageInfo.GetStartIdx(), pageInfo.GetPageSize())
if err != nil {
common.ApiError(c, err)
return
Expand Down
31 changes: 14 additions & 17 deletions model/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ func GetAllUsers(pageInfo *common.PageInfo) (users []*User, total int64, err err
return users, total, nil
}

func SearchUsers(keyword string, group string, startIdx int, num int) ([]*User, int64, error) {
func SearchUsers(keyword string, group string, role *int, status *int, startIdx int, num int) ([]*User, int64, error) {
var users []*User
var total int64
var err error
Expand All @@ -246,28 +246,25 @@ func SearchUsers(keyword string, group string, startIdx int, num int) ([]*User,

// 构建搜索条件
likeCondition := "username LIKE ? OR email LIKE ? OR display_name LIKE ?"
likeArgs := []interface{}{"%" + keyword + "%", "%" + keyword + "%", "%" + keyword + "%"}

// 尝试将关键字转换为整数ID
keywordInt, err := strconv.Atoi(keyword)
if err == nil {
// 如果是数字,同时搜索ID和其他字段
likeCondition = "id = ? OR " + likeCondition
if group != "" {
query = query.Where("("+likeCondition+") AND "+commonGroupCol+" = ?",
keywordInt, "%"+keyword+"%", "%"+keyword+"%", "%"+keyword+"%", group)
} else {
query = query.Where(likeCondition,
keywordInt, "%"+keyword+"%", "%"+keyword+"%", "%"+keyword+"%")
}
} else {
// 非数字关键字,只搜索字符串字段
if group != "" {
query = query.Where("("+likeCondition+") AND "+commonGroupCol+" = ?",
"%"+keyword+"%", "%"+keyword+"%", "%"+keyword+"%", group)
} else {
query = query.Where(likeCondition,
"%"+keyword+"%", "%"+keyword+"%", "%"+keyword+"%")
}
likeArgs = append([]interface{}{keywordInt}, likeArgs...)
}

query = query.Where("("+likeCondition+")", likeArgs...)
if group != "" {
query = query.Where(commonGroupCol+" = ?", group)
}
if role != nil {
query = query.Where("role = ?", *role)
}
if status != nil {
query = query.Where("status = ?", *status)
}

// 获取总数
Expand Down
627 changes: 289 additions & 338 deletions web/default/bun.lock

Large diffs are not rendered by default.

115 changes: 64 additions & 51 deletions web/default/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,83 +18,96 @@
"knip": "knip"
},
"dependencies": {
"@base-ui/react": "^1.4.1",
"@base-ui/react": "^1.5.0",
"@fontsource-variable/public-sans": "^5.2.7",
"@hookform/resolvers": "^5.2.2",
"@hugeicons/core-free-icons": "^4.1.1",
"@hookform/resolvers": "^5.4.0",
"@hugeicons/core-free-icons": "^4.1.4",
"@hugeicons/react": "^1.1.6",
"@lobehub/icons": "^4.0.3",
"@tailwindcss/postcss": "^4.2.2",
"@tanstack/react-query": "^5.95.2",
"@tanstack/react-router": "^1.168.23",
"@lobehub/icons": "^5.8.0",
"@tailwindcss/postcss": "^4.3.0",
"@tanstack/react-query": "^5.100.14",
"@tanstack/react-router": "^1.170.8",
"@tanstack/react-table": "^8.21.3",
"@tanstack/react-virtual": "^3.13.18",
"@visactor/react-vchart": "^2.0.13",
"@visactor/vchart": "^2.0.13",
"ai": "^6.0.27",
"@tanstack/react-virtual": "^3.13.25",
"@visactor/react-vchart": "^2.0.22",
"@visactor/vchart": "^2.0.22",
"ai": "^6.0.191",
"auto-skeleton-react": "^1.0.5",
"axios": "^1.13.6",
"axios": "^1.16.1",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
"date-fns": "^4.1.0",
"dayjs": "^1.11.19",
"i18next": "^25.7.4",
"i18next-browser-languagedetector": "^8.2.0",
"date-fns": "^4.3.0",
"dayjs": "^1.11.20",
"i18next": "^26.2.0",
"i18next-browser-languagedetector": "^8.2.1",
"input-otp": "^1.4.2",
"lucide-react": "^1.7.0",
"motion": "^12.38.0",
"nanoid": "^5.1.6",
"lucide-react": "^1.16.0",
"motion": "^12.40.0",
"nanoid": "^5.1.11",
"next-themes": "^0.4.6",
"qrcode.react": "^4.2.0",
"react": "^19.2.4",
"react-day-picker": "^9.14.0",
"react-dom": "^19.2.4",
"react-hook-form": "^7.71.0",
"react-i18next": "^16.5.2",
"react-icons": "^5.5.0",
"react": "^19.2.6",
"react-day-picker": "^10.0.1",
"react-dom": "^19.2.6",
"react-hook-form": "^7.76.1",
"react-i18next": "^17.0.8",
"react-icons": "^5.6.0",
"react-markdown": "^10.1.0",
"react-resizable-panels": "^4.11.0",
"react-resizable-panels": "^4.11.2",
"react-top-loading-bar": "^3.0.2",
"recharts": "3.8.0",
"recharts": "3.8.1",
"rehype-raw": "^7.0.0",
"remark-gfm": "^4.0.1",
"shiki": "^4.0.2",
"shiki": "^4.1.0",
"sonner": "^2.0.7",
"sse.js": "^2.7.2",
"streamdown": "^2.0.1",
"tailwind-merge": "^3.5.0",
"tailwindcss": "^4.2.2",
"sse.js": "^2.8.0",
"streamdown": "^2.5.0",
"tailwind-merge": "^3.6.0",
"tailwindcss": "^4.3.0",
"tokenlens": "^1.3.1",
"tw-animate-css": "^1.4.0",
"use-stick-to-bottom": "^1.1.1",
"use-stick-to-bottom": "^1.1.4",
"vaul": "^1.1.2",
"zod": "^4.3.6",
"zustand": "^5.0.12"
"zod": "^4.4.3",
"zustand": "^5.0.13"
},
"devDependencies": {
"@eslint/js": "^10.0.1",
"@rsbuild/core": "^2.0.1",
"@rsbuild/core": "^2.0.7",
"@rsbuild/plugin-react": "^2.0.0",
"@tanstack/eslint-plugin-query": "^5.95.2",
"@tanstack/react-query-devtools": "^5.95.2",
"@tanstack/react-router-devtools": "^1.166.13",
"@tanstack/router-plugin": "^1.167.23",
"@tanstack/eslint-plugin-query": "^5.100.14",
"@tanstack/react-query-devtools": "^5.100.14",
"@tanstack/react-router-devtools": "^1.167.0",
"@tanstack/router-plugin": "^1.168.11",
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
"@types/node": "^25.5.0",
"@types/react": "^19.2.14",
"@types/node": "^25.9.1",
"@types/react": "^19.2.15",
"@types/react-dom": "^19.2.3",
"@xyflow/react": "^12.10.2",
"embla-carousel-react": "^8.6.0",
"eslint": "^10.1.0",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint": "^10.4.0",
"eslint-plugin-react-hooks": "^7.1.1",
"eslint-plugin-react-refresh": "^0.5.2",
"globals": "^17.4.0",
"knip": "^6.0.6",
"prettier": "^3.8.1",
"prettier-plugin-tailwindcss": "^0.7.2",
"shadcn": "^3.7.0",
"typescript": "~5.9.3",
"typescript-eslint": "^8.57.2"
"globals": "^17.6.0",
"knip": "^6.14.2",
"prettier": "^3.8.3",
"prettier-plugin-tailwindcss": "^0.8.0",
"shadcn": "^4.8.0",
"typescript": "~6.0.3",
"typescript-eslint": "^8.59.4"
},
"overrides": {
"brace-expansion": "5.0.6",
"dompurify": "3.4.5",
"fast-uri": "3.1.2",
"hono": "4.12.22",
"ip-address": "10.2.0",
"js-cookie": "3.0.7",
"mermaid": "11.15.0",
"minimist": "1.2.8",
"postcss": "8.5.15",
"qs": "6.15.2",
"uuid": "14.0.0"
}
}
16 changes: 11 additions & 5 deletions web/default/src/components/config-drawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ import {
SheetTitle,
SheetTrigger,
} from '@/components/ui/sheet'
import {
sideDrawerContentClassName,
sideDrawerFooterClassName,
sideDrawerFormClassName,
sideDrawerHeaderClassName,
} from '@/components/drawer-layout'
import { useSidebar } from './ui/sidebar'

const Item = RadioPrimitive.Root
Expand Down Expand Up @@ -88,14 +94,14 @@ export function ConfigDrawer() {
>
<Palette className='size-[1.2rem]' aria-hidden='true' />
</SheetTrigger>
<SheetContent className='flex w-full flex-col sm:max-w-md'>
<SheetHeader className='pb-0 text-start'>
<SheetContent className={sideDrawerContentClassName('sm:max-w-md')}>
<SheetHeader className={sideDrawerHeaderClassName()}>
<SheetTitle>{t('Theme Settings')}</SheetTitle>
<SheetDescription id='config-drawer-description'>
{t('Adjust the appearance and layout to suit your preferences.')}
</SheetDescription>
</SheetHeader>
<div className='space-y-6 overflow-y-auto px-4'>
<div className={sideDrawerFormClassName()}>
<ThemeConfig />
<PresetConfig />
<RadiusConfig />
Expand All @@ -105,7 +111,7 @@ export function ConfigDrawer() {
<ContentLayoutConfig />
<DirConfig />
</div>
<SheetFooter className='gap-2'>
<SheetFooter className={sideDrawerFooterClassName('grid-cols-1')}>
<Button
variant='destructive'
onClick={handleReset}
Expand Down Expand Up @@ -302,7 +308,7 @@ const RADIUS_OPTIONS: {
// CSS border-radius value used to render the visual preview corner.
preview: string
}[] = [
{ value: 'default', label: 'Auto', preview: '999px' },
{ value: 'default', label: 'Auto', preview: '1rem' },
{ value: 'none', label: '0', preview: '0' },
{ value: 'sm', label: '0.3', preview: '0.3rem' },
{ value: 'md', label: '0.5', preview: '0.5rem' },
Expand Down
105 changes: 105 additions & 0 deletions web/default/src/components/drawer-layout.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
Copyright (C) 2023-2026 QuantumNous

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.

For commercial licensing, please contact support@quantumnous.com
*/
import { createElement, type ReactNode } from 'react'
import { cn } from '@/lib/utils'

export const sideDrawerContentClassName = (className?: string) =>
cn(
'bg-background text-foreground flex h-dvh w-full flex-col gap-0 overflow-hidden p-0 shadow-none',
className
)

export const sideDrawerHeaderClassName = (className?: string) =>
cn(
'border-border/70 bg-background/95 border-b px-4 py-3 text-start backdrop-blur supports-[backdrop-filter]:bg-background/80 sm:px-6 sm:py-4',
className
)

export const sideDrawerFormClassName = (className?: string) =>
cn(
'flex min-h-0 flex-1 flex-col gap-6 overflow-y-auto overscroll-contain px-4 py-4 sm:px-6 sm:py-5',
className
)

export const sideDrawerFooterClassName = (className?: string) =>
cn(
'border-border/70 bg-background/95 grid grid-cols-2 gap-2 border-t px-4 py-3 backdrop-blur supports-[backdrop-filter]:bg-background/80 sm:flex sm:flex-row sm:justify-end sm:px-6 sm:py-4',
className
)

export const sideDrawerSectionClassName = (className?: string) =>
cn(
'border-border/60 flex flex-col gap-4 border-b pb-6 last:border-b-0 last:pb-0',
className
)

export const sideDrawerSwitchItemClassName = (className?: string) =>
cn(
'border-border/60 flex min-h-16 flex-row items-center justify-between gap-3 border-y py-3',
className
)

export function SideDrawerSection(props: {
children: ReactNode
className?: string
}) {
return createElement(
'section',
{ className: sideDrawerSectionClassName(props.className) },
props.children
)
}

export function SideDrawerSectionHeader(props: {
title: ReactNode
description?: ReactNode
icon?: ReactNode
className?: string
}) {
return createElement(
'div',
{ className: cn('flex items-start gap-3', props.className) },
props.icon
? createElement(
'span',
{
className:
'bg-muted text-muted-foreground flex size-8 shrink-0 items-center justify-center rounded-md',
},
props.icon
)
: null,
createElement(
'div',
{ className: 'min-w-0 flex-1' },
createElement(
'h3',
{ className: 'text-sm leading-none font-semibold tracking-tight' },
props.title
),
props.description
? createElement(
'p',
{ className: 'text-muted-foreground mt-1 text-xs leading-5' },
props.description
)
: null
)
)
}
Loading