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

View File

@ -1,16 +1,17 @@
import type { FC } from 'react' import type { FC } from 'react'
import { memo } 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 = { type CommentIconProps = {
comment: WorkflowComment comment: WorkflowCommentList
onClick: () => void onClick: () => void
} }
export const CommentIcon: FC<CommentIconProps> = memo(({ comment, onClick }) => { export const CommentIcon: FC<CommentIconProps> = memo(({ comment, onClick }) => {
return ( return (
<div <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={{ style={{
left: comment.position_x, left: comment.position_x,
top: comment.position_y, top: comment.position_y,
@ -18,8 +19,19 @@ export const CommentIcon: FC<CommentIconProps> = memo(({ comment, onClick }) =>
}} }}
onClick={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"> <div className="relative h-14 w-14 overflow-hidden rounded-br-full rounded-tl-full rounded-tr-full">
{'A'} <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>
</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 { useParams } from 'next/navigation'
import { useStore } from '../store' import { useStore } from '../store'
import { ControlMode } from '../types' import { ControlMode } from '../types'
import type { WorkflowComment } from '@/service/workflow-comment' import type { WorkflowComment } from '@/service/workflow-comment'
import { createWorkflowComment } from '@/service/workflow-comment' import { createWorkflowComment, fetchWorkflowComments } from '@/service/workflow-comment'
export const useWorkflowComment = () => { export const useWorkflowComment = () => {
const params = useParams() const params = useParams()
@ -13,6 +13,29 @@ export const useWorkflowComment = () => {
const pendingComment = useStore(s => s.pendingComment) const pendingComment = useStore(s => s.pendingComment)
const setPendingComment = useStore(s => s.setPendingComment) const setPendingComment = useStore(s => s.setPendingComment)
const [comments, setComments] = useState<WorkflowComment[]>([]) 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) => { const handleCommentSubmit = useCallback(async (content: string) => {
if (!pendingComment) return if (!pendingComment) return
@ -77,10 +100,12 @@ export const useWorkflowComment = () => {
return { return {
comments, comments,
loading,
pendingComment, pendingComment,
handleCommentSubmit, handleCommentSubmit,
handleCommentCancel, handleCommentCancel,
handleCommentIconClick, handleCommentIconClick,
handleCreateComment, handleCreateComment,
loadComments,
} }
} }

View File

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