20260723173317
This commit is contained in:
1 parent
59b3ccfb1c
commit
5f73b54802
33 files changed
+3064
-2108
No files matched your search
@@ -0,0 +1,256 @@
|
||||
<script setup>
|
||||
import { computed, ref, watch } from "vue";
|
||||
import { message } from "antdv-next";
|
||||
import FmsTree from "@/components/FmsTree.vue";
|
||||
import { saveObjectApi } from "@/services/api";
|
||||
import { cloneData, diffRows, allocateTemporaryIds, tableChange, hasChanges } from "@/utils/dataChanges";
|
||||
import { createTempId } from "@/utils/tempId";
|
||||
|
||||
const props = defineProps({
|
||||
visible: { type: Boolean, default: false },
|
||||
moduleId: { type: String, required: true },
|
||||
groups: { type: Array, default: () => [] },
|
||||
});
|
||||
|
||||
const emit = defineEmits(["update:visible", "update"]);
|
||||
|
||||
const localGroups = ref([]);
|
||||
const selectedGroupId = ref(null);
|
||||
const editForm = ref(null);
|
||||
|
||||
watch(
|
||||
() => props.groups,
|
||||
(val) => {
|
||||
localGroups.value = cloneData(val || []);
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
const treeData = computed(() => {
|
||||
return buildTreeData(localGroups.value);
|
||||
});
|
||||
|
||||
function buildTreeData(items) {
|
||||
const map = new Map();
|
||||
const roots = [];
|
||||
items.forEach((item) => {
|
||||
item.key = String(item.b_id);
|
||||
map.set(item.key, item);
|
||||
});
|
||||
items.forEach((item) => {
|
||||
if (item.b_parent_id && map.has(String(item.b_parent_id))) {
|
||||
const parent = map.get(String(item.b_parent_id));
|
||||
if (!parent.children) parent.children = [];
|
||||
parent.children.push(item);
|
||||
} else {
|
||||
roots.push(item);
|
||||
}
|
||||
});
|
||||
return roots.map((r) => ({
|
||||
key: r.key,
|
||||
title: r.b_title || `分组-${r.b_id}`,
|
||||
children: r.children?.map((c) => ({
|
||||
key: c.key,
|
||||
title: c.b_title || `分组-${c.b_id}`,
|
||||
children: c.children?.map((gc) => ({
|
||||
key: gc.key,
|
||||
title: gc.b_title || `分组-${gc.b_id}`,
|
||||
})),
|
||||
})) || [],
|
||||
}));
|
||||
}
|
||||
|
||||
const selectedGroup = computed(() =>
|
||||
localGroups.value.find((g) => String(g.b_id) === selectedGroupId.value),
|
||||
);
|
||||
|
||||
function onTreeSelect(keys) {
|
||||
if (keys.length > 0) {
|
||||
selectedGroupId.value = String(keys[0]);
|
||||
const g = selectedGroup.value;
|
||||
editForm.value = g ? { ...g } : null;
|
||||
} else {
|
||||
selectedGroupId.value = null;
|
||||
editForm.value = null;
|
||||
}
|
||||
}
|
||||
|
||||
function addGroup(parentId) {
|
||||
const newGroup = {
|
||||
b_id: createTempId(),
|
||||
b_module_id: props.moduleId,
|
||||
b_parent_id: parentId || null,
|
||||
b_title: "",
|
||||
b_i18n: null,
|
||||
b_xh: localGroups.value.length * 10,
|
||||
b_canuse: 1,
|
||||
};
|
||||
localGroups.value.push(newGroup);
|
||||
selectedGroupId.value = String(newGroup.b_id);
|
||||
editForm.value = { ...newGroup };
|
||||
}
|
||||
|
||||
function deleteGroup() {
|
||||
if (!selectedGroupId.value) return;
|
||||
localGroups.value = localGroups.value.filter((g) => String(g.b_id) !== selectedGroupId.value);
|
||||
selectedGroupId.value = null;
|
||||
editForm.value = null;
|
||||
}
|
||||
|
||||
async function saveAllGroups() {
|
||||
const original = cloneData(props.groups || []);
|
||||
const changes = diffRows(localGroups.value, original);
|
||||
const req = tableChange("b_module_field_group", changes);
|
||||
if (!hasChanges(req)) {
|
||||
message.info("没有需要保存的变更");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await allocateTemporaryIds([req.inserts || [], req.updates || [], req.deletes || []]);
|
||||
await saveObjectApi([req]);
|
||||
emit("update", cloneData(localGroups.value));
|
||||
message.success("分组已保存");
|
||||
emit("update:visible", false);
|
||||
} catch (e) {
|
||||
message.error("保存失败:" + (e.message || e));
|
||||
}
|
||||
}
|
||||
|
||||
function onClose() {
|
||||
emit("update:visible", false);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-modal
|
||||
:open="visible"
|
||||
title="分组管理"
|
||||
width="650px"
|
||||
@cancel="onClose"
|
||||
@ok="saveAllGroups"
|
||||
>
|
||||
<div class="group-manager">
|
||||
<div class="group-sidebar">
|
||||
<div class="group-sidebar-header">
|
||||
<span>分组树</span>
|
||||
<a-button size="small" type="primary" @click="addGroup(null)">新增</a-button>
|
||||
</div>
|
||||
<FmsTree
|
||||
v-if="treeData.length > 0"
|
||||
class="group-tree"
|
||||
:tree-data="treeData"
|
||||
:field-names="{ title: 'title', key: 'key', children: 'children' }"
|
||||
:selected-keys="selectedGroupId ? [selectedGroupId] : []"
|
||||
@select="onTreeSelect"
|
||||
>
|
||||
<template #title="{ key, title: nodeTitle }">
|
||||
<span class="group-tree-node">
|
||||
<span>{{ nodeTitle }}</span>
|
||||
<span class="group-tree-node-actions">
|
||||
<a-button size="small" type="text" @click.stop="addGroup(key)">+</a-button>
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
</FmsTree>
|
||||
<a-empty v-else description="暂无分组" />
|
||||
</div>
|
||||
<div class="group-detail" v-if="editForm">
|
||||
<a-form layout="vertical">
|
||||
<a-form-item label="分组标题" required>
|
||||
<a-input
|
||||
:value="editForm.b_title"
|
||||
@update:value="(v) => editForm.b_title = v"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="多语言键">
|
||||
<a-input
|
||||
:value="editForm.b_i18n"
|
||||
@update:value="(v) => editForm.b_i18n = v || null"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="排序号">
|
||||
<a-input-number
|
||||
:value="editForm.b_xh"
|
||||
:min="0"
|
||||
@update:value="(v) => editForm.b_xh = v"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="启用">
|
||||
<a-switch
|
||||
:checked="editForm.b_canuse === 1"
|
||||
@update:checked="(v) => editForm.b_canuse = v ? 1 : 0"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-button danger size="small" @click="deleteGroup">删除此分组</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
<a-empty v-else description="选择左侧分组节点编辑" class="group-detail-empty" />
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.group-manager {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
min-height: 350px;
|
||||
}
|
||||
|
||||
.group-sidebar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: none;
|
||||
width: 240px;
|
||||
border: 1px solid var(--fms-border);
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.group-sidebar-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex: none;
|
||||
padding: 8px 12px;
|
||||
border-bottom: 1px solid var(--fms-border);
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.group-tree {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.group-tree-node {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.group-tree-node-actions {
|
||||
display: none;
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.group-tree-node:hover .group-tree-node-actions {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.group-detail {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.group-detail-empty {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user