Skip to content

Commit 2589db3

Browse files
committed
feat: enhance AI comment review functionality and update toast styles
- Added new API methods for testing AI comment reviews, including data structures for request and response. - Updated toast styles in the CSS for improved visual consistency, including adjustments to padding, shadows, and button styles. - Introduced a modal for testing AI review functionality in the system settings, allowing users to input comments and receive feedback on spam detection. These changes aim to improve the user experience with AI comment moderation and enhance the overall UI of toast notifications. Signed-off-by: Innei <tukon479@gmail.com>
1 parent 1071941 commit 2589db3

6 files changed

Lines changed: 315 additions & 48 deletions

File tree

src/api/ai.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,24 @@ export interface CreateTaskResponse {
199199
created: boolean
200200
}
201201

202+
export interface AICommentReviewTestData {
203+
text: string
204+
author?: string
205+
}
206+
207+
export interface AICommentReviewTestResponse {
208+
isSpam: boolean
209+
score?: number
210+
reason?: string
211+
}
212+
202213
export const aiApi = {
214+
// AI 评论审核测试
215+
testCommentReview: (data: AICommentReviewTestData) =>
216+
request.post<AICommentReviewTestResponse>('/ai/comment-review/test', {
217+
data,
218+
}),
219+
203220
// AI 写作生成标题/Slug
204221
writerGenerate: (data: AIWriterGenerateData) =>
205222
request.post<AIWriterGenerateResponse>('/ai/writer/generate', { data }),

src/components/ai/ai-helper.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { NButton, NIcon, NTooltip } from 'naive-ui'
1+
import { SparklesIcon } from 'lucide-vue-next'
2+
import { NButton, NTooltip } from 'naive-ui'
23
import { defineComponent, ref } from 'vue'
34
import type { PropType } from 'vue'
45

@@ -93,9 +94,9 @@ export const AiHelperButton = defineComponent({
9394
loading={loading.value}
9495
onClick={callApi}
9596
text
96-
class={'ml-2'}
97+
class={'ml-2 size-4'}
9798
>
98-
<NIcon>{!loading.value && <OpenAIIcon />}</NIcon>
99+
<SparklesIcon class="size-3" />
99100
</NButton>
100101
)
101102
},

src/components/config-form/index.tsx

Lines changed: 67 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,40 @@
11
import { get, set } from 'es-toolkit/compat'
22
import { marked } from 'marked'
3-
import { NDynamicTags, NInput, NInputNumber, NSelect, NSwitch } from 'naive-ui'
4-
import { defineComponent, ref, watch, watchEffect } from 'vue'
5-
import type { PropType, Ref } from 'vue'
3+
import {
4+
NButton,
5+
NDynamicTags,
6+
NInput,
7+
NInputNumber,
8+
NSelect,
9+
NSwitch,
10+
} from 'naive-ui'
11+
import { defineComponent, inject, provide, ref, watch, watchEffect } from 'vue'
12+
import type { InjectionKey, PropType, Ref } from 'vue'
613
import type { FormField } from './types'
714

815
import { SettingsItem } from '~/layouts/settings-layout'
916
import { uuid } from '~/utils'
1017

18+
export type ActionHandler = (actionId: string) => void
19+
export const ActionHandlerKey: InjectionKey<ActionHandler> = Symbol(
20+
'config-form-action-handler',
21+
)
22+
23+
/**
24+
* Compare values for showWhen conditions.
25+
* Handles boolean/string coercion for values like { aiReview: 'true' }
26+
*/
27+
function matchShowWhenValue(actualValue: unknown, expected: unknown): boolean {
28+
if (actualValue === expected) return true
29+
if (typeof actualValue === 'boolean' && typeof expected === 'string') {
30+
return String(actualValue) === expected
31+
}
32+
if (typeof actualValue === 'string' && typeof expected === 'boolean') {
33+
return actualValue === String(expected)
34+
}
35+
return false
36+
}
37+
1138
/**
1239
* Check if a field should be shown based on showWhen conditions.
1340
* When the condition is not met, the field and all its nested children are hidden.
@@ -23,9 +50,11 @@ function shouldShowField(
2350
for (const [key, expected] of Object.entries(showWhen)) {
2451
const actualValue = get(formData.value, `${sectionPrefix}.${key}`)
2552
if (Array.isArray(expected)) {
26-
if (!expected.includes(actualValue)) return false
53+
if (!expected.some((exp) => matchShowWhenValue(actualValue, exp))) {
54+
return false
55+
}
2756
} else {
28-
if (actualValue !== expected) return false
57+
if (!matchShowWhenValue(actualValue, expected)) return false
2958
}
3059
}
3160
return true
@@ -45,8 +74,18 @@ export const SectionFields = defineComponent({
4574
type: String,
4675
required: true,
4776
},
77+
onAction: {
78+
type: Function as PropType<ActionHandler>,
79+
},
4880
},
4981
setup(props) {
82+
const parentHandler = inject(ActionHandlerKey, undefined)
83+
const handler = props.onAction || parentHandler
84+
85+
if (handler) {
86+
provide(ActionHandlerKey, handler)
87+
}
88+
5089
return () => {
5190
const { fields, formData, dataKeyPrefix } = props
5291

@@ -111,6 +150,7 @@ export const FormFieldItem = defineComponent({
111150
},
112151
setup(props) {
113152
const innerValue = ref(props.value)
153+
const actionHandler = inject(ActionHandlerKey, undefined)
114154

115155
watch(
116156
() => props.value,
@@ -133,7 +173,7 @@ export const FormFieldItem = defineComponent({
133173
<NInput
134174
inputProps={{ id: uuid() }}
135175
value={innerValue.value}
136-
onUpdateValue={(val) => {
176+
onUpdateValue={(val: string | null) => {
137177
innerValue.value = val
138178
}}
139179
placeholder={ui.placeholder}
@@ -146,7 +186,7 @@ export const FormFieldItem = defineComponent({
146186
<NInput
147187
inputProps={{ id: uuid() }}
148188
value={innerValue.value}
149-
onUpdateValue={(val) => {
189+
onUpdateValue={(val: string | null) => {
150190
innerValue.value = val
151191
}}
152192
type="password"
@@ -161,7 +201,7 @@ export const FormFieldItem = defineComponent({
161201
<NInput
162202
inputProps={{ id: uuid() }}
163203
value={innerValue.value}
164-
onUpdateValue={(val) => {
204+
onUpdateValue={(val: string | null) => {
165205
innerValue.value = val
166206
}}
167207
type="textarea"
@@ -175,7 +215,7 @@ export const FormFieldItem = defineComponent({
175215
return (
176216
<NInputNumber
177217
value={innerValue.value}
178-
onUpdateValue={(val) => {
218+
onUpdateValue={(val: number | null) => {
179219
innerValue.value = val
180220
}}
181221
placeholder={ui.placeholder}
@@ -186,7 +226,7 @@ export const FormFieldItem = defineComponent({
186226
return (
187227
<NSwitch
188228
value={innerValue.value}
189-
onUpdateValue={(val) => {
229+
onUpdateValue={(val: boolean) => {
190230
innerValue.value = val
191231
}}
192232
/>
@@ -196,7 +236,7 @@ export const FormFieldItem = defineComponent({
196236
return (
197237
<NSelect
198238
value={innerValue.value}
199-
onUpdateValue={(val) => {
239+
onUpdateValue={(val: string | number | null) => {
200240
innerValue.value = val
201241
}}
202242
options={ui.options}
@@ -209,12 +249,27 @@ export const FormFieldItem = defineComponent({
209249
return (
210250
<NDynamicTags
211251
value={innerValue.value}
212-
onUpdateValue={(val) => {
252+
onUpdateValue={(val: string[]) => {
213253
innerValue.value = val
214254
}}
215255
/>
216256
)
217257

258+
case 'action':
259+
return (
260+
<NButton
261+
size="small"
262+
secondary
263+
onClick={() => {
264+
if (ui.actionId && actionHandler) {
265+
actionHandler(ui.actionId)
266+
}
267+
}}
268+
>
269+
{ui.actionLabel || field.title}
270+
</NButton>
271+
)
272+
218273
default:
219274
return null
220275
}

src/components/config-form/types.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export type UIComponent =
66
| 'switch'
77
| 'select'
88
| 'tags'
9+
| 'action'
910

1011
export interface UIConfig {
1112
component: UIComponent
@@ -18,6 +19,18 @@ export interface UIConfig {
1819
* When the condition is not met, the field and all its nested children are hidden.
1920
*/
2021
showWhen?: Record<string, string | string[]>
22+
/**
23+
* Action button configuration (only used when component is 'action')
24+
*/
25+
actionId?: string
26+
actionLabel?: string
27+
actionVariant?:
28+
| 'default'
29+
| 'primary'
30+
| 'info'
31+
| 'success'
32+
| 'warning'
33+
| 'error'
2134
}
2235

2336
export interface FormField {

0 commit comments

Comments
 (0)