Conversation
|
Warning Rate limit exceeded@SonyLeo has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 6 minutes and 25 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (2)
WalkthroughA new Attachments component system is introduced, including Vue components, composables, and comprehensive documentation. The implementation supports file lists with preview, download, status display, custom actions, and extensibility for file types and icons. Multiple demos illustrate usage scenarios, and type definitions ensure strong typing for all interfaces and events. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Attachments as Attachments.vue
participant FileCard
participant ImagePreview
participant Composables
User->>Attachments: Provide items, props, actions
Attachments->>Composables: useListType, useImagePreview
Attachments->>FileCard: Render for each file
FileCard->>Attachments: Emit remove/preview/download/retry/action events
Attachments->>User: Emit corresponding events
FileCard->>Composables: useFileCard for file interactions
Attachments->>ImagePreview: Render when preview triggered
ImagePreview->>Attachments: Emit close or update events
Estimated code review effort3 (120 minutes) Poem
✨ Finishing Touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
775d733 to
efbb81e
Compare
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 13
♻️ Duplicate comments (1)
packages/components/src/attachments/index.type.ts (1)
5-7: Consider consolidating FileStatus and StatusDisplayModeAs mentioned in a previous review, these two types control overlapping UI aspects and could potentially be merged into a single
variantprop for better API clarity.
🧹 Nitpick comments (8)
docs/demos/attachments/custom-icon.vue (1)
11-14: Consider using more descriptive icons for better UX.While the emoji icons work functionally, consider using more conventional file type icons or SVG icons for better visual consistency and accessibility.
const fileIcons: Record<FileType, Component> = { - word: h('span', '📄'), - excel: h('span', '🐼'), + word: h('span', { class: 'file-icon-word' }, '📄'), + excel: h('span', { class: 'file-icon-excel' }, '📊'), }docs/demos/attachments/basic.vue (1)
16-16: Consider using constants for file size calculations.The file size calculations are repeated and could be made more maintainable using constants.
+const MB = 1024 * 1024 + const infoFiles = ref<Attachment[]>([ { id: '1', name: '设计文档.docx', fileType: 'word', - size: 1024 * 1024 * 1.2, // 1.2MB + size: MB * 1.2, // 1.2MB status: 'success', }, // ... apply similar changes to other entries ])Also applies to: 23-23, 30-30
packages/components/src/attachments/composables/useFileCard.ts (1)
89-92: Review the custom action logic for image files.The current logic only triggers preview/download for image files when
action.typematches, but this might not cover all use cases. Consider if non-image files should also support these action types.Consider this refactor to make the logic more explicit:
- if (action.type === 'preview' && isImage.value) { - handlePreview() - } else if (action.type === 'download' && isImage.value) { - downloadFile() + if (action.type === 'preview') { + handlePreview() + } else if (action.type === 'download') { + downloadFile() } else { emit('action', { action, file: props.file }) }docs/demos/attachments/picture-list.vue (1)
34-84: Consider using local or more reliable image sources.The demo uses external CDN URLs (
https://res.hc-cdn.com/...) which may become unavailable over time, potentially breaking the demo.Consider using:
- Local placeholder images in the project
- A more reliable CDN like
https://picsum.photos/for demo purposes- Data URLs for small placeholder images
Example with placeholder service:
- previewUrl: 'https://res.hc-cdn.com/tiny-vue-web-doc/3.23.0.20250521142915/static/images/fruit.jpg', + previewUrl: 'https://picsum.photos/200/150?random=1',packages/components/src/attachments/composables/useImagePreview.ts (1)
43-45: Consider using a more robust image filtering strategy.The current filtering excludes
erroranduploadingstates, but consider if other states should also be excluded from preview.Consider defining allowed states more explicitly:
- previewImages.value = fileList.value.filter( - (item) => item.fileType === 'image' && item.status !== 'error' && item.status !== 'uploading', - ) + const allowedStatuses = ['success', 'warning', 'info'] + previewImages.value = fileList.value.filter( + (item) => item.fileType === 'image' && allowedStatuses.includes(item.status), + )packages/components/src/attachments/index.vue (1)
63-71: Consider optimizing the deep watch strategy.The current deep watch on
props.itemsmight cause performance issues with large file lists. Consider using a more targeted approach.Consider using
watchEffector a more specific watch:-// 监听props.items变化 -watch( - () => props.items, - (newItems) => { - if (newItems) { - fileList.value = newItems - } - }, - { deep: true }, -) +// 监听props.items变化 +watchEffect(() => { + if (props.items) { + fileList.value = [...props.items] + } +})packages/components/src/attachments/composables/useFileType.ts (2)
87-89: Consider using a more robust ID generation methodThe current implementation using
Math.random()could theoretically produce collisions. Consider using a more robust approach like nanoid or UUID.For better uniqueness guarantees:
- const generateID = (): string => { - return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15) - } + const generateID = (): string => { + // Use timestamp + random for better uniqueness + return `${Date.now()}-${Math.random().toString(36).substring(2, 9)}` + }
94-102: Add support for gigabyte file sizesThe function only handles up to MB. Consider adding GB support for larger files.
const formatFileSize = (size: number): string => { if (size < 1024) { return size + ' B' } else if (size < 1024 * 1024) { return (size / 1024).toFixed(2) + ' KB' - } else { + } else if (size < 1024 * 1024 * 1024) { return (size / (1024 * 1024)).toFixed(2) + ' MB' + } else { + return (size / (1024 * 1024 * 1024)).toFixed(2) + ' GB' } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (13)
packages/svgs/src/assets/file-excel.svgis excluded by!**/*.svgpackages/svgs/src/assets/file-folder.svgis excluded by!**/*.svgpackages/svgs/src/assets/file-image.svgis excluded by!**/*.svgpackages/svgs/src/assets/file-none.svgis excluded by!**/*.svgpackages/svgs/src/assets/file-other.svgis excluded by!**/*.svgpackages/svgs/src/assets/file-pdf.svgis excluded by!**/*.svgpackages/svgs/src/assets/file-ppt.svgis excluded by!**/*.svgpackages/svgs/src/assets/file-remove.svgis excluded by!**/*.svgpackages/svgs/src/assets/file-word.svgis excluded by!**/*.svgpackages/svgs/src/assets/image-loading.svgis excluded by!**/*.svgpackages/svgs/src/assets/image-warning.svgis excluded by!**/*.svgpackages/svgs/src/assets/upload-failed.svgis excluded by!**/*.svgpackages/svgs/src/assets/upload-loading.svgis excluded by!**/*.svg
📒 Files selected for processing (19)
docs/.vitepress/config.mts(1 hunks)docs/demos/attachments/basic.vue(1 hunks)docs/demos/attachments/custom-icon.vue(1 hunks)docs/demos/attachments/picture-list.vue(1 hunks)docs/demos/attachments/status.vue(1 hunks)docs/demos/attachments/wrap.vue(1 hunks)docs/src/components/attachments.md(1 hunks)packages/components/src/attachments/components/FileCard.vue(1 hunks)packages/components/src/attachments/components/ImagePreview.vue(1 hunks)packages/components/src/attachments/components/index.ts(1 hunks)packages/components/src/attachments/composables/index.ts(1 hunks)packages/components/src/attachments/composables/useFileCard.ts(1 hunks)packages/components/src/attachments/composables/useFileType.ts(1 hunks)packages/components/src/attachments/composables/useImagePreview.ts(1 hunks)packages/components/src/attachments/composables/useListType.ts(1 hunks)packages/components/src/attachments/index.ts(1 hunks)packages/components/src/attachments/index.type.ts(1 hunks)packages/components/src/attachments/index.vue(1 hunks)packages/components/src/index.ts(3 hunks)
🧰 Additional context used
🧠 Learnings (13)
📓 Common learnings
Learnt from: gene9831
PR: opentiny/tiny-robot#123
File: packages/components/src/bubble/message/Message.vue:22-40
Timestamp: 2025-06-25T07:04:18.791Z
Learning: In the Message component (packages/components/src/bubble/message/Message.vue), the renderer resolution is intentionally not reactive to type changes - the component is designed to work with a fixed type that doesn't change after initialization.
docs/.vitepress/config.mts (1)
Learnt from: SonyLeo
PR: opentiny/tiny-robot#119
File: packages/components/src/attachments/index.less:213-213
Timestamp: 2025-06-18T09:29:47.974Z
Learning: 在 packages/components/src/attachments/index.less 中,.tr-file-card__close 的背景色使用了硬编码的 rgb(194, 194, 194),但这个UI元素(关闭按钮)将会被直接替换为图标,所以不需要抽取为CSS变量。
docs/demos/attachments/custom-icon.vue (2)
Learnt from: SonyLeo
PR: opentiny/tiny-robot#119
File: packages/components/src/attachments/index.less:213-213
Timestamp: 2025-06-18T09:29:47.974Z
Learning: 在 packages/components/src/attachments/index.less 中,.tr-file-card__close 的背景色使用了硬编码的 rgb(194, 194, 194),但这个UI元素(关闭按钮)将会被直接替换为图标,所以不需要抽取为CSS变量。
Learnt from: gene9831
PR: opentiny/tiny-robot#123
File: packages/components/src/bubble/message/Message.vue:22-40
Timestamp: 2025-06-25T07:04:18.791Z
Learning: In the Message component (packages/components/src/bubble/message/Message.vue), the renderer resolution is intentionally not reactive to type changes - the component is designed to work with a fixed type that doesn't change after initialization.
docs/demos/attachments/basic.vue (2)
Learnt from: gene9831
PR: opentiny/tiny-robot#123
File: packages/components/src/bubble/message/Message.vue:22-40
Timestamp: 2025-06-25T07:04:18.791Z
Learning: In the Message component (packages/components/src/bubble/message/Message.vue), the renderer resolution is intentionally not reactive to type changes - the component is designed to work with a fixed type that doesn't change after initialization.
Learnt from: SonyLeo
PR: opentiny/tiny-robot#119
File: packages/components/src/attachments/index.less:213-213
Timestamp: 2025-06-18T09:29:47.974Z
Learning: 在 packages/components/src/attachments/index.less 中,.tr-file-card__close 的背景色使用了硬编码的 rgb(194, 194, 194),但这个UI元素(关闭按钮)将会被直接替换为图标,所以不需要抽取为CSS变量。
docs/demos/attachments/picture-list.vue (2)
Learnt from: gene9831
PR: opentiny/tiny-robot#123
File: packages/components/src/bubble/message/Message.vue:22-40
Timestamp: 2025-06-25T07:04:18.791Z
Learning: In the Message component (packages/components/src/bubble/message/Message.vue), the renderer resolution is intentionally not reactive to type changes - the component is designed to work with a fixed type that doesn't change after initialization.
Learnt from: SonyLeo
PR: opentiny/tiny-robot#119
File: packages/components/src/attachments/index.less:213-213
Timestamp: 2025-06-18T09:29:47.974Z
Learning: 在 packages/components/src/attachments/index.less 中,.tr-file-card__close 的背景色使用了硬编码的 rgb(194, 194, 194),但这个UI元素(关闭按钮)将会被直接替换为图标,所以不需要抽取为CSS变量。
packages/components/src/attachments/composables/useFileCard.ts (1)
Learnt from: SonyLeo
PR: opentiny/tiny-robot#119
File: packages/components/src/attachments/index.less:213-213
Timestamp: 2025-06-18T09:29:47.974Z
Learning: 在 packages/components/src/attachments/index.less 中,.tr-file-card__close 的背景色使用了硬编码的 rgb(194, 194, 194),但这个UI元素(关闭按钮)将会被直接替换为图标,所以不需要抽取为CSS变量。
docs/demos/attachments/status.vue (2)
Learnt from: gene9831
PR: opentiny/tiny-robot#123
File: packages/components/src/bubble/message/Message.vue:22-40
Timestamp: 2025-06-25T07:04:18.791Z
Learning: In the Message component (packages/components/src/bubble/message/Message.vue), the renderer resolution is intentionally not reactive to type changes - the component is designed to work with a fixed type that doesn't change after initialization.
Learnt from: SonyLeo
PR: opentiny/tiny-robot#119
File: packages/components/src/attachments/index.less:213-213
Timestamp: 2025-06-18T09:29:47.974Z
Learning: 在 packages/components/src/attachments/index.less 中,.tr-file-card__close 的背景色使用了硬编码的 rgb(194, 194, 194),但这个UI元素(关闭按钮)将会被直接替换为图标,所以不需要抽取为CSS变量。
docs/src/components/attachments.md (1)
Learnt from: SonyLeo
PR: opentiny/tiny-robot#119
File: packages/components/src/attachments/index.less:213-213
Timestamp: 2025-06-18T09:29:47.974Z
Learning: 在 packages/components/src/attachments/index.less 中,.tr-file-card__close 的背景色使用了硬编码的 rgb(194, 194, 194),但这个UI元素(关闭按钮)将会被直接替换为图标,所以不需要抽取为CSS变量。
packages/components/src/attachments/components/ImagePreview.vue (1)
Learnt from: SonyLeo
PR: opentiny/tiny-robot#119
File: packages/components/src/attachments/index.less:213-213
Timestamp: 2025-06-18T09:29:47.974Z
Learning: 在 packages/components/src/attachments/index.less 中,.tr-file-card__close 的背景色使用了硬编码的 rgb(194, 194, 194),但这个UI元素(关闭按钮)将会被直接替换为图标,所以不需要抽取为CSS变量。
packages/components/src/attachments/index.vue (2)
Learnt from: gene9831
PR: opentiny/tiny-robot#123
File: packages/components/src/bubble/message/Message.vue:22-40
Timestamp: 2025-06-25T07:04:18.791Z
Learning: In the Message component (packages/components/src/bubble/message/Message.vue), the renderer resolution is intentionally not reactive to type changes - the component is designed to work with a fixed type that doesn't change after initialization.
Learnt from: SonyLeo
PR: opentiny/tiny-robot#119
File: packages/components/src/attachments/index.less:213-213
Timestamp: 2025-06-18T09:29:47.974Z
Learning: 在 packages/components/src/attachments/index.less 中,.tr-file-card__close 的背景色使用了硬编码的 rgb(194, 194, 194),但这个UI元素(关闭按钮)将会被直接替换为图标,所以不需要抽取为CSS变量。
packages/components/src/attachments/composables/useFileType.ts (1)
Learnt from: SonyLeo
PR: opentiny/tiny-robot#119
File: packages/components/src/attachments/index.less:213-213
Timestamp: 2025-06-18T09:29:47.974Z
Learning: 在 packages/components/src/attachments/index.less 中,.tr-file-card__close 的背景色使用了硬编码的 rgb(194, 194, 194),但这个UI元素(关闭按钮)将会被直接替换为图标,所以不需要抽取为CSS变量。
packages/components/src/attachments/components/FileCard.vue (1)
Learnt from: SonyLeo
PR: opentiny/tiny-robot#119
File: packages/components/src/attachments/index.less:213-213
Timestamp: 2025-06-18T09:29:47.974Z
Learning: 在 packages/components/src/attachments/index.less 中,.tr-file-card__close 的背景色使用了硬编码的 rgb(194, 194, 194),但这个UI元素(关闭按钮)将会被直接替换为图标,所以不需要抽取为CSS变量。
packages/components/src/attachments/index.type.ts (1)
Learnt from: SonyLeo
PR: opentiny/tiny-robot#119
File: packages/components/src/attachments/index.less:213-213
Timestamp: 2025-06-18T09:29:47.974Z
Learning: 在 packages/components/src/attachments/index.less 中,.tr-file-card__close 的背景色使用了硬编码的 rgb(194, 194, 194),但这个UI元素(关闭按钮)将会被直接替换为图标,所以不需要抽取为CSS变量。
🧬 Code Graph Analysis (4)
packages/components/src/attachments/index.ts (1)
packages/components/src/index.ts (3)
Attachments(59-59)Attachments(60-60)install(50-55)
packages/components/src/attachments/composables/useListType.ts (2)
packages/components/src/attachments/composables/index.ts (1)
useListType(6-6)packages/components/src/attachments/index.type.ts (2)
Attachment(11-20)DisplayVariant(9-9)
packages/components/src/attachments/composables/useImagePreview.ts (2)
packages/components/src/attachments/index.type.ts (2)
Attachment(11-20)AttachmentListEmits(45-52)packages/components/src/attachments/composables/index.ts (1)
useImagePreview(6-6)
packages/components/src/attachments/composables/useFileType.ts (2)
packages/components/src/attachments/index.type.ts (1)
FileType(3-3)packages/components/src/attachments/composables/index.ts (1)
useFileType(6-6)
🪛 markdownlint-cli2 (0.17.2)
docs/src/components/attachments.md
9-9: Heading levels should only increment by one level at a time
Expected: h3; Actual: h4
(MD001, heading-increment)
31-31: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
🔇 Additional comments (23)
docs/.vitepress/config.mts (1)
67-67: LGTM! Clean integration with existing sidebar structure.The new attachments component entry follows the established pattern with bilingual text and consistent formatting.
packages/components/src/index.ts (2)
4-4: LGTM! Consistent component import pattern.The import follows the established convention for component imports in this library.
31-31: LGTM! Proper component registration.The component is correctly added to the components array for automatic registration.
packages/components/src/attachments/components/index.ts (1)
1-2: LGTM! Clean barrel export pattern.The component exports follow the standard pattern for creating a consolidated entry point for related components.
packages/components/src/attachments/composables/index.ts (1)
1-6: LGTM! Well-organized composables barrel export.The composables are properly imported and re-exported, providing a clean single entry point for related functionality.
packages/components/src/attachments/index.ts (4)
1-2: LGTM! Standard Vue plugin imports.The imports follow the established pattern for Vue component plugin setup.
4-4: LGTM! Consistent naming convention.The component name
TrAttachmentsfollows the established naming pattern used by other components in the library.
6-8: LGTM! Standard Vue plugin install function.The install function properly registers the component globally using the component's name property.
10-12: LGTM! Correct plugin setup and TypeScript typing.The install method is properly attached to the component, and the TypeScript type assertion correctly extends the component type with the install method.
packages/components/src/attachments/composables/useListType.ts (2)
10-33: Well-implemented auto-detection logic.The composable correctly handles the three scenarios:
- Explicit list type specification (non-auto)
- Empty file list fallback to card mode
- Automatic detection based on file content
The computed property ensures reactivity when the file list changes.
23-23: Image detection logic validated.
- Confirmed in packages/components/src/attachments/index.type.ts that FileType is defined as
'image' | 'pdf' | 'word' | 'excel' | 'ppt' | 'folder' | 'other'.- Checking
file.fileType === 'image'for picture mode aligns with this enum and intended behavior.No changes required.
docs/demos/attachments/status.vue (2)
52-52: External CDN URL used for demo preview.The demo uses an external CDN URL for the preview image. This is acceptable for demonstration purposes, but ensure this URL remains accessible or consider using a more stable demo asset.
21-43: Good coverage of different file statuses.The demo effectively demonstrates various file statuses (success, uploading, error) which helps users understand how the component handles different states and the retry functionality.
docs/demos/attachments/wrap.vue (2)
27-27: Placeholder preview URLs are acceptable for demo purposes.The demo uses placeholder paths like
/path/to/preview.docxwhich won't work in practice, but this is acceptable for demonstration purposes since the focus is on the wrap functionality rather than actual file preview.Also applies to: 35-35, 43-43, 51-51, 59-59
4-7: Clear and intuitive wrap toggle control.The radio group provides an intuitive way to demonstrate the wrap functionality with clear labels.
packages/components/src/attachments/composables/useFileCard.ts (2)
9-17: LGTM - Clean download implementation.The download trigger function correctly creates a temporary link element and handles the download process with proper cleanup.
99-104: Excellent memory management with blob URL cleanup.The automatic cleanup of blob URLs on component unmount prevents memory leaks, which is crucial for file handling components.
docs/demos/attachments/picture-list.vue (1)
1-27: Well-structured demo showcasing component features.The demo effectively demonstrates the different display modes and configurations of the attachments component with clear explanations.
packages/components/src/attachments/composables/useImagePreview.ts (1)
73-83: Clean implementation of the render function.The conditional rendering using Vue's
hfunction is well-implemented and handles the preview component lifecycle correctly.docs/src/components/attachments.md (2)
33-38: Excellent comprehensive status mode documentation.The table clearly explains the different status modes and their behaviors, which will be very helpful for users understanding the component.
44-89: Comprehensive API documentation.The props, events, and type definitions are well-documented with clear explanations and examples.
packages/components/src/attachments/index.vue (2)
76-93: Clean template structure with proper event handling.The template effectively renders the file cards with all necessary props and event handlers. The conditional rendering and event binding are well-structured.
100-133: Well-organized styles with flexible layout options.The CSS provides good support for both wrapping and non-wrapping layouts with proper scrolling behavior.
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (1)
docs/src/components/attachments.md (1)
31-31: Convert emphasized text to proper heading.
🧹 Nitpick comments (4)
packages/components/src/attachments/components/FileCard.vue (3)
73-73: Add accessibility improvements for the close button.The close button has basic
aria-labelbut could benefit from additional accessibility enhancements for better screen reader experience.Consider enhancing the accessibility:
- <button v-if="!disabled" class="tr-file-card__close-btn" @click.stop="handleRemove" aria-label="移除文件"> + <button + v-if="!disabled" + class="tr-file-card__close-btn" + @click.stop="handleRemove" + aria-label="移除文件" + type="button" + :aria-describedby="`file-${file.id}-name`" + >And add an id to the file name:
- <div class="tr-file-card__name" :title="file.name"> + <div class="tr-file-card__name" :title="file.name" :id="`file-${file.id}-name`">
94-98: Consider extracting loading text logic to computed property.The conditional loading text rendering could be more maintainable if extracted to a computed property, especially since this pattern might be reused.
+const loadingDisplayText = computed(() => { + return isUploading.value && props.file.uploadTimeoutText + ? props.file.uploadTimeoutText + : null +}) // In template: - <span v-if="file.uploadTimeoutText" class="tr-file-card__status-icon--loading-text"> - {{ file.uploadTimeoutText }} - </span> + <span v-if="loadingDisplayText" class="tr-file-card__status-icon--loading-text"> + {{ loadingDisplayText }} + </span>
243-265: Potential CSS specificity issues with nested selectors.The close button visibility logic uses nested selectors that might cause specificity conflicts. Consider using CSS custom properties or data attributes for cleaner state management.
Alternative approach using data attributes:
&__close-btn { position: absolute; top: -8px; right: -8px; z-index: 20; border: none; background: transparent; cursor: pointer; border-radius: 50%; transition: opacity 0.2s; opacity: 0; - // 悬浮时显示 - .tr-file-card:hover &, - .tr-file-card--picture:hover & { - opacity: 1; - } - - // 成功状态始终显示 - .tr-file-card--success & { - opacity: 1; - } + [data-card-state="hover"] &, + [data-card-state="success"] & { + opacity: 1; + } }Then add data attributes to the main div in the template.
docs/src/components/attachments.md (1)
83-88: Event documentation could be more detailed.The events table lacks important details about when events are triggered and what developers should expect.
Consider expanding the event descriptions:
| 事件名 | 参数类型 | 说明 | | ------------- | -------------------------------------------- | ------------------------ | -| update:items | `Attachment[]` | 附件列表更新时触发 | -| file-remove | `Attachment` | 文件被移除时触发 | -| file-download | `Attachment` | 文件下载时触发 | -| file-preview | `Attachment` | 文件预览时触发 | -| file-retry | `Attachment` | 文件重试上传时触发 | -| action | `{ action: ActionButton, file: Attachment }` | 自定义操作按钮点击时触发 | +| update:items | `Attachment[]` | 附件列表更新时触发(用于v-model双向绑定) | +| file-remove | `Attachment` | 用户点击移除按钮时触发 | +| file-download | `Attachment` | 用户点击下载操作时触发 | +| file-preview | `Attachment` | 用户点击预览操作时触发(仅图片类型) | +| file-retry | `Attachment` | 用户点击重试按钮时触发(仅error状态) | +| action | `{ action: ActionButton, file: Attachment }` | 自定义操作按钮点击时触发(statusMode='actions'时) |
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
docs/src/components/attachments.md(1 hunks)packages/components/src/attachments/components/FileCard.vue(1 hunks)packages/components/src/attachments/composables/useFileType.ts(1 hunks)packages/components/src/attachments/index.vue(1 hunks)packages/components/src/index.ts(4 hunks)
✅ Files skipped from review due to trivial changes (1)
- packages/components/src/attachments/index.vue
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/components/src/index.ts
- packages/components/src/attachments/composables/useFileType.ts
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: gene9831
PR: opentiny/tiny-robot#123
File: packages/components/src/bubble/message/Message.vue:22-40
Timestamp: 2025-06-25T07:04:18.791Z
Learning: In the Message component (packages/components/src/bubble/message/Message.vue), the renderer resolution is intentionally not reactive to type changes - the component is designed to work with a fixed type that doesn't change after initialization.
packages/components/src/attachments/components/FileCard.vue (2)
Learnt from: SonyLeo
PR: opentiny/tiny-robot#119
File: packages/components/src/attachments/index.less:213-213
Timestamp: 2025-06-18T09:29:47.974Z
Learning: 在 packages/components/src/attachments/index.less 中,.tr-file-card__close 的背景色使用了硬编码的 rgb(194, 194, 194),但这个UI元素(关闭按钮)将会被直接替换为图标,所以不需要抽取为CSS变量。
Learnt from: gene9831
PR: opentiny/tiny-robot#59
File: packages/components/src/suggestion-popover/index.vue:131-133
Timestamp: 2025-05-27T03:35:11.008Z
Learning: In the SuggestionPopover component (packages/components/src/suggestion-popover/index.vue), the click handler can be bound unconditionally because the `show` computed property has a custom setter that prevents state mutations when `props.trigger === 'manual'`. This design centralizes trigger mode logic in the computed property rather than requiring conditional checks in event handlers.
docs/src/components/attachments.md (1)
Learnt from: SonyLeo
PR: opentiny/tiny-robot#119
File: packages/components/src/attachments/index.less:213-213
Timestamp: 2025-06-18T09:29:47.974Z
Learning: 在 packages/components/src/attachments/index.less 中,.tr-file-card__close 的背景色使用了硬编码的 rgb(194, 194, 194),但这个UI元素(关闭按钮)将会被直接替换为图标,所以不需要抽取为CSS变量。
🪛 markdownlint-cli2 (0.17.2)
docs/src/components/attachments.md
31-31: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
docs/src/components/attachments.md (1)
31-31: Convert the emphasised text to a proper heading.
The line is still formatted as bold text, which breaks the document’s heading hierarchy and fails MD036 again.-**文件卡片状态显示模式** +### 文件卡片状态显示模式
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
packages/svgs/src/assets/arrow-left.svgis excluded by!**/*.svgpackages/svgs/src/assets/arrow-right.svgis excluded by!**/*.svg
📒 Files selected for processing (2)
docs/src/components/attachments.md(1 hunks)packages/components/src/attachments/components/ImagePreview.vue(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/components/src/attachments/components/ImagePreview.vue
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: gene9831
PR: opentiny/tiny-robot#123
File: packages/components/src/bubble/message/Message.vue:22-40
Timestamp: 2025-06-25T07:04:18.791Z
Learning: In the Message component (packages/components/src/bubble/message/Message.vue), the renderer resolution is intentionally not reactive to type changes - the component is designed to work with a fixed type that doesn't change after initialization.
docs/src/components/attachments.md (1)
Learnt from: SonyLeo
PR: opentiny/tiny-robot#119
File: packages/components/src/attachments/index.less:213-213
Timestamp: 2025-06-18T09:29:47.974Z
Learning: 在 packages/components/src/attachments/index.less 中,.tr-file-card__close 的背景色使用了硬编码的 rgb(194, 194, 194),但这个UI元素(关闭按钮)将会被直接替换为图标,所以不需要抽取为CSS变量。
🪛 markdownlint-cli2 (0.17.2)
docs/src/components/attachments.md
31-31: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (1)
packages/components/src/attachments/composables/useFileType.ts (1)
161-166: Memory leak: Blob URLs are not cleaned up.The
createPreviewUrlfunction creates blob URLs that persist in memory until explicitly revoked, which can cause memory leaks when previewing many images.This is a known issue from previous reviews. The consuming component should handle cleanup. Consider documenting this requirement:
/** * 创建文件预览URL + * @returns 预览URL (需要在组件卸载时调用 URL.revokeObjectURL 清理) */ const createPreviewUrl = (file: File): string => { if (file.type?.startsWith('image/')) { return URL.createObjectURL(file) } return '' }
🧹 Nitpick comments (10)
docs/demos/attachments/custom-download.vue (2)
64-64: Consider using a more user-friendly error handling approach.Using
alert()for error messages is not ideal for user experience in modern web applications.Consider using a toast notification or inline error message instead:
- alert(`下载失败: ${error.message}`) + console.error('下载失败:', error) + // TODO: Replace with proper error notification system + // Example: showToast({ type: 'error', message: `下载失败: ${error.message}` })
23-23: Consider adding comments about mock URLs.The hard-coded URLs might confuse users about real implementation.
- previewUrl: 'https://secure-api.example.com/files/secret-doc.pdf', + previewUrl: 'https://secure-api.example.com/files/secret-doc.pdf', // 仅供演示,实际使用时替换为真实APIAlso applies to: 31-31
docs/demos/attachments/custom-file-type.vue (2)
22-24: Move icon creation outside setup for better performance.Creating icons with
h()function in setup can be inefficient as they're recreated on each component mount.Consider defining icons as constants outside the component:
+const TextIcon = h('div', { style: { color: '#52c41a', fontSize: '20px' } }, '📄') +const MDIcon = h('div', { style: { color: '#1890ff', fontSize: '20px' } }, '📝') +const JsonIcon = h('div', { style: { color: '#fa8c16', fontSize: '20px' } }, '📊') + <script setup lang="ts"> import { ref, h } from 'vue' import { TrAttachments } from '@opentiny/tiny-robot' import type { Attachment, FileTypeMatcher } from '@opentiny/tiny-robot' -// 自定义图标组件 -const TextIcon = h('div', { style: { color: '#52c41a', fontSize: '20px' } }, '📄') -const MDIcon = h('div', { style: { color: '#1890ff', fontSize: '20px' } }, '📝') -const JsonIcon = h('div', { style: { color: '#fa8c16', fontSize: '20px' } }, '📊')
93-95: Add file validation before processing.The code doesn't validate the selected file before processing it.
if (files && files.length > 0) { const file = files[0] + + // 验证文件大小和类型 + if (file.size > 10 * 1024 * 1024) { // 10MB limit + alert('文件大小不能超过10MB') + return + }docs/src/components/attachments.md (1)
80-80: Improve description clarity for actions prop.The current description could be clearer about when and how the actions prop works.
-| actions | `ActionButton[]` | 图片默认: `['preview', 'download']` | 自定义操作按钮,详见 [自定义操作按钮](#自定义操作按钮-actions)。 | +| actions | `ActionButton[]` | 图片默认提供预览和下载按钮 | 自定义操作按钮列表,详见 [自定义操作按钮](#自定义操作按钮-actions)。 |packages/components/src/attachments/composables/useFileType.ts (2)
140-142: Use a more robust ID generation method.
Math.random()is not cryptographically secure and may not guarantee uniqueness in high-concurrency scenarios.Consider using a more robust approach:
const generateID = (): string => { - return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15) + return `${Date.now()}_${Math.random().toString(36).substring(2, 15)}` }Or better yet, use crypto.randomUUID() if available:
const generateID = (): string => { + if (typeof crypto !== 'undefined' && crypto.randomUUID) { + return crypto.randomUUID() + } return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15) }
32-34: Improve file extension detection robustness.The current extension detection might fail with files that have no extension or multiple dots.
- const extension = file.split('.').pop()?.toLowerCase() || '' + const extension = file.toLowerCase().split('.').pop() || ''This ensures the entire filename is lowercased before splitting, making the detection more reliable.
packages/components/src/attachments/index.type.ts (3)
21-23: Add JSDoc documentation for DownloadHandler.The
DownloadHandlerinterface lacks documentation about its expected behavior.-// 下载处理器 +/** + * 自定义下载处理器 + * @param file 要下载的附件对象 + * @returns Promise<void> 或 void,用于处理异步或同步下载逻辑 + */ export interface DownloadHandler { (file: Attachment): Promise<void> | void }
13-18: Add comprehensive JSDoc for FileTypeMatcher.The
FileTypeMatcherinterface needs better documentation for its properties and usage.-// 文件类型匹配器 +/** + * 文件类型匹配器,用于自定义文件类型检测 + */ export interface FileTypeMatcher { + /** 文件类型标识符 */ type: string + /** 匹配函数,接收 File 对象或文件名字符串,返回是否匹配 */ matcher: (file: File | string) => boolean + /** 该文件类型对应的图标组件 */ icon?: Component - priority?: number // 优先级,数字越大优先级越高 + /** 优先级,数字越大优先级越高,默认为 0 */ + priority?: number }
25-34: Add JSDoc documentation for Attachment interface.The core
Attachmentinterface needs comprehensive documentation for all its properties.+/** + * 附件对象,描述一个文件的完整信息 + */ export interface Attachment { + /** 唯一标识符 */ id: string + /** 文件名 */ name: string + /** 文件状态 */ status: FileStatus + /** 预览URL,主要用于图片预览 */ previewUrl?: string + /** 文件类型 */ fileType?: FileType + /** 文件大小(字节) */ size?: number + /** 上传超时提示文本 */ uploadTimeoutText?: string - rawFile?: File // 原始文件对象,用于下载和预览 + /** 原始文件对象,用于下载和预览 */ + rawFile?: File }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (12)
docs/demos/attachments/basic.vue(1 hunks)docs/demos/attachments/custom-download.vue(1 hunks)docs/demos/attachments/custom-file-type.vue(1 hunks)docs/demos/attachments/picture-list.vue(1 hunks)docs/demos/attachments/status.vue(1 hunks)docs/src/components/attachments.md(1 hunks)packages/components/src/attachments/components/FileCard.vue(1 hunks)packages/components/src/attachments/composables/useFileCard.ts(1 hunks)packages/components/src/attachments/composables/useFileType.ts(1 hunks)packages/components/src/attachments/composables/useImagePreview.ts(1 hunks)packages/components/src/attachments/index.type.ts(1 hunks)packages/components/src/attachments/index.vue(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- packages/components/src/attachments/components/FileCard.vue
🚧 Files skipped from review as they are similar to previous changes (6)
- docs/demos/attachments/status.vue
- docs/demos/attachments/picture-list.vue
- packages/components/src/attachments/composables/useFileCard.ts
- packages/components/src/attachments/composables/useImagePreview.ts
- docs/demos/attachments/basic.vue
- packages/components/src/attachments/index.vue
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: gene9831
PR: opentiny/tiny-robot#123
File: packages/components/src/bubble/message/Message.vue:22-40
Timestamp: 2025-06-25T07:04:18.791Z
Learning: In the Message component (packages/components/src/bubble/message/Message.vue), the renderer resolution is intentionally not reactive to type changes - the component is designed to work with a fixed type that doesn't change after initialization.
docs/demos/attachments/custom-file-type.vue (2)
Learnt from: gene9831
PR: opentiny/tiny-robot#123
File: packages/components/src/bubble/message/Message.vue:22-40
Timestamp: 2025-06-25T07:04:18.791Z
Learning: In the Message component (packages/components/src/bubble/message/Message.vue), the renderer resolution is intentionally not reactive to type changes - the component is designed to work with a fixed type that doesn't change after initialization.
Learnt from: SonyLeo
PR: opentiny/tiny-robot#119
File: packages/components/src/attachments/index.less:213-213
Timestamp: 2025-06-18T09:29:47.974Z
Learning: 在 packages/components/src/attachments/index.less 中,.tr-file-card__close 的背景色使用了硬编码的 rgb(194, 194, 194),但这个UI元素(关闭按钮)将会被直接替换为图标,所以不需要抽取为CSS变量。
packages/components/src/attachments/composables/useFileType.ts (1)
Learnt from: SonyLeo
PR: opentiny/tiny-robot#119
File: packages/components/src/attachments/index.less:213-213
Timestamp: 2025-06-18T09:29:47.974Z
Learning: 在 packages/components/src/attachments/index.less 中,.tr-file-card__close 的背景色使用了硬编码的 rgb(194, 194, 194),但这个UI元素(关闭按钮)将会被直接替换为图标,所以不需要抽取为CSS变量。
packages/components/src/attachments/index.type.ts (1)
Learnt from: SonyLeo
PR: opentiny/tiny-robot#119
File: packages/components/src/attachments/index.less:213-213
Timestamp: 2025-06-18T09:29:47.974Z
Learning: 在 packages/components/src/attachments/index.less 中,.tr-file-card__close 的背景色使用了硬编码的 rgb(194, 194, 194),但这个UI元素(关闭按钮)将会被直接替换为图标,所以不需要抽取为CSS变量。
docs/src/components/attachments.md (1)
Learnt from: SonyLeo
PR: opentiny/tiny-robot#119
File: packages/components/src/attachments/index.less:213-213
Timestamp: 2025-06-18T09:29:47.974Z
Learning: 在 packages/components/src/attachments/index.less 中,.tr-file-card__close 的背景色使用了硬编码的 rgb(194, 194, 194),但这个UI元素(关闭按钮)将会被直接替换为图标,所以不需要抽取为CSS变量。
🧬 Code Graph Analysis (1)
packages/components/src/attachments/composables/useFileType.ts (2)
packages/components/src/attachments/index.type.ts (4)
BaseFileType(4-4)FileTypeMatcher(13-18)FileType(6-6)FileStatus(8-8)packages/components/src/attachments/composables/index.ts (1)
useFileType(6-6)
🪛 markdownlint-cli2 (0.17.2)
docs/src/components/attachments.md
79-79: Link fragments should be valid
(MD051, link-fragments)
🔇 Additional comments (2)
packages/components/src/attachments/composables/useFileType.ts (1)
171-181: LGTM: Status parameter properly addressed.The
createAttachmentsfunction now properly accepts adefaultStatusparameter, addressing the previous review comment about hardcoded status.packages/components/src/attachments/index.type.ts (1)
1-93: LGTM: Comprehensive type system addresses past feedback.The type definitions properly address previous review comments:
- Custom file type support is implemented via
FileTypeMatcherandcustomMatchers- The types are well-structured without unnecessary prefixes
- The component properly separates concerns with different interfaces for different use cases
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
docs/src/components/attachments.md (1)
81-81: Fix invalid link fragment reference.The link fragment
#展示形式-variantcontains a hyphen that makes it invalid for markdown navigation.-| variant | `'picture' \| 'card' \| 'auto'` | `'auto'` | 附件列表的展示形式,详见 [展示形式](#展示形式-variant)。 | +| variant | `'picture' \| 'card' \| 'auto'` | `'auto'` | 附件列表的展示形式,详见 [展示形式](#展示形式variant)。 |
🧹 Nitpick comments (2)
docs/demos/attachments/basic.vue (1)
22-40: Consider using more realistic file sizes in the demo.All files in the demo use the same size (3.5MB), which makes the demo less realistic. Consider varying the file sizes to better demonstrate the component's capabilities.
const infoFiles = ref<Attachment[]>([ { - size: 1024 * 1024 * 3.5, // 3.5MB + size: 1024 * 1024 * 2.1, // 2.1MB url: 'https://tinyengine-assets.obs.cn-north-4.myhuaweicloud.com/files/images/icons-250711.rar', }, ]) const pictureFiles = ref<Attachment[]>([ { - size: 1024 * 1024 * 3.5, // 3.5MB + size: 1024 * 512, // 512KB url: 'https://res.hc-cdn.com/tiny-vue-web-doc/3.23.0.20250521142915/static/images/fruit.jpg', }, { - size: 1024 * 1024 * 3.5, // 3.5MB + size: 1024 * 256, // 256KB url: 'https://res.hc-cdn.com/tiny-vue-web-doc/3.23.0.20250521142915/static/images/book.jpg', }, { - size: 1024 * 1024 * 3.5, // 3.5MB + size: 1024 * 384, // 384KB url: 'https://res.hc-cdn.com/tiny-vue-web-doc/3.23.0.20250521142915/static/images/fruit.jpg', }, ])docs/demos/attachments/download.vue (1)
25-32: Consider using varied file sizes for better demonstration.Similar to the basic demo, all files use identical sizes. Using different sizes would make the demo more realistic and showcase the size display feature better.
const attachments = ref<Attachment[]>([ { - size: 1024 * 1024 * 3.5, // 3.5MB + size: 1024 * 1024 * 1.8, // 1.8MB url: 'https://res.hc-cdn.com/tiny-vue-web-doc/3.23.0.20250521142915/static/images/fruit.jpg', }, { - size: 1024 * 1024 * 3.5, // 3.5MB + size: 1024 * 768, // 768KB url: 'https://res.hc-cdn.com/tiny-vue-web-doc/3.23.0.20250521142915/static/images/fruit.jpg', }, ])
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (14)
docs/demos/attachments/basic.vue(1 hunks)docs/demos/attachments/custom-file-type.vue(1 hunks)docs/demos/attachments/custom-icon.vue(1 hunks)docs/demos/attachments/download.vue(1 hunks)docs/demos/attachments/picture-list.vue(1 hunks)docs/demos/attachments/status.vue(1 hunks)docs/demos/attachments/wrap.vue(1 hunks)docs/src/components/attachments.md(1 hunks)packages/components/src/attachments/components/FileCard.vue(1 hunks)packages/components/src/attachments/composables/useFileCard.ts(1 hunks)packages/components/src/attachments/composables/useFileType.ts(1 hunks)packages/components/src/attachments/index.type.ts(1 hunks)packages/components/src/attachments/index.vue(1 hunks)packages/components/src/index.ts(4 hunks)
✅ Files skipped from review due to trivial changes (1)
- docs/demos/attachments/status.vue
🚧 Files skipped from review as they are similar to previous changes (9)
- packages/components/src/index.ts
- docs/demos/attachments/custom-icon.vue
- docs/demos/attachments/picture-list.vue
- docs/demos/attachments/custom-file-type.vue
- packages/components/src/attachments/composables/useFileCard.ts
- packages/components/src/attachments/index.vue
- packages/components/src/attachments/components/FileCard.vue
- docs/demos/attachments/wrap.vue
- packages/components/src/attachments/index.type.ts
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: gene9831
PR: opentiny/tiny-robot#123
File: packages/components/src/bubble/message/Message.vue:22-40
Timestamp: 2025-06-25T07:04:18.791Z
Learning: In the Message component (packages/components/src/bubble/message/Message.vue), the renderer resolution is intentionally not reactive to type changes - the component is designed to work with a fixed type that doesn't change after initialization.
docs/demos/attachments/basic.vue (1)
Learnt from: SonyLeo
PR: opentiny/tiny-robot#119
File: packages/components/src/attachments/index.less:213-213
Timestamp: 2025-06-18T09:29:47.974Z
Learning: 在 packages/components/src/attachments/index.less 中,.tr-file-card__close 的背景色使用了硬编码的 rgb(194, 194, 194),但这个UI元素(关闭按钮)将会被直接替换为图标,所以不需要抽取为CSS变量。
docs/demos/attachments/download.vue (1)
Learnt from: SonyLeo
PR: opentiny/tiny-robot#119
File: packages/components/src/attachments/index.less:213-213
Timestamp: 2025-06-18T09:29:47.974Z
Learning: 在 packages/components/src/attachments/index.less 中,.tr-file-card__close 的背景色使用了硬编码的 rgb(194, 194, 194),但这个UI元素(关闭按钮)将会被直接替换为图标,所以不需要抽取为CSS变量。
packages/components/src/attachments/composables/useFileType.ts (1)
Learnt from: SonyLeo
PR: opentiny/tiny-robot#119
File: packages/components/src/attachments/index.less:213-213
Timestamp: 2025-06-18T09:29:47.974Z
Learning: 在 packages/components/src/attachments/index.less 中,.tr-file-card__close 的背景色使用了硬编码的 rgb(194, 194, 194),但这个UI元素(关闭按钮)将会被直接替换为图标,所以不需要抽取为CSS变量。
docs/src/components/attachments.md (1)
Learnt from: SonyLeo
PR: opentiny/tiny-robot#119
File: packages/components/src/attachments/index.less:213-213
Timestamp: 2025-06-18T09:29:47.974Z
Learning: 在 packages/components/src/attachments/index.less 中,.tr-file-card__close 的背景色使用了硬编码的 rgb(194, 194, 194),但这个UI元素(关闭按钮)将会被直接替换为图标,所以不需要抽取为CSS变量。
🧬 Code Graph Analysis (1)
packages/components/src/attachments/composables/useFileType.ts (2)
packages/components/src/attachments/index.type.ts (4)
BaseFileType(4-4)FileTypeMatcher(13-17)FileType(6-6)Attachment(19-39)packages/components/src/attachments/composables/index.ts (1)
useFileType(6-6)
🪛 markdownlint-cli2 (0.17.2)
docs/src/components/attachments.md
81-81: Link fragments should be valid
(MD051, link-fragments)
🔇 Additional comments (5)
docs/demos/attachments/basic.vue (1)
1-17: Well-structured basic demo component.The component structure follows Vue 3 Composition API best practices with proper TypeScript integration and clear component usage examples.
docs/demos/attachments/download.vue (1)
36-52: Excellent file handling and event demonstration.The file input handling and download event handler are well-implemented, properly demonstrating the component's capabilities for both local and remote file management.
docs/src/components/attachments.md (1)
1-151: Comprehensive and well-structured documentation.The documentation provides thorough coverage of the Attachments component's API, usage patterns, and customization options. The examples and type definitions are clear and helpful for developers.
packages/components/src/attachments/composables/useFileType.ts (2)
193-271: Well-designed composable with comprehensive file type utilities.The composable provides a solid foundation for file type detection and attachment handling with good separation of concerns and extensibility through custom matchers and icons.
251-262: Blob URL cleanup already implementedThe
useFileCard.tscomposable creates blob URLs withURL.createObjectURLand automatically revokes them in anonUnmountedhook (lines 109–114), so there’s no memory-leak risk. No further action or documentation is needed.
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (3)
docs/src/components/attachments.md (1)
83-83: Fix invalid link fragment reference.The link fragment contains an invalid character that will prevent proper navigation.
-| variant | `'picture' \| 'card' \| 'auto'` | `'auto'` | 附件列表的展示形式,详见 [展示形式](#展示形式-variant)。 | +| variant | `'picture' \| 'card' \| 'auto'` | `'auto'` | 附件列表的展示形式,详见 [展示形式](#展示形式variant)。 |packages/components/src/attachments/components/FileCard.vue (2)
68-68: Hardcoded Chinese text needs internationalization support.The component contains hardcoded Chinese text that limits its reusability in international contexts.
Consider making these texts configurable through props or using an i18n solution:
- <div v-if="isUploadSuccess" class="tr-file-card__picture-overlay"> - <span class="tr-file-card__picture-overlay-text">预览</span> - </div> + <div v-if="isUploadSuccess" class="tr-file-card__picture-overlay"> + <span class="tr-file-card__picture-overlay-text">{{ previewText || '预览' }}</span> + </div> - <div v-else-if="isUploading" class="tr-file-card__status-uploading"> - <span class="tr-file-card__uploading-text">上传中...</span> - </div> + <div v-else-if="isUploading" class="tr-file-card__status-uploading"> + <span class="tr-file-card__uploading-text">{{ uploadingText || '上传中...' }}</span> + </div> - <div v-else-if="isUploadFailed" class="tr-file-card__status-error"> - <span class="tr-file-card__error-text">上传失败</span> - <button class="tr-file-card__retry-btn" @click="handleRetry">重试</button> + <div v-else-if="isUploadFailed" class="tr-file-card__status-error"> + <span class="tr-file-card__error-text">{{ uploadFailedText || '上传失败' }}</span> + <button class="tr-file-card__retry-btn" @click="handleRetry">{{ retryText || '重试' }}</button>Also applies to: 171-171, 176-177
148-165: Action buttons lack accessibility attributes.The action buttons need proper accessibility support for keyboard navigation and screen readers.
<div v-if="actions" class="tr-file-card__actions"> - <div v-for="(action, index) in actions" :key="index" class="tr-file-card__action-btn"> + <div v-for="(action, index) in actions" :key="index" class="tr-file-card__action-btn"> <a v-if="action.type === 'download'" class="tr-file-card__action-btn--download" :href="file.url ?? 'javascript:void(0)'" :target="file.url ? '_blank' : '_self'" + :aria-label="`${action.label} ${file.name}`" @click="handleCustomAction(action, $event)" > {{ action.label }}</a > <button v-else + type="button" :class="`tr-file-card__action-btn--${action.type}`" + :aria-label="`${action.label} ${file.name}`" + @keydown.enter="handleCustomAction(action, $event)" + @keydown.space.prevent="handleCustomAction(action, $event)" @click="handleCustomAction(action, $event)" > {{ action.label }} </button> </div> </div>
🧹 Nitpick comments (1)
docs/src/components/attachments.md (1)
138-140: Fix inconsistent event parameter type documentation.The event parameter types are inconsistently documented. Some show function signatures while others show parameter types.
-| preview | `(file: Attachment) => void` | 点击内置预览按钮时触发。 | -| retry | `(file: Attachment) => void` | 点击重试按钮时触发。 | +| preview | `Attachment` | 点击内置预览按钮时触发。 | +| retry | `Attachment` | 点击重试按钮时触发。 |
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
docs/demos/attachments/download.vue(1 hunks)docs/src/components/attachments.md(1 hunks)packages/components/src/attachments/components/FileCard.vue(1 hunks)packages/components/src/attachments/composables/useFileCard.ts(1 hunks)packages/components/src/attachments/composables/useFileType.ts(1 hunks)packages/components/src/attachments/index.type.ts(1 hunks)packages/components/src/attachments/index.vue(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- packages/components/src/attachments/composables/useFileCard.ts
- docs/demos/attachments/download.vue
- packages/components/src/attachments/index.vue
- packages/components/src/attachments/index.type.ts
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: gene9831
PR: opentiny/tiny-robot#123
File: packages/components/src/bubble/message/Message.vue:22-40
Timestamp: 2025-06-25T07:04:18.791Z
Learning: In the Message component (packages/components/src/bubble/message/Message.vue), the renderer resolution is intentionally not reactive to type changes - the component is designed to work with a fixed type that doesn't change after initialization.
packages/components/src/attachments/composables/useFileType.ts (1)
Learnt from: SonyLeo
PR: opentiny/tiny-robot#119
File: packages/components/src/attachments/index.less:213-213
Timestamp: 2025-06-18T09:29:47.974Z
Learning: 在 packages/components/src/attachments/index.less 中,.tr-file-card__close 的背景色使用了硬编码的 rgb(194, 194, 194),但这个UI元素(关闭按钮)将会被直接替换为图标,所以不需要抽取为CSS变量。
docs/src/components/attachments.md (1)
Learnt from: SonyLeo
PR: opentiny/tiny-robot#119
File: packages/components/src/attachments/index.less:213-213
Timestamp: 2025-06-18T09:29:47.974Z
Learning: 在 packages/components/src/attachments/index.less 中,.tr-file-card__close 的背景色使用了硬编码的 rgb(194, 194, 194),但这个UI元素(关闭按钮)将会被直接替换为图标,所以不需要抽取为CSS变量。
packages/components/src/attachments/components/FileCard.vue (2)
Learnt from: SonyLeo
PR: opentiny/tiny-robot#119
File: packages/components/src/attachments/index.less:213-213
Timestamp: 2025-06-18T09:29:47.974Z
Learning: 在 packages/components/src/attachments/index.less 中,.tr-file-card__close 的背景色使用了硬编码的 rgb(194, 194, 194),但这个UI元素(关闭按钮)将会被直接替换为图标,所以不需要抽取为CSS变量。
Learnt from: gene9831
PR: opentiny/tiny-robot#59
File: packages/components/src/suggestion-popover/index.vue:131-133
Timestamp: 2025-05-27T03:35:11.008Z
Learning: In the SuggestionPopover component (packages/components/src/suggestion-popover/index.vue), the click handler can be bound unconditionally because the `show` computed property has a custom setter that prevents state mutations when `props.trigger === 'manual'`. This design centralizes trigger mode logic in the computed property rather than requiring conditional checks in event handlers.
🧬 Code Graph Analysis (1)
packages/components/src/attachments/composables/useFileType.ts (2)
packages/components/src/attachments/index.type.ts (6)
BaseFileType(4-4)FileTypeMatcher(13-17)FileType(6-6)RawFileAttachment(36-40)UrlAttachment(29-33)Attachment(43-43)packages/components/src/attachments/composables/index.ts (1)
useFileType(6-6)
🪛 markdownlint-cli2 (0.17.2)
docs/src/components/attachments.md
83-83: Link fragments should be valid
(MD051, link-fragments)
🔇 Additional comments (5)
packages/components/src/attachments/composables/useFileType.ts (3)
25-99: Excellent file type detection implementation.The default matchers provide comprehensive coverage for common file types with proper dual handling of File objects and filename strings. The consistent pattern and thorough MIME type + extension matching ensures reliable file type detection.
134-156: Well-designed icon resolution with proper fallback chain.The icon component resolution follows a logical priority order and properly returns a computed reference for Vue 3 reactivity. The fallback chain ensures there's always a valid icon component.
210-264: Robust attachment normalization with good type safety.The normalization logic properly handles different input types with appropriate type guards and transformation functions. The common properties extraction and type-specific transformations create a clean API for handling various attachment sources.
packages/components/src/attachments/components/FileCard.vue (2)
8-46: Well-structured component setup with proper composable usage.The component properly leverages composables for file type detection and card interactions, uses appropriate computed properties for reactive state management, and has clean prop definitions with sensible defaults.
188-550: Comprehensive and well-organized component styles.The LESS styles provide excellent coverage for both card variants with proper hover states, transitions, and responsive behavior. The nested structure is clean and maintainable, with appropriate use of CSS variables and good visual hierarchy.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
docs/src/components/attachments.md (1)
83-83: Fix invalid link fragment reference.The link fragment
#展示形式-variantcontains an invalid character (hyphen) and will not work properly for navigation.-| variant | `'picture' \| 'card' \| 'auto'` | `'auto'` | 附件列表的展示形式,详见 [展示形式](#展示形式-variant)。 | +| variant | `'picture' \| 'card' \| 'auto'` | `'auto'` | 附件列表的展示形式,详见 [展示形式](#展示形式variant)。 |
🧹 Nitpick comments (1)
docs/demos/attachments/basic.vue (1)
20-33: Consider using more diverse sample data and local fallbacks.While the current implementation works, consider these improvements:
- URL duplication: Lines 23 and 31 use the same image URL, which doesn't showcase variety
- External dependency: All images rely on external CDN which could be unavailable during development
- Missing required fields: The Attachment objects lack
id,name, andstatusfields that appear to be required based on the documentationConsider this improved sample data:
const basicFiles = ref<Attachment[]>([ { + id: '1', + name: 'fruit.jpg', + status: 'success', size: 1024 * 1024 * 3.5, // 3.5MB url: 'https://res.hc-cdn.com/tiny-vue-web-doc/3.23.0.20250521142915/static/images/fruit.jpg', }, { + id: '2', + name: 'book.jpg', + status: 'success', size: 1024 * 1024 * 3.5, // 3.5MB url: 'https://res.hc-cdn.com/tiny-vue-web-doc/3.23.0.20250521142915/static/images/book.jpg', }, { + id: '3', + name: 'landscape.jpg', + status: 'success', - size: 1024 * 1024 * 3.5, // 3.5MB + size: 1024 * 1024 * 2.1, // 2.1MB - url: 'https://res.hc-cdn.com/tiny-vue-web-doc/3.23.0.20250521142915/static/images/fruit.jpg', + url: 'https://res.hc-cdn.com/tiny-vue-web-doc/3.23.0.20250521142915/static/images/landscape.jpg', }, ])
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
docs/demos/attachments/basic.vue(1 hunks)docs/demos/attachments/download.vue(1 hunks)docs/src/components/attachments.md(1 hunks)packages/components/src/attachments/composables/useFileCard.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/components/src/attachments/composables/useFileCard.ts
- docs/demos/attachments/download.vue
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: gene9831
PR: opentiny/tiny-robot#123
File: packages/components/src/bubble/message/Message.vue:22-40
Timestamp: 2025-06-25T07:04:18.791Z
Learning: In the Message component (packages/components/src/bubble/message/Message.vue), the renderer resolution is intentionally not reactive to type changes - the component is designed to work with a fixed type that doesn't change after initialization.
docs/demos/attachments/basic.vue (1)
Learnt from: SonyLeo
PR: opentiny/tiny-robot#119
File: packages/components/src/attachments/index.less:213-213
Timestamp: 2025-06-18T09:29:47.974Z
Learning: 在 packages/components/src/attachments/index.less 中,.tr-file-card__close 的背景色使用了硬编码的 rgb(194, 194, 194),但这个UI元素(关闭按钮)将会被直接替换为图标,所以不需要抽取为CSS变量。
docs/src/components/attachments.md (1)
Learnt from: SonyLeo
PR: opentiny/tiny-robot#119
File: packages/components/src/attachments/index.less:213-213
Timestamp: 2025-06-18T09:29:47.974Z
Learning: 在 packages/components/src/attachments/index.less 中,.tr-file-card__close 的背景色使用了硬编码的 rgb(194, 194, 194),但这个UI元素(关闭按钮)将会被直接替换为图标,所以不需要抽取为CSS变量。
🪛 markdownlint-cli2 (0.17.2)
docs/src/components/attachments.md
83-83: Link fragments should be valid
(MD051, link-fragments)
🔇 Additional comments (6)
docs/demos/attachments/basic.vue (2)
1-12: Template structure looks clean and demonstrates key variants effectively.The template clearly showcases both the 'card' variant and the default behavior of the TrAttachments component with proper semantic HTML structure.
14-17: LGTM: Proper TypeScript imports and component setup.The imports are correctly structured using the composition API and TypeScript support with proper type importing.
docs/src/components/attachments.md (4)
47-51: Clarify the local vs network file download distinction.Based on the retrieved learning from gene9831's comment, there seems to be confusion about the local vs network file download descriptions. The current explanation may be misleading.
The documentation states that local files (with
rawFile) use automatic download while network files (withurl) trigger events. However, this distinction needs clarification - typically both scenarios might need custom handling depending on the security context and user requirements.Can you verify if this download behavior accurately reflects the actual implementation in the component?
74-87: API documentation is comprehensive and well-structured.The Props table provides clear descriptions, appropriate types, and helpful cross-references to related sections. The default values and explanations are informative for developers.
94-129: TypeScript interfaces are well-defined and documented.The type definitions for
Attachment,ActionButton, andFileTypeMatcherare comprehensive with clear field descriptions and appropriate type annotations. This provides excellent developer experience with strong typing.
142-153: Built-in file types section provides good reference.The list of supported file types with their extensions gives developers a clear understanding of what's supported out-of-the-box and sets expectations for when custom matchers might be needed.
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
docs/src/components/attachments.md (1)
83-83: Fix invalid link fragment#展示形式-variant.
markdownlint(MD051) still flags this anchor as invalid.
The heading is### 展示形式(variant), whose automatic slug becomes#展示形式variant(parentheses are stripped, no dash). Update the link to keep in-page navigation working.-| variant | `'picture' \| 'card' \| 'auto'` | `'auto'` | 附件列表的展示形式,详见 [展示形式](#展示形式-variant)。 | +| variant | `'picture' \| 'card' \| 'auto'` | `'auto'` | 附件列表的展示形式,详见 [展示形式](#展示形式variant)。 |
🧹 Nitpick comments (2)
docs/src/components/attachments.md (2)
78-86: Keep the “默认值” cell pure; move explanatory text to the “说明” cell.Placing “图片默认: …” in the 默认值 column mixes default-value data with explanatory prose, reducing table clarity. Shift the note into the 说明 column and leave the 默认值 cell as `-` (or the literal default if one exists).
-| actions | `ActionButton[]` | 图片默认: `['preview', 'download']` | 自定义操作按钮,详见 [自定义操作按钮](#自定义操作按钮-actions)。 | +| actions | `ActionButton[]` | - | 自定义操作按钮(图片文件默认显示 `下载 | 预览`),详见 [自定义操作按钮](#自定义操作按钮-actions)。 |
47-52: Tighten wording and typography around download behaviour.The blockquote currently reads “本地文件 和 网络文件 的下载方式不同”,with extra spaces around 和/的. Consider rephrasing for smoother Chinese copy and to match the bullet titles below, e.g.:
-> 本地文件 和 网络文件 的下载方式不同 +> 本地文件与网络文件的下载方式不同
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
docs/src/components/attachments.md(1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: gene9831
PR: opentiny/tiny-robot#123
File: packages/components/src/bubble/message/Message.vue:22-40
Timestamp: 2025-06-25T07:04:18.791Z
Learning: In the Message component (packages/components/src/bubble/message/Message.vue), the renderer resolution is intentionally not reactive to type changes - the component is designed to work with a fixed type that doesn't change after initialization.
docs/src/components/attachments.md (1)
Learnt from: SonyLeo
PR: opentiny/tiny-robot#119
File: packages/components/src/attachments/index.less:213-213
Timestamp: 2025-06-18T09:29:47.974Z
Learning: 在 packages/components/src/attachments/index.less 中,.tr-file-card__close 的背景色使用了硬编码的 rgb(194, 194, 194),但这个UI元素(关闭按钮)将会被直接替换为图标,所以不需要抽取为CSS变量。
🪛 markdownlint-cli2 (0.17.2)
docs/src/components/attachments.md
83-83: Link fragments should be valid
(MD051, link-fragments)
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
docs/src/components/attachments.md (1)
83-85: Invalid anchor fragment – previous feedback still unresolved.The link target
#展示形式-variantis still invalid, causing broken navigation. Please update the fragment to match the actual heading slug (likely#展示形式variant).-| variant | `'picture' \| 'card' \| 'auto'` | `'auto'` | 附件列表的展示形式,详见 [展示形式](#展示形式-variant)。 | +| variant | `'picture' \| 'card' \| 'auto'` | `'auto'` | 附件列表的展示形式,详见 [展示形式](#展示形式variant)。 |
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
docs/demos/attachments/download.vue(1 hunks)docs/src/components/attachments.md(1 hunks)packages/components/src/attachments/components/FileCard.vue(1 hunks)packages/components/src/attachments/composables/useFileCard.ts(1 hunks)packages/components/src/attachments/composables/useImagePreview.ts(1 hunks)packages/components/src/attachments/index.type.ts(1 hunks)packages/components/src/attachments/index.vue(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- packages/components/src/attachments/index.type.ts
🚧 Files skipped from review as they are similar to previous changes (5)
- docs/demos/attachments/download.vue
- packages/components/src/attachments/composables/useFileCard.ts
- packages/components/src/attachments/composables/useImagePreview.ts
- packages/components/src/attachments/index.vue
- packages/components/src/attachments/components/FileCard.vue
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: gene9831
PR: opentiny/tiny-robot#123
File: packages/components/src/bubble/message/Message.vue:22-40
Timestamp: 2025-06-25T07:04:18.791Z
Learning: In the Message component (packages/components/src/bubble/message/Message.vue), the renderer resolution is intentionally not reactive to type changes - the component is designed to work with a fixed type that doesn't change after initialization.
docs/src/components/attachments.md (1)
Learnt from: SonyLeo
PR: #119
File: packages/components/src/attachments/index.less:213-213
Timestamp: 2025-06-18T09:29:47.974Z
Learning: 在 packages/components/src/attachments/index.less 中,.tr-file-card__close 的背景色使用了硬编码的 rgb(194, 194, 194),但这个UI元素(关闭按钮)将会被直接替换为图标,所以不需要抽取为CSS变量。
🪛 markdownlint-cli2 (0.17.2)
docs/src/components/attachments.md
84-84: Link fragments should be valid
(MD051, link-fragments)
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
docs/src/components/attachments.md (1)
84-84: Fix invalid link fragment for “variant” heading.The anchor
#展示形式-variantdoes not exist – GitHub’s slug for### 展示形式(variant)is#展示形式variant(no hyphen). Broken links hurt navigation.-| variant | `'picture' \| 'card' \| 'auto'` | `'auto'` | 附件列表的展示形式,详见 [展示形式](#展示形式-variant)。 | +| variant | `'picture' \| 'card' \| 'auto'` | `'auto'` | 附件列表的展示形式,详见 [展示形式](#展示形式variant)。 |
🧹 Nitpick comments (1)
docs/src/components/attachments.md (1)
47-53: Streamline the “预览和下载” note for readability.The double-space around “本地文件 和 网络文件” and split sentences make the note a bit choppy. Tightening the phrasing improves flow without changing meaning.
-> 本地文件 和 网络文件 的下载方式不同 +> 本地文件与网络文件的下载方式不同
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
docs/src/components/attachments.md(1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: gene9831
PR: opentiny/tiny-robot#123
File: packages/components/src/bubble/message/Message.vue:22-40
Timestamp: 2025-06-25T07:04:18.791Z
Learning: In the Message component (packages/components/src/bubble/message/Message.vue), the renderer resolution is intentionally not reactive to type changes - the component is designed to work with a fixed type that doesn't change after initialization.
docs/src/components/attachments.md (3)
Learnt from: SonyLeo
PR: #119
File: packages/components/src/attachments/index.less:213-213
Timestamp: 2025-06-18T09:29:47.974Z
Learning: 在 packages/components/src/attachments/index.less 中,.tr-file-card__close 的背景色使用了硬编码的 rgb(194, 194, 194),但这个UI元素(关闭按钮)将会被直接替换为图标,所以不需要抽取为CSS变量。
Learnt from: gene9831
PR: #59
File: packages/components/src/suggestion-popover/index.vue:0-0
Timestamp: 2025-05-27T03:45:56.392Z
Learning: In Vue components using VueUse's onClickOutside, calling stopPropagation() in the onClickOutside handler can prevent subsequent click event handlers on the same element from being triggered. This means that clicking a trigger element while a popover is open can successfully close the popover without immediately reopening it, even when the onClickOutside only listens to the popover element and not the trigger element.
Learnt from: gene9831
PR: #59
File: packages/components/src/suggestion-popover/index.vue:131-133
Timestamp: 2025-05-27T03:35:11.008Z
Learning: In the SuggestionPopover component (packages/components/src/suggestion-popover/index.vue), the click handler can be bound unconditionally because the show computed property has a custom setter that prevents state mutations when props.trigger === 'manual'. This design centralizes trigger mode logic in the computed property rather than requiring conditional checks in event handlers.
🪛 markdownlint-cli2 (0.17.2)
docs/src/components/attachments.md
84-84: Link fragments should be valid
(MD051, link-fragments)
Summary by CodeRabbit
New Features
Documentation
Style