157 lines
4.8 KiB
Vue
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>
|