20260802220544
This commit is contained in:
1 parent
c00329b5a4
commit
4fc9040a8a
60 files changed
+6273
-908
No files matched your search
@@ -0,0 +1,234 @@
|
||||
<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>
|
||||
Reference in new issue
Block a user