diff --git a/src/components/Form/index.ts b/src/components/Form/index.ts
index eb7b797..480eade 100644
--- a/src/components/Form/index.ts
+++ b/src/components/Form/index.ts
@@ -13,5 +13,6 @@ export { default as ApiTree } from './src/components/ApiTree.vue'
 export { default as ApiRadioGroup } from './src/components/ApiRadioGroup.vue'
 export { default as ApiCascader } from './src/components/ApiCascader.vue'
 export { default as ApiTransfer } from './src/components/ApiTransfer.vue'
+export { default as ImageUpload } from './src/components/ImageUpload.vue'
 
 export { BasicForm }
diff --git a/src/components/Form/src/componentMap.ts b/src/components/Form/src/componentMap.ts
index 0ef3d28..c7c71ac 100644
--- a/src/components/Form/src/componentMap.ts
+++ b/src/components/Form/src/componentMap.ts
@@ -20,7 +20,6 @@ import type { ComponentType } from './types'
 /**
  * Component list, register here to setting it in the form
  */
-
 import ApiRadioGroup from './components/ApiRadioGroup.vue'
 import RadioButtonGroup from './components/RadioButtonGroup.vue'
 import ApiSelect from './components/ApiSelect.vue'
@@ -29,6 +28,7 @@ import ApiTreeSelect from './components/ApiTreeSelect.vue'
 import ApiCascader from './components/ApiCascader.vue'
 import ApiTransfer from './components/ApiTransfer.vue'
 import FileUpload from './components/FileUpload.vue'
+import ImageUpload from './components/ImageUpload.vue'
 import { BasicUpload } from '@/components/Upload'
 import { StrengthMeter } from '@/components/StrengthMeter'
 import { IconPicker } from '@/components/Icon'
@@ -44,7 +44,7 @@ componentMap.set('InputSearch', Input.Search)
 componentMap.set('InputTextArea', Input.TextArea)
 componentMap.set('InputNumber', InputNumber)
 componentMap.set('AutoComplete', AutoComplete)
-
+componentMap.set('ImageUpload', ImageUpload)
 componentMap.set('Select', Select)
 componentMap.set('ApiSelect', ApiSelect)
 componentMap.set('ApiTree', ApiTree)
@@ -67,6 +67,7 @@ componentMap.set('MonthPicker', DatePicker.MonthPicker)
 componentMap.set('RangePicker', DatePicker.RangePicker)
 componentMap.set('WeekPicker', DatePicker.WeekPicker)
 componentMap.set('TimePicker', TimePicker)
+componentMap.set('TimeRangePicker', TimePicker.TimeRangePicker)
 componentMap.set('StrengthMeter', StrengthMeter)
 componentMap.set('IconPicker', IconPicker)
 componentMap.set('InputCountDown', CountdownInput)
