瀏覽代碼

feat: add examples: form-upload (#5955)

* feat: add examples: form-upload

* fix: upload: accept and label

* fix: upload: 设置表单值、图片预览
yuh 3 周之前
父節點
當前提交
4baec83db5

+ 13 - 0
apps/backend-mock/api/upload.ts

@@ -0,0 +1,13 @@
+import { verifyAccessToken } from '~/utils/jwt-utils';
+import { unAuthorizedResponse } from '~/utils/response';
+
+export default eventHandler((event) => {
+  const userinfo = verifyAccessToken(event);
+  if (!userinfo) {
+    return unAuthorizedResponse(event);
+  }
+  return useResponseSuccess({
+    url: 'https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp',
+  });
+  // return useResponseError("test")
+});

+ 1 - 0
apps/backend-mock/routes/[...].ts

@@ -7,6 +7,7 @@ export default defineEventHandler(() => {
 <li><a href="/api/menu">/api/menu/all</a></li>
 <li><a href="/api/auth/codes">/api/auth/codes</a></li>
 <li><a href="/api/auth/login">/api/auth/login</a></li>
+<li><a href="/api/upload">/api/upload</a></li>
 </ul>
 `;
 });

+ 25 - 0
playground/src/api/examples/upload.ts

@@ -0,0 +1,25 @@
+import { requestClient } from '#/api/request';
+
+interface UploadFileParams {
+  file: File;
+  onError?: (error: Error) => void;
+  onProgress?: (progress: { percent: number }) => void;
+  onSuccess?: (data: any, file: File) => void;
+}
+export async function upload_file({
+  file,
+  onError,
+  onProgress,
+  onSuccess,
+}: UploadFileParams) {
+  try {
+    onProgress?.({ percent: 0 });
+
+    const data = await requestClient.upload('/upload', { file });
+
+    onProgress?.({ percent: 100 });
+    onSuccess?.(data, file);
+  } catch (error) {
+    onError?.(error instanceof Error ? error : new Error(String(error)));
+  }
+}

+ 5 - 1
playground/src/locales/langs/en-US/examples.json

@@ -18,7 +18,11 @@
     "dynamic": "Dynamic Form",
     "custom": "Custom Component",
     "api": "Api",
-    "merge": "Merge Form"
+    "merge": "Merge Form",
+    "upload-error": "Partial file upload failed",
+    "upload-urls": "Urls after file upload",
+    "file": "file",
+    "upload-image": "Click to upload image"
   },
   "vxeTable": {
     "title": "Vxe Table",

+ 5 - 1
playground/src/locales/langs/zh-CN/examples.json

@@ -21,7 +21,11 @@
     "dynamic": "动态表单",
     "custom": "自定义组件",
     "api": "Api",
-    "merge": "合并表单"
+    "merge": "合并表单",
+    "upload-error": "部分文件上传失败",
+    "upload-urls": "文件上传后的网址",
+    "file": "文件",
+    "upload-image": "点击上传图片"
   },
   "vxeTable": {
     "title": "Vxe 表格",

+ 57 - 1
playground/src/views/examples/form/basic.vue

@@ -1,5 +1,7 @@
 <script lang="ts" setup>
-import { h, ref } from 'vue';
+import type { UploadFile } from 'ant-design-vue';
+
+import { h, ref, toRaw } from 'vue';
 
 import { Page } from '@vben/common-ui';
 
@@ -9,6 +11,8 @@ import dayjs from 'dayjs';
 
 import { useVbenForm, z } from '#/adapter/form';
 import { getAllMenusApi } from '#/api';
+import { upload_file } from '#/api/examples/upload';
+import { $t } from '#/locales';
 
 import DocButton from '../doc-button.vue';
 
@@ -329,12 +333,56 @@ const [BaseForm, baseFormApi] = useVbenForm({
       fieldName: 'treeSelect',
       label: '树选择',
     },
+    {
+      component: 'Upload',
+      componentProps: {
+        // 更多属性见:https://ant.design/components/upload-cn
+        accept: '.png,.jpg,.jpeg',
+        // 自动携带认证信息
+        customRequest: upload_file,
+        disabled: false,
+        maxCount: 1,
+        multiple: false,
+        showUploadList: true,
+        // 上传列表的内建样式,支持四种基本样式 text, picture, picture-card 和 picture-circle
+        listType: 'picture-card',
+      },
+      fieldName: 'files',
+      label: $t('examples.form.file'),
+      renderComponentContent: () => {
+        return {
+          default: () => $t('examples.form.upload-image'),
+        };
+      },
+      rules: 'required',
+    },
   ],
   // 大屏一行显示3个,中屏一行显示2个,小屏一行显示1个
   wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3',
 });
 
 function onSubmit(values: Record<string, any>) {
+  const files = toRaw(values.files) as UploadFile[];
+  const doneFiles = files.filter((file) => file.status === 'done');
+  const failedFiles = files.filter((file) => file.status !== 'done');
+
+  const msg = [
+    ...doneFiles.map((file) => file.response?.url || file.url),
+    ...failedFiles.map((file) => file.name),
+  ].join(', ');
+
+  if (failedFiles.length === 0) {
+    message.success({
+      content: `${$t('examples.form.upload-urls')}: ${msg}`,
+    });
+  } else {
+    message.error({
+      content: `${$t('examples.form.upload-error')}: ${msg}`,
+    });
+    return;
+  }
+  // 如果需要可提交前替换为需要的urls
+  values.files = doneFiles.map((file) => file.response?.url || file.url);
   message.success({
     content: `form values: ${JSON.stringify(values)}`,
   });
@@ -347,6 +395,14 @@ function handleSetFormValue() {
   baseFormApi.setValues({
     checkboxGroup: ['1'],
     datePicker: dayjs('2022-01-01'),
+    files: [
+      {
+        name: 'example.png',
+        status: 'done',
+        uid: '-1',
+        url: 'https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp',
+      },
+    ],
     mentions: '@afc163',
     number: 3,
     options: '1',