diff --git a/apps/web-naive/src/components/cropper/cropper-avatar.vue b/apps/web-naive/src/components/cropper/cropper-avatar.vue
new file mode 100644
index 00000000..de4ed8c6
--- /dev/null
+++ b/apps/web-naive/src/components/cropper/cropper-avatar.vue
@@ -0,0 +1,159 @@
+
+
+
+
+
+
+
+
+
![avatar]()
+
+
+ {{ btnText ? btnText : $t('ui.cropper.selectImage') }}
+
+
+
+
+
+
+
diff --git a/apps/web-naive/src/components/cropper/cropper-modal.vue b/apps/web-naive/src/components/cropper/cropper-modal.vue
new file mode 100644
index 00000000..d14bf71f
--- /dev/null
+++ b/apps/web-naive/src/components/cropper/cropper-modal.vue
@@ -0,0 +1,350 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
![]()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/web-naive/src/components/cropper/cropper.vue b/apps/web-naive/src/components/cropper/cropper.vue
new file mode 100644
index 00000000..c2d62755
--- /dev/null
+++ b/apps/web-naive/src/components/cropper/cropper.vue
@@ -0,0 +1,173 @@
+
+
+
+
+
![]()
+
+
+
+
diff --git a/apps/web-naive/src/components/cropper/index.ts b/apps/web-naive/src/components/cropper/index.ts
new file mode 100644
index 00000000..43fd89ff
--- /dev/null
+++ b/apps/web-naive/src/components/cropper/index.ts
@@ -0,0 +1,3 @@
+export { default as CropperAvatar } from './cropper-avatar.vue';
+export { default as CropperImage } from './cropper.vue';
+export type { CropperType } from './typing';
diff --git a/apps/web-naive/src/components/cropper/typing.ts b/apps/web-naive/src/components/cropper/typing.ts
new file mode 100644
index 00000000..6683c34a
--- /dev/null
+++ b/apps/web-naive/src/components/cropper/typing.ts
@@ -0,0 +1,68 @@
+import type Cropper from 'cropperjs';
+import type { ButtonProps } from 'naive-ui';
+
+import type { CSSProperties } from 'vue';
+
+export interface apiFunParams {
+ file: Blob;
+ filename: string;
+ name: string;
+}
+
+export interface CropendResult {
+ imgBase64: string;
+ imgInfo: Cropper.Data;
+}
+
+export interface CropperProps {
+ src?: string;
+ alt?: string;
+ circled?: boolean;
+ realTimePreview?: boolean;
+ height?: number | string;
+ crossorigin?: '' | 'anonymous' | 'use-credentials' | undefined;
+ imageStyle?: CSSProperties;
+ options?: Cropper.Options;
+}
+
+export interface CropperAvatarProps {
+ width?: number | string;
+ value?: string;
+ showBtn?: boolean;
+ btnProps?: ButtonProps;
+ btnText?: string;
+ uploadApi?: (params: apiFunParams) => Promise;
+ size?: number;
+}
+
+export interface CropperModalProps {
+ circled?: boolean;
+ uploadApi?: (params: apiFunParams) => Promise;
+ src?: string;
+ size?: number;
+}
+
+export const defaultOptions: Cropper.Options = {
+ aspectRatio: 1,
+ zoomable: true,
+ zoomOnTouch: true,
+ zoomOnWheel: true,
+ cropBoxMovable: true,
+ cropBoxResizable: true,
+ toggleDragModeOnDblclick: true,
+ autoCrop: true,
+ background: true,
+ highlight: true,
+ center: true,
+ responsive: true,
+ restore: true,
+ checkCrossOrigin: true,
+ checkOrientation: true,
+ scalable: true,
+ modal: true,
+ guides: true,
+ movable: true,
+ rotatable: true,
+};
+
+export type { Cropper as CropperType };