display comments avatar on the canvas

This commit is contained in:
hjlarry 2025-09-15 11:41:06 +08:00
parent 9fda130b3a
commit d7f5da5df4
4 changed files with 94 additions and 27 deletions

View File

@ -1,6 +1,6 @@
from flask_restful import fields
from libs.helper import TimestampField
from libs.helper import AvatarUrlField, TimestampField
# Basic account fields for comment creators/resolvers
comment_account_fields = {"id": fields.String, "name": fields.String, "email": fields.String}
@ -26,7 +26,7 @@ workflow_comment_participant_fields = {
"id": fields.String,
"name": fields.String,
"email": fields.String,
"avatar": fields.String,
"avatar_url": AvatarUrlField,
}
# Basic comment fields (for list views)

View File

@ -1,16 +1,17 @@
import type { FC } from 'react'
import { memo } from 'react'
import type { WorkflowComment } from '@/service/workflow-comment'
import Avatar from '@/app/components/base/avatar'
import type { WorkflowCommentList } from '@/service/workflow-comment'
type CommentIconProps = {
comment: WorkflowComment
comment: WorkflowCommentList
onClick: () => void
}
export const CommentIcon: FC<CommentIconProps> = memo(({ comment, onClick }) => {
return (
<div
className="absolute z-40 flex h-8 w-8 cursor-pointer items-center justify-center rounded-full bg-blue-500 text-white shadow-lg hover:bg-blue-600"
className="absolute z-40 cursor-pointer"
style={{
left: comment.position_x,
top: comment.position_y,
@ -18,8 +19,19 @@ export const CommentIcon: FC<CommentIconProps> = memo(({ comment, onClick }) =>
}}
onClick={onClick}
>
<div className="flex h-6 w-6 items-center justify-center rounded-full bg-white text-xs font-medium text-blue-500">
{'A'}
<div className="relative h-14 w-14 overflow-hidden rounded-br-full rounded-tl-full rounded-tr-full">
<div className="absolute inset-1 overflow-hidden rounded-br-full rounded-tl-full rounded-tr-full bg-white">
<div className="flex h-full w-full items-center justify-center">
<div className="h-10 w-10 overflow-hidden rounded-full">
<Avatar
avatar={comment.created_by_account.avatar_url}
name={comment.created_by_account.name}
size={40}
className="h-full w-full"
/>
</div>
</div>
</div>
</div>
</div>
)

View File

@ -1,9 +1,9 @@
import { useCallback, useState } from 'react'
import { useCallback, useEffect, useState } from 'react'
import { useParams } from 'next/navigation'
import { useStore } from '../store'
import { ControlMode } from '../types'
import type { WorkflowComment } from '@/service/workflow-comment'
import { createWorkflowComment } from '@/service/workflow-comment'
import { createWorkflowComment, fetchWorkflowComments } from '@/service/workflow-comment'
export const useWorkflowComment = () => {
const params = useParams()
@ -13,6 +13,29 @@ export const useWorkflowComment = () => {
const pendingComment = useStore(s => s.pendingComment)
const setPendingComment = useStore(s => s.setPendingComment)
const [comments, setComments] = useState<WorkflowComment[]>([])
const [loading, setLoading] = useState(false)
// 加载评论列表
const loadComments = useCallback(async () => {
if (!appId) return
setLoading(true)
try {
const commentsData = await fetchWorkflowComments(appId)
setComments(commentsData)
}
catch (error) {
console.error('Failed to fetch comments:', error)
}
finally {
setLoading(false)
}
}, [appId])
// 初始化时加载评论
useEffect(() => {
loadComments()
}, [loadComments])
const handleCommentSubmit = useCallback(async (content: string) => {
if (!pendingComment) return
@ -77,10 +100,12 @@ export const useWorkflowComment = () => {
return {
comments,
loading,
pendingComment,
handleCommentSubmit,
handleCommentCancel,
handleCommentIconClick,
handleCreateComment,
loadComments,
}
}

View File

@ -1,26 +1,56 @@
import { del, get, post, put } from './base'
import type { CommonResponse } from '@/models/common'
export type WorkflowComment = {
export type UserProfile = {
id: string
name: string
email: string
avatar_url?: string
}
export type WorkflowCommentList = {
id: string
app_id: string
position_x: number
position_y: number
content: string
created_by: string
created_by_account: UserProfile
created_at: string
updated_at: string
resolved: boolean
resolved_by?: string
resolved_by_account?: UserProfile
resolved_at?: string
mentioned_user_ids: string[]
replies_count: number
author: {
id: string
name: string
email: string
avatar?: string
}
mention_count: number
reply_count: number
participants: UserProfile[]
}
export type WorkflowCommentDetail = {
id: string
position_x: number
position_y: number
content: string
created_by: string
created_by_account: UserProfile
created_at: string
updated_at: string
resolved: boolean
resolved_by?: string
resolved_by_account?: UserProfile
resolved_at?: string
replies: []
mentions: []
}
export type WorkflowCommentCreateRes = {
id: string
created_at: string
}
export type WorkflowCommentUpdateRes = {
id: string
updated_at: string
}
export type WorkflowCommentReply = {
@ -58,21 +88,21 @@ export type CreateReplyParams = {
mentioned_user_ids?: string[]
}
export const fetchWorkflowComments = async (appId: string): Promise<WorkflowComment[]> => {
const response = await get<{ data: WorkflowComment[] }>(`apps/${appId}/workflow/comments`)
export const fetchWorkflowComments = async (appId: string): Promise<WorkflowCommentList[]> => {
const response = await get<{ data: WorkflowCommentList[] }>(`apps/${appId}/workflow/comments`)
return response.data
}
export const createWorkflowComment = async (appId: string, params: CreateCommentParams): Promise<WorkflowComment> => {
return post<WorkflowComment>(`apps/${appId}/workflow/comments`, { body: params })
export const createWorkflowComment = async (appId: string, params: CreateCommentParams): Promise<WorkflowCommentCreateRes> => {
return post<WorkflowCommentCreateRes>(`apps/${appId}/workflow/comments`, { body: params })
}
export const fetchWorkflowComment = async (appId: string, commentId: string): Promise<WorkflowComment> => {
return get<WorkflowComment>(`apps/${appId}/workflow/comments/${commentId}`)
export const fetchWorkflowComment = async (appId: string, commentId: string): Promise<WorkflowCommentDetail> => {
return get<WorkflowCommentDetail>(`apps/${appId}/workflow/comments/${commentId}`)
}
export const updateWorkflowComment = async (appId: string, commentId: string, params: UpdateCommentParams): Promise<WorkflowComment> => {
return put<WorkflowComment>(`apps/${appId}/workflow/comments/${commentId}`, { body: params })
export const updateWorkflowComment = async (appId: string, commentId: string, params: UpdateCommentParams): Promise<WorkflowCommentUpdateRes> => {
return put<WorkflowCommentUpdateRes>(`apps/${appId}/workflow/comments/${commentId}`, { body: params })
}
export const deleteWorkflowComment = async (appId: string, commentId: string): Promise<CommonResponse> => {