diff --git a/src/components/Form/src/components/FileUpload.vue b/src/components/Form/src/components/FileUpload.vue
index 43ec3f7..1b5c6e5 100644
--- a/src/components/Form/src/components/FileUpload.vue
+++ b/src/components/Form/src/components/FileUpload.vue
@@ -57,19 +57,15 @@ const isMaxCount = computed(() => props.maxCount > 0 && fileList.value.length >=
 const isImageMode = computed(() => props.fileType === 'image')
 // 合并 props 和 attrs
 const bindProps = computed(() => {
-  // update-begin-author:liusq date:20220411 for: [issue/455]上传组件传入accept限制上传文件类型无效
   const bind: any = Object.assign({}, props, unref(attrs))
-  // update-end-author:liusq date:20220411 for: [issue/455]上传组件传入accept限制上传文件类型无效
 
   bind.name = 'file'
   bind.listType = isImageMode.value ? 'picture-card' : 'text'
   bind.class = [bind.class, { 'upload-disabled': props.disabled }]
   bind.data = { biz: props.bizPath, ...bind.data }
-  // update-begin-author:taoyan date:20220407 for: 自定义beforeUpload return false,并不能中断上传过程
   if (!bind.beforeUpload)
     bind.beforeUpload = onBeforeUpload
 
-  // update-end-author:taoyan date:20220407 for: 自定义beforeUpload return false,并不能中断上传过程
   // 如果当前是图片上传模式,就只能上传图片
   if (isImageMode.value && !bind.accept)
     bind.accept = 'image/*'
diff --git a/src/components/Form/src/components/ImageUpload.vue b/src/components/Form/src/components/ImageUpload.vue
new file mode 100644
index 0000000..0a57d41
--- /dev/null
+++ b/src/components/Form/src/components/ImageUpload.vue
@@ -0,0 +1,223 @@
+<script lang="ts" setup>
+import type { PropType } from 'vue'
+import { reactive, ref, watch } from 'vue'
+import type { UploadProps } from 'ant-design-vue'
+import { Modal, Upload, message } from 'ant-design-vue'
+import type { UploadFile } from 'ant-design-vue/lib/upload/interface'
+import { join } from 'lodash-es'
+import { PlusOutlined } from '@ant-design/icons-vue'
+import { useI18n } from '@/hooks/web/useI18n'
+import { buildShortUUID } from '@/utils/uuid'
+import { isArray, isNotEmpty, isUrl } from '@/utils/is'
+import { useRuleFormItem } from '@/hooks/component/useFormItem'
+import { useAttrs } from '@/hooks/core/useAttrs'
+
+type ImageUploadType = 'text' | 'picture' | 'picture-card'
+
+defineOptions({ name: 'ImageUpload', inheritAttrs: false })
+
+const props = defineProps({
+  value: [Array, String],
+  api: {
+    type: Function as PropType<(file: UploadFile) => Promise<string>>,
+    default: null,
+  },
+  listType: {
+    type: String as PropType<ImageUploadType>,
+    default: () => 'picture-card',
+  },
+  // 文件类型
+  fileType: {
+    type: Array,
+    default: () => ['image/png', 'image/jpeg'],
+  },
+  multiple: {
+    type: Boolean,
+    default: () => false,
+  },
+  // 最大数量的文件
+  maxCount: {
+    type: Number,
+    default: () => 1,
+  },
+  // 最小数量的文件
+  minCount: {
+    type: Number,
+    default: () => 0,
+  },
+  // 文件最大多少MB
+  maxSize: {
+    type: Number,
+    default: () => 2,
+  },
+})
+
+const emit = defineEmits(['change', 'update:value'])
+
+const attrs = useAttrs()
+const { t } = useI18n()
+const previewOpen = ref(false)
+const previewImage = ref('')
+const emitData = ref<any[] | any | undefined>()
+const fileList = ref<UploadFile[]>([])
+// Embedded in the form, just use the hook binding to perform form verification
+const [state] = useRuleFormItem(props, 'value', 'change', emitData)
+const fileState = reactive<{
+  newList: any[]
+  newStr: string
+  oldStr: string
+}>({
+  newList: [],
+  newStr: '',
+  oldStr: '',
+})
+watch(
+  () => fileList.value,
+  (v) => {
+    fileState.newList = v
+      .filter((item: any) => {
+        return item?.url && item.status === 'done' && isUrl(item?.url)
+      })
+      .map((item: any) => item?.url)
+    fileState.newStr = join(fileState.newList)
+    // 不相等代表数据变更
+    if (fileState.newStr !== fileState.oldStr) {
+      fileState.oldStr = fileState.newStr
+      emitData.value = props.multiple ? fileState.newList : fileState.newStr
+      state.value = props.multiple ? fileState.newList : fileState.newStr
+    }
+  },
+  {
+    deep: true,
+  },
+)
+watch(
+  () => state.value,
+  (v) => {
+    changeFileValue(v)
+    emit('update:value', v)
+  },
+)
+function changeFileValue(value: any) {
+  const stateStr = props.multiple ? join((value as string[]) || []) : value || ''
+  if (stateStr !== fileState.oldStr) {
+    fileState.oldStr = stateStr
+    let list: string[] = []
+    if (props.multiple) {
+      if (isNotEmpty(value)) {
+        if (isArray(value))
+          list = value as string[]
+        else
+          list.push(value as string)
+      }
+    }
+    else {
+      if (isNotEmpty(value))
+        list.push(value as string)
+    }
+    fileList.value = list.map((item) => {
+      const uuid = buildShortUUID()
+      return {
+        uid: uuid,
+        name: uuid,
+        status: 'done',
+        url: item,
+      }
+    })
+  }
+}
+/** 关闭查看 */
+function handleCancel() {
+  previewOpen.value = false
+}
+/** 查看图片 */
+async function handlePreview(file: UploadProps['fileList'][number]) {
+  if (!file.url && !file.preview)
+    file.preview = (await getBase64(file.originFileObj)) as string
+
+  previewImage.value = file.url || file.preview
+  previewOpen.value = true
+}
+/** 上传前校验 */
+const handleBeforeUpload: UploadProps['beforeUpload'] = (file) => {
+  if (fileList.value.length > props.maxCount) {
+    fileList.value.splice(props.maxCount, fileList.value.length - props.maxCount)
+    message.error(t('component.upload.maxNumber', [props.maxCount]))
+    return Upload.LIST_IGNORE
+  }
+  const isPNG = props.fileType.includes(file.type)
+  if (!isPNG)
+    message.error(t('component.upload.acceptUpload', [props.fileType.toString()]))
+
+  const isLt2M = file.size / 1024 / 1024 < props.maxSize
+  if (!isLt2M)
+    message.error(t('component.upload.maxSizeMultiple', [props.maxSize]))
+
+  if (!(isPNG && isLt2M))
+    fileList.value.pop()
+
+  return (isPNG && isLt2M) || Upload.LIST_IGNORE
+}
+/** 自定义上传 */
+async function handleCustomRequest(option: any) {
+  const { file } = option
+  await props
+    .api(option)
+    .then((url) => {
+      file.url = url
+      file.status = 'done'
+      fileList.value.pop()
+      fileList.value.push(file)
+    })
+    .catch(() => {
+      fileList.value.pop()
+    })
+}
+function getBase64(file: File) {
+  return new Promise((resolve, reject) => {
+    const reader = new FileReader()
+    reader.readAsDataURL(file)
+    reader.onload = () => resolve(reader.result)
+    reader.onerror = error => reject(error)
+  })
+}
+</script>
+
+<template>
+  <div class="clearfix">
+    <Upload
+      v-model:file-list="fileList"
+      v-bind="attrs"
+      v-model:value="state"
+      :list-type="listType"
+      :multiple="multiple"
+      :max-count="maxCount"
+      :custom-request="handleCustomRequest"
+      :before-upload="handleBeforeUpload"
+      @preview="handlePreview"
+    >
+      <div v-if="fileList.length < maxCount">
+        <PlusOutlined />
+        <div style="margin-top: 8px">
+          {{ t('component.upload.upload') }}
+        </div>
+      </div>
+    </Upload>
+    <Modal :open="previewOpen" :footer="null" @cancel="handleCancel">
+      <img alt="example" style="width: 100%" :src="previewImage">
+    </Modal>
+  </div>
+</template>
+
+<style scoped>
+  /* you can make up upload button and sample style by using stylesheets */
+  .ant-upload-select-picture-card i {
+    font-size: 32px;
+    color: #999;
+  }
+
+  .ant-upload-select-picture-card .ant-upload-text {
+    margin-top: 8px;
+    color: #666;
+  }
+</style>
diff --git a/src/components/Form/src/types/index.ts b/src/components/Form/src/types/index.ts
index dbf21c8..5bc05cf 100644
--- a/src/components/Form/src/types/index.ts
+++ b/src/components/Form/src/types/index.ts
@@ -106,9 +106,11 @@ export type ComponentType =
   | 'RangePicker'
   | 'WeekPicker'
   | 'TimePicker'
+  | 'TimeRangePicker'
   | 'Switch'
   | 'StrengthMeter'
   | 'Upload'
+  | 'ImageUpload'
   | 'IconPicker'
   | 'Render'
   | 'Slider'