DESKTOP-USV654P\pc 1 éve
szülő
commit
1c928146cf

+ 49 - 0
apps/web-baicai/src/api/system/file.ts

@@ -0,0 +1,49 @@
+import type { BasicFetchResult, BasicPageParams } from '#/api/model';
+
+import { requestClient } from '#/api/request';
+
+export namespace FileApi {
+  export interface PageParams extends BasicPageParams {
+    fileName?: string;
+    suffix?: string;
+    startTime?: string;
+    endTime?: string;
+  }
+
+  export interface BasicRecordItem {
+    path: string;
+    directory: string;
+    allowSuffix: string;
+    folderId: string;
+    file?: any;
+  }
+
+  export interface RecordItem extends BasicRecordItem {
+    id: number;
+  }
+
+  export interface ListItem {
+    id: number;
+    folderId: number;
+    url: string;
+    fileName: string;
+    filePath: string;
+    sizeKb: number;
+  }
+
+  export type PageResult = BasicFetchResult<RecordItem>;
+
+  export const getPage = (params: PageParams) =>
+    requestClient.get<PageResult>('/file/page', { params });
+
+  export const getList = (id: number) =>
+    requestClient.get<ListItem[]>('/file/list', {
+      params: { id },
+    });
+
+  export const upload = (data: BasicRecordItem) =>
+    requestClient.post('/file/upload', data);
+
+  export const deleteDetail = (id: number) =>
+    requestClient.delete('/file', { data: { id } });
+}

+ 1 - 0
apps/web-baicai/src/api/system/index.ts

@@ -3,6 +3,7 @@ export * from './database';
 export * from './department';
 export * from './dictionary';
 export * from './enum';
+export * from './file';
 export * from './log';
 export * from './menu';
 export * from './post';

+ 191 - 0
apps/web-baicai/src/components/form/components/upload.vue

@@ -0,0 +1,191 @@
+<script setup lang="ts">
+import { computed, onMounted, type PropType, reactive, ref, watch } from 'vue';
+
+import { useAccessStore } from '@vben/stores';
+
+import {
+  Button,
+  message,
+  Upload,
+  type UploadChangeParam,
+  type UploadFile,
+} from 'ant-design-vue';
+
+import { FileApi } from '#/api';
+import { Icon } from '#/components/icon';
+
+type UploadParams = {
+  allowSuffix: string;
+  directory: string;
+  folderId: Number | String;
+  path: string;
+};
+
+defineOptions({
+  name: 'Upload',
+  inheritAttrs: false,
+});
+const props = defineProps({
+  value: {
+    type: [Number, String] as PropType<number | string>,
+    default: 0,
+  },
+  action: {
+    // 上传的地址
+    type: String,
+    default: `${import.meta.env.VITE_GLOB_API_URL}/file/upload`,
+  },
+  headers: {
+    // 上传请求的 headers
+    type: Object,
+    default: () => ({}),
+  },
+  disabled: {
+    // 是否禁用
+    type: Boolean,
+    default: false,
+  },
+  listType: {
+    // 上传类型
+    type: String as PropType<'picture' | 'picture-card' | 'text'>,
+    default: 'text',
+  },
+  maxCount: {
+    // 最大上传数量
+    type: Number,
+    default: undefined,
+  },
+  uploadName: {
+    // 文件上传字段名,name被字段名覆写了,要换成uploadName
+    type: String,
+    default: undefined,
+  },
+  params: {
+    type: Object as PropType<UploadParams>,
+    default: () => ({}),
+  },
+});
+const emits = defineEmits(['update:modelValue']);
+const accessStore = useAccessStore();
+const fileList = ref<UploadFile[]>([]);
+// const attrs = useAttrs();
+const bindProps = computed(() => {
+  return {
+    // ...omit(attrs, ['name']), // 'onChange','onInput', 'onBlur',
+    ...props,
+    name: props.uploadName || 'file',
+  };
+});
+const getFileList = async (val: number | string | undefined) => {
+  if (!val) {
+    if (fileList.value.length > 0) fileList.value = [];
+    return;
+  }
+  const data = await FileApi.getList(Number(val));
+  fileList.value =
+    data && data.length > 0
+      ? data.map((item) => {
+          return {
+            uid: item.id.toString(),
+            size: item.sizeKb,
+            name: item.fileName,
+            url: item.filePath,
+          } as UploadFile;
+        })
+      : [];
+};
+
+const uploadParams = reactive<UploadParams>({
+  allowSuffix: '',
+  directory: '',
+  folderId: 0,
+  path: '',
+});
+const modelValue = computed({
+  get() {
+    return props.value;
+  },
+  set(val) {
+    Object.assign(uploadParams, props.params, { folderId: val });
+    emits('update:modelValue', val);
+  },
+});
+
+const getHeaders = {
+  Authorization: `Bearer ${accessStore.accessToken}`,
+  ...props.headers,
+};
+const handleChange = (info: UploadChangeParam) => {
+  if (info.file.status === 'done') {
+    // 更新file-list当前file的uid
+    // console.log('handleChange', info.file, fileList.value);
+    if (info.file.response.code === 200) {
+      fileList.value = fileList.value.map((item) => {
+        if (item.uid === info.file.uid) {
+          return {
+            ...item,
+            uid: info.file.response.data.id,
+          };
+        }
+        return item;
+      });
+      modelValue.value = info.file.response.data.folderId;
+    } else {
+      fileList.value.splice(fileList.value.indexOf(info.file), 1);
+      message.error(info.file.response.message);
+    }
+  } else if (info.file.status === 'error') {
+    message.error(`${info.file.name} file upload failed.`);
+  }
+};
+
+const handleRemove = async (file: UploadFile) => {
+  // console.log('handleRemove', file, fileList.value);
+  await FileApi.deleteDetail(Number(file.uid));
+  fileList.value.splice(fileList.value.indexOf(file), 1);
+  if (fileList.value.length === 0) {
+    modelValue.value = 0;
+  }
+};
+
+watch(
+  () => props.value,
+  async (val) => {
+    await getFileList(val);
+  },
+);
+onMounted(async () => {
+  await getFileList(props.value);
+});
+</script>
+<template>
+  <Upload
+    v-model:file-list="fileList"
+    v-bind="bindProps"
+    :action="action"
+    :data="uploadParams"
+    :disabled="disabled"
+    :headers="getHeaders"
+    :list-type="listType"
+    :max-count="maxCount"
+    @change="handleChange"
+    @remove="handleRemove"
+  >
+    <template #default>
+      <Icon
+        v-if="listType === 'picture-card'"
+        :size="24"
+        icon="ant-design:plus-outlined"
+      />
+      <Button v-else>
+        上传文件
+        <template #icon>
+          <Icon icon="ant-design:cloud-upload-outlined" />
+        </template>
+      </Button>
+    </template>
+    <template v-for="item in Object.keys($slots)" #[item]="data">
+      <slot :name="item" v-bind="data || {}"></slot>
+    </template>
+  </Upload>
+</template>