Files
workspace/code/fms/fms-vue/src/components/fms-file/FmsFile.vue
T
2026-08-02 22:05:44 +08:00

235 lines
5.7 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup>
import { ref, computed, watch, onMounted } from "vue";
import { loadDataApi } from "@/services/api";
import FileCategoryPanel from "./FileCategoryPanel.vue";
import FileListPanel from "./FileListPanel.vue";
import FilePreviewPanel from "./FilePreviewPanel.vue";
import { useFileCategory } from "./composables/useFileCategory";
const props = defineProps({
moduleCode: { type: [String, Number], required: true },
father: { type: [String, Number], required: true },
showTree: { type: Boolean, default: true },
readonly: { type: Boolean, default: false },
initialCateId: { type: String, default: "" },
});
const emit = defineEmits(["uploaded", "deleted", "selected"]);
// ---------- 模块解析 ----------
const moduleId = ref("");
const moduleResolving = ref(false);
const moduleError = ref("");
async function resolveModule() {
const code = props.moduleCode;
if (!code) {
moduleId.value = "";
return;
}
moduleResolving.value = true;
moduleError.value = "";
try {
const res = await loadDataApi(
"s_module",
`b_code = N'${String(code).replaceAll("'", "''")}' AND b_canuse = 1`,
"",
["b_id", "b_code", "b_name"],
);
if (res.data?.length) {
moduleId.value = String(res.data[0].b_id);
} else {
moduleId.value = "";
moduleError.value = `未找到模块 "${code}"`;
}
} catch (e) {
moduleError.value = e?.message || "模块解析失败";
moduleId.value = "";
} finally {
moduleResolving.value = false;
}
}
watch(
() => props.moduleCode,
() => resolveModule(),
);
// ---------- 分类 ----------
const {
treeData: categoryTreeData,
loading: categoryLoading,
selectedKey: categorySelectedKey,
loadCategories,
selectCategory,
} = useFileCategory();
const activeCateId = computed(() => {
const key = categorySelectedKey.value;
if (!key || key === "__fms_tree_root__") return "";
return key;
});
const categoryExpandedKeys = ref(["__fms_tree_root__"]);
function onCategorySelect(key) {
selectCategory(key);
}
// ---------- 预览选中 ----------
const fileListRef = ref(null);
const selectedFile = ref(null);
const currentFileList = computed(() => fileListRef.value?.files || []);
function onPreviewFile(file) {
selectedFile.value = file;
emit("selected", file);
}
function onPreviewPrev() {
const list = currentFileList.value;
const idx = list.findIndex((f) => f.subid === selectedFile.value?.subid);
if (idx > 0) {
selectedFile.value = list[idx - 1];
emit("selected", selectedFile.value);
}
}
function onPreviewNext() {
const list = currentFileList.value;
const idx = list.findIndex((f) => f.subid === selectedFile.value?.subid);
if (idx >= 0 && idx < list.length - 1) {
selectedFile.value = list[idx + 1];
emit("selected", selectedFile.value);
}
}
// ---------- 事件代理 ----------
function onUploaded() {
emit("uploaded");
}
function onDeleted() {
selectedFile.value = null;
emit("deleted");
}
// ---------- 挂载 ----------
onMounted(() => {
resolveModule();
loadCategories();
if (props.initialCateId) {
selectCategory(props.initialCateId);
categoryExpandedKeys.value = ["__fms_tree_root__", String(props.initialCateId)];
}
});
const ready = computed(() => !moduleResolving.value && !moduleError.value);
defineExpose({ moduleId, refreshCategories: loadCategories });
</script>
<template>
<div class="ff-container">
<div v-if="moduleResolving" class="ff-status">
<a-spin size="small" />
<span class="ff-status__text">正在解析模块...</span>
</div>
<div v-else-if="moduleError" class="ff-status ff-status--error">
{{ moduleError }}
</div>
<template v-else>
<a-splitter class="ff-splitter" lazy>
<!-- 左:分类树(固定 220px 侧栏) -->
<a-splitter-panel
v-if="showTree"
:default-size="220"
:min="160"
:max="320"
>
<FileCategoryPanel
:tree-data="categoryTreeData"
:loading="categoryLoading"
:selected-key="categorySelectedKey"
:expanded-keys="categoryExpandedKeys"
@select="onCategorySelect"
@update:expanded-keys="(keys) => (categoryExpandedKeys = keys)"
/>
</a-splitter-panel>
<!-- 中:文件列表(固定 500px) -->
<a-splitter-panel :default-size="500" :min="280">
<div class="ff-center">
<FileListPanel
ref="fileListRef"
:module-code="moduleCode"
:module-id="moduleId"
:father="father"
:cate-id="activeCateId"
:readonly="readonly"
@preview="onPreviewFile"
@uploaded="onUploaded"
@deleted="onDeleted"
/>
</div>
</a-splitter-panel>
<!-- 右:预览(不指定默认宽,自动占剩余空间) -->
<a-splitter-panel :min="440" collapsible>
<FilePreviewPanel
:file="selectedFile"
:file-list="currentFileList"
@prev="onPreviewPrev"
@next="onPreviewNext"
/>
</a-splitter-panel>
</a-splitter>
</template>
</div>
</template>
<style scoped lang="scss">
.ff-container {
width: 100%;
height: 100%;
min-width: 0;
min-height: 0;
border: 1px solid var(--fms-border);
border-radius: 6px;
background: #fff;
overflow: hidden;
}
.ff-splitter {
width: 100%;
height: 100%;
min-width: 0;
min-height: 0;
}
.ff-center {
display: flex;
width: 100%;
height: 100%;
min-width: 0;
min-height: 0;
}
.ff-status {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
gap: 8px;
font-size: 13px;
color: #646a73;
&--error {
color: #cf1322;
}
}
</style>