Files
yunzerwebsiteallinone/backend/src/views/apps/crm/project/components/detail_attach.vue
T

157 lines
4.8 KiB
Vue

<template>
<div class="detail-attach">
<div class="tab-toolbar">
<el-upload :show-file-list="false" :http-request="handleUpload" :disabled="uploading">
<el-button type="primary" size="small" :icon="Upload" :loading="uploading">上传附件</el-button>
</el-upload>
<span v-if="tip" class="tip">{{ tip }}</span>
</div>
<el-table :data="list" v-loading="loading" stripe border size="small">
<el-table-column label="文件名" min-width="170" show-overflow-tooltip>
<template #default="{ row }">
<a :href="row.file_url" target="_blank" class="file-link">{{ row.file_name || "-" }}</a>
</template>
</el-table-column>
<el-table-column v-if="showStage" label="阶段" min-width="62" align="center">
<template #default="{ row }">
<el-tag size="small" effect="plain" :type="pipelineStageTag(row.related_type)">
{{ pipelineStageText(row.related_type) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="大小" min-width="78" align="right">
<template #default="{ row }">{{ formatFileSize(row.file_size) }}</template>
</el-table-column>
<el-table-column prop="uploader_name" label="上传人" min-width="78" show-overflow-tooltip />
<el-table-column label="上传时间" min-width="140">
<template #default="{ row }">{{ formatDateTime(row.create_time) }}</template>
</el-table-column>
<el-table-column label="操作" width="76" align="center">
<template #default="{ row }">
<el-button link type="danger" size="small" @click="handleDelete(row)">删除</el-button>
</template>
</el-table-column>
<template #empty><el-empty description="暂无附件" :image-size="60" /></template>
</el-table>
</div>
</template>
<script setup>
import { ref } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import { Upload } from "@element-plus/icons-vue";
import { getAttachList, addAttach, deleteAttach } from "@/api/crmPipeline";
import { uploadFile } from "@/api/file";
import { formatDateTime, pipelineStageText, pipelineStageTag } from "../../dict";
const props = defineProps({
// 关联对象ID
relatedId: { type: [Number, String], default: null },
// 关联类型:1=线索 2=商机 3=项目
relatedType: { type: Number, default: 3 },
// 工具栏提示文案
tip: { type: String, default: "" },
// 是否累积展示上游链路的附件(线索 + 商机 + 项目)
includeSource: { type: Boolean, default: false },
// 是否展示「阶段」列
showStage: { type: Boolean, default: false },
});
const emit = defineEmits(["count"]);
const loading = ref(false);
const uploading = ref(false);
const list = ref([]);
/** 供父组件(detail.vue)在抽屉打开时调用 */
async function reload() {
if (!props.relatedId) {
list.value = [];
emit("count", 0);
return;
}
loading.value = true;
try {
const params = {
related_type: props.relatedType,
related_id: props.relatedId,
};
if (props.includeSource) params.include_source = 1;
const res = await getAttachList(params);
list.value = res?.data?.list || [];
} catch (e) {
list.value = [];
} finally {
loading.value = false;
}
emit("count", list.value.length);
}
async function handleUpload(options) {
const file = options.file;
uploading.value = true;
try {
const fd = new FormData();
fd.append("file", file);
const res = await uploadFile(fd);
const data = res?.data || {};
await addAttach({
related_type: props.relatedType,
related_id: props.relatedId,
file_id: data.id || 0,
file_name: data.name || file.name,
file_url: data.url || "",
file_size: file.size || 0,
});
ElMessage.success("上传成功");
reload();
} catch (e) {
ElMessage.error(e.message || "上传失败");
} finally {
uploading.value = false;
}
}
async function handleDelete(row) {
try {
await ElMessageBox.confirm(`确定删除附件「${row.file_name}」吗?`, "删除确认", { type: "warning" });
await deleteAttach({ id: row.id });
ElMessage.success("删除成功");
reload();
} catch (e) {
if (e !== "cancel") ElMessage.error(e.message || "删除失败");
}
}
function formatFileSize(size) {
const n = Number(size) || 0;
if (n < 1024) return `${n} B`;
if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
return `${(n / 1024 / 1024).toFixed(2)} MB`;
}
defineExpose({ reload });
</script>
<style lang="less" scoped>
.tab-toolbar {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 12px;
.tip {
font-size: 12px;
color: var(--el-text-color-secondary);
}
}
.file-link {
color: var(--el-color-primary);
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
</style>