Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
陈曦
sub2api
Commits
a04ae28a
Commit
a04ae28a
authored
Apr 13, 2026
by
陈曦
Browse files
merge v0.1.111
parents
68f67198
ad64190b
Changes
302
Show whitespace changes
Inline
Side-by-side
Too many changes to show.
To preserve performance only
302 of 302+
files are displayed.
Plain diff
Email patch
frontend/src/views/admin/AnnouncementsView.vue
View file @
a04ae28a
...
...
@@ -39,7 +39,15 @@
</
template
>
<
template
#table
>
<DataTable
:columns=
"columns"
:data=
"announcements"
:loading=
"loading"
>
<DataTable
:columns=
"columns"
:data=
"announcements"
:loading=
"loading"
:server-side-sort=
"true"
default-sort-key=
"created_at"
default-sort-order=
"desc"
@
sort=
"handleSort"
>
<template
#cell-title
="
{ value, row }">
<div
class=
"min-w-0"
>
<div
class=
"flex items-center gap-2"
>
...
...
@@ -68,7 +76,7 @@
</span>
</
template
>
<
template
#cell-notify
M
ode=
"{ row }"
>
<
template
#cell-notify
_m
ode=
"{ row }"
>
<span
:class=
"[
'badge',
...
...
@@ -100,7 +108,7 @@
</div>
</
template
>
<
template
#cell-created
A
t=
"{ value }"
>
<
template
#cell-created
_a
t=
"{ value }"
>
<span
class=
"text-sm text-gray-500 dark:text-dark-400"
>
{{
formatDateTime
(
value
)
}}
</span>
</
template
>
...
...
@@ -236,7 +244,7 @@
</template>
<
script
setup
lang=
"ts"
>
import
{
computed
,
onMounted
,
reactive
,
ref
}
from
'
vue
'
import
{
computed
,
onMounted
,
onUnmounted
,
reactive
,
ref
}
from
'
vue
'
import
{
useI18n
}
from
'
vue-i18n
'
import
{
useAppStore
}
from
'
@/stores/app
'
import
{
getPersistedPageSize
}
from
'
@/composables/usePersistedPageSize
'
...
...
@@ -276,6 +284,11 @@ const pagination = reactive({
pages
:
0
})
const
sortState
=
reactive
({
sort_by
:
'
created_at
'
,
sort_order
:
'
desc
'
as
'
asc
'
|
'
desc
'
})
const
statusFilterOptions
=
computed
(()
=>
[
{
value
:
''
,
label
:
t
(
'
admin.announcements.allStatus
'
)
},
{
value
:
'
draft
'
,
label
:
t
(
'
admin.announcements.statusLabels.draft
'
)
},
...
...
@@ -295,12 +308,12 @@ const notifyModeOptions = computed(() => [
])
const
columns
=
computed
<
Column
[]
>
(()
=>
[
{
key
:
'
title
'
,
label
:
t
(
'
admin.announcements.columns.title
'
)
},
{
key
:
'
status
'
,
label
:
t
(
'
admin.announcements.columns.status
'
)
},
{
key
:
'
notify
M
ode
'
,
label
:
t
(
'
admin.announcements.columns.notifyMode
'
)
},
{
key
:
'
title
'
,
label
:
t
(
'
admin.announcements.columns.title
'
)
,
sortable
:
true
},
{
key
:
'
status
'
,
label
:
t
(
'
admin.announcements.columns.status
'
)
,
sortable
:
true
},
{
key
:
'
notify
_m
ode
'
,
label
:
t
(
'
admin.announcements.columns.notifyMode
'
)
,
sortable
:
true
},
{
key
:
'
targeting
'
,
label
:
t
(
'
admin.announcements.columns.targeting
'
)
},
{
key
:
'
timeRange
'
,
label
:
t
(
'
admin.announcements.columns.timeRange
'
)
},
{
key
:
'
created
A
t
'
,
label
:
t
(
'
admin.announcements.columns.createdAt
'
)
},
{
key
:
'
created
_a
t
'
,
label
:
t
(
'
admin.announcements.columns.createdAt
'
)
,
sortable
:
true
},
{
key
:
'
actions
'
,
label
:
t
(
'
admin.announcements.columns.actions
'
)
}
])
...
...
@@ -321,15 +334,21 @@ const targetingSummary = (targeting: AnnouncementTargeting) => {
let
currentController
:
AbortController
|
null
=
null
async
function
loadAnnouncements
()
{
if
(
currentController
)
currentController
.
abort
()
currentController
=
new
AbortController
()
currentController
?.
abort
()
const
requestController
=
new
AbortController
()
currentController
=
requestController
const
{
signal
}
=
requestController
try
{
loading
.
value
=
true
const
res
=
await
adminAPI
.
announcements
.
list
(
pagination
.
page
,
pagination
.
page_size
,
{
status
:
filters
.
status
||
undefined
,
search
:
searchQuery
.
value
||
undefined
})
search
:
searchQuery
.
value
||
undefined
,
sort_by
:
sortState
.
sort_by
,
sort_order
:
sortState
.
sort_order
},
{
signal
})
if
(
signal
.
aborted
||
currentController
!==
requestController
)
return
announcements
.
value
=
res
.
items
pagination
.
total
=
res
.
total
...
...
@@ -337,11 +356,21 @@ async function loadAnnouncements() {
pagination
.
page
=
res
.
page
pagination
.
page_size
=
res
.
page_size
}
catch
(
error
:
any
)
{
if
(
currentController
.
signal
.
aborted
||
error
?.
name
===
'
AbortError
'
)
return
if
(
signal
.
aborted
||
currentController
!==
requestController
||
error
?.
name
===
'
AbortError
'
||
error
?.
code
===
'
ERR_CANCELED
'
)
{
return
}
console
.
error
(
'
Error loading announcements:
'
,
error
)
appStore
.
showError
(
error
.
response
?.
data
?.
detail
||
t
(
'
admin.announcements.failedToLoad
'
))
}
finally
{
if
(
currentController
===
requestController
)
{
loading
.
value
=
false
currentController
=
null
}
}
}
...
...
@@ -361,6 +390,13 @@ function handleStatusChange() {
loadAnnouncements
()
}
function
handleSort
(
key
:
string
,
order
:
'
asc
'
|
'
desc
'
)
{
sortState
.
sort_by
=
key
sortState
.
sort_order
=
order
pagination
.
page
=
1
loadAnnouncements
()
}
let
searchDebounceTimer
:
number
|
null
=
null
function
handleSearch
()
{
if
(
searchDebounceTimer
)
window
.
clearTimeout
(
searchDebounceTimer
)
...
...
@@ -562,4 +598,9 @@ onMounted(async () => {
await
loadSubscriptionGroups
()
await
loadAnnouncements
()
})
onUnmounted
(()
=>
{
if
(
searchDebounceTimer
)
window
.
clearTimeout
(
searchDebounceTimer
)
currentController
?.
abort
()
})
</
script
>
frontend/src/views/admin/ChannelsView.vue
View file @
a04ae28a
...
...
@@ -48,7 +48,15 @@
</
template
>
<
template
#table
>
<DataTable
:columns=
"columns"
:data=
"channels"
:loading=
"loading"
>
<DataTable
:columns=
"columns"
:data=
"channels"
:loading=
"loading"
:server-side-sort=
"true"
default-sort-key=
"created_at"
default-sort-order=
"desc"
@
sort=
"handleSort"
>
<template
#cell-name
="
{ value }">
<span
class=
"font-medium text-gray-900 dark:text-white"
>
{{
value
}}
</span>
</
template
>
...
...
@@ -486,6 +494,10 @@ const pagination = reactive({
page_size
:
getPersistedPageSize
(),
total
:
0
})
const
sortState
=
reactive
({
sort_by
:
'
created_at
'
,
sort_order
:
'
desc
'
as
'
asc
'
|
'
desc
'
})
// Dialog state
const
showDialog
=
ref
(
false
)
...
...
@@ -766,7 +778,9 @@ async function loadChannels() {
try
{
const
response
=
await
adminAPI
.
channels
.
list
(
pagination
.
page
,
pagination
.
page_size
,
{
status
:
filters
.
status
||
undefined
,
search
:
searchQuery
.
value
||
undefined
search
:
searchQuery
.
value
||
undefined
,
sort_by
:
sortState
.
sort_by
,
sort_order
:
sortState
.
sort_order
},
{
signal
:
ctrl
.
signal
})
if
(
ctrl
.
signal
.
aborted
||
abortController
!==
ctrl
)
return
...
...
@@ -825,6 +839,13 @@ function handlePageSizeChange(pageSize: number) {
loadChannels
()
}
function
handleSort
(
key
:
string
,
order
:
'
asc
'
|
'
desc
'
)
{
sortState
.
sort_by
=
key
sortState
.
sort_order
=
order
pagination
.
page
=
1
loadChannels
()
}
// ── Dialog ──
function
resetForm
()
{
form
.
name
=
''
...
...
Prev
1
…
12
13
14
15
16
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment