换树结构数据

This commit is contained in:
辉鸭蛋
2024-10-11 02:25:07 +08:00
parent bad29076ed
commit e261da7a5a
2 changed files with 115 additions and 76 deletions

View File

@@ -4,7 +4,7 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<link rel="icon" href="/favicon.ico"> <link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Genshin Copilot Scripts</title> <title>Genshin Copilot Scripts | BetterGI 更好的原神</title>
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>

View File

@@ -8,38 +8,36 @@
style="width: 320px" style="width: 320px"
@change="fetchRepoData" @change="fetchRepoData"
> >
<a-option value="https://raw.githubusercontent.com/babalae/bettergi-scripts-list/refs/heads/main/repo/items.json">BetterGI 中央仓库</a-option> <a-option value="https://raw.githubusercontent.com/babalae/bettergi-scripts-list/refs/heads/main/build/tree.json">BetterGI 中央仓库</a-option>
</a-select> </a-select>
<a-tabs v-if="repoData.length"> <a-tabs v-if="repoData.length">
<a-tab-pane v-for="category in repoData" :key="category.type" :title="getTabTitle(category.type)"> <a-tab-pane v-for="category in repoData" :key="category.name" :title="getCategoryDisplayName(category.name)">
<a-row :gutter="16"> <a-row :gutter="16">
<a-col :span="6" v-if="showTree(category)"> <a-col :span="6" v-if="showTree(category)">
<a-tree <a-tree
:data="getCategoryTree(category)" :data="getCategoryTree(category)"
@select="(selectedKeys) => handleTreeSelect(selectedKeys, category.type)" @select="(selectedKeys, event) => handleTreeSelect(selectedKeys, event, category.name)"
:selectedKeys="searchConditions[category.type].tags"
multiple
/> />
</a-col> </a-col>
<a-col :span="showTree(category) ? 18 : 24"> <a-col :span="showTree(category) ? 18 : 24">
<a-space direction="vertical" size="medium" style="width: 100%;"> <a-space direction="vertical" size="medium" style="width: 100%;">
<a-row :gutter="16"> <a-row :gutter="16">
<a-col :span="8"> <a-col :span="8">
<a-input v-model="searchConditions[category.type].name" placeholder="搜索名称" allow-clear @change="filterData(category.type)" /> <a-input v-model="searchConditions[category.name].name" placeholder="搜索名称" allow-clear @change="filterData(category.name)" />
</a-col> </a-col>
<a-col :span="8"> <a-col :span="8">
<a-select v-model="searchConditions[category.type].author" placeholder="选择作者" style="width: 100%;" allow-clear @change="filterData(category.type)"> <a-select v-model="searchConditions[category.name].author" placeholder="选择作者" style="width: 100%;" allow-clear @change="filterData(category.name)">
<a-option v-for="author in getUniqueAuthors(category.list)" :key="author" :value="author">{{ author }}</a-option> <a-option v-for="author in getUniqueAuthors(category)" :key="author" :value="author">{{ author }}</a-option>
</a-select> </a-select>
</a-col> </a-col>
<a-col :span="8"> <a-col :span="8">
<a-select v-model="searchConditions[category.type].tags" placeholder="选择标签" style="width: 100%;" allow-clear @change="handleTagSelect(category.type)" multiple> <a-select v-model="searchConditions[category.name].tags" placeholder="选择标签" style="width: 100%;" allow-clear @change="handleTagSelect(category.name)" multiple>
<a-option v-for="tag in getUniqueTags(category.list)" :key="tag" :value="tag">{{ tag }}</a-option> <a-option v-for="tag in getUniqueTags(category)" :key="tag" :value="tag">{{ tag }}</a-option>
</a-select> </a-select>
</a-col> </a-col>
</a-row> </a-row>
<a-table :columns="columns" :data="filteredData[category.type]" :pagination="{ pageSize: 20 }"> <a-table :columns="columns" :data="filteredData[category.name]" :pagination="{ pageSize: 20 }">
<template #name="{ record }"> <template #name="{ record }">
{{ record.name }} {{ record.name }}
</template> </template>
@@ -102,30 +100,22 @@ const columns = [
{ title: '操作', slotName: 'operations' }, { title: '操作', slotName: 'operations' },
]; ];
const getTabTitle = (type) => {
const titles = {
js: 'JS脚本',
pathing: '地图追踪',
macro: '键鼠脚本',
combat: '战斗策略',
tcg: '七圣召唤策略',
onekey: '一键宏',
};
return titles[type] || type;
};
const fetchRepoData = async () => { const fetchRepoData = async () => {
if (!selectedRepo.value) return; if (!selectedRepo.value) return;
try { try {
const response = await fetch(selectedRepo.value); const response = await fetch(selectedRepo.value);
const data = await response.json(); const data = await response.json();
// 为所有节点生成 path
data.forEach(category => generatePaths(category));
repoData.value = data; repoData.value = data;
initializeSearchConditions(); initializeSearchConditions();
// 初始化 tagColorMap // 初始化 tagColorMap
data.forEach(category => { data.forEach(category => {
category.list.forEach(item => { traverseCategory(category, (item) => {
if (Array.isArray(item.tags)) { if (Array.isArray(item.tags)) {
item.tags.forEach(tag => { item.tags.forEach(tag => {
if (tag && typeof tag === 'string' && !tagColorMap[tag]) { if (tag && typeof tag === 'string' && !tagColorMap[tag]) {
@@ -141,32 +131,82 @@ const fetchRepoData = async () => {
} }
}; };
const getUniqueAuthors = (list) => { // 新增函数:为所有节点生成 path
return [...new Set(list.map(item => item.author))]; const generatePaths = (node, parentPath = '') => {
const currentPath = parentPath ? `${parentPath}/${node.name}` : node.name;
node.path = currentPath;
if (node.type === 'directory' && Array.isArray(node.children)) {
node.children.forEach(child => generatePaths(child, currentPath));
}
}; };
const getUniqueTags = (list) => { const traverseCategory = (category, callback) => {
return [...new Set(list.flatMap(item => item.tags))]; if (category.type === 'file') {
callback(category);
} else if (category.type === 'directory' && Array.isArray(category.children)) {
if (category.name === 'js') {
category.children.forEach(child => {
if (child.type === 'directory') {
callback(child);
} else {
traverseCategory(child, callback);
}
});
} else {
category.children.forEach(child => traverseCategory(child, callback));
}
}
}; };
const filterData = (type) => { const getUniqueAuthors = (category) => {
const condition = searchConditions[type]; const authors = new Set();
filteredData[type] = repoData.value.find(category => category.type === type).list.filter(item => { traverseCategory(category, (item) => {
if (item.author) authors.add(item.author);
});
return [...authors];
};
const getUniqueTags = (category) => {
const tags = new Set();
traverseCategory(category, (item) => {
if (Array.isArray(item.tags)) {
item.tags.forEach(tag => tags.add(tag));
}
});
return [...tags];
};
const filterData = (categoryName) => {
const category = repoData.value.find(cat => cat.name === categoryName);
const condition = searchConditions[categoryName];
const filtered = [];
traverseCategory(category, (item) => {
const nameMatch = !condition.name || item.name.toLowerCase().includes(condition.name.toLowerCase()); const nameMatch = !condition.name || item.name.toLowerCase().includes(condition.name.toLowerCase());
const authorMatch = !condition.author || item.author === condition.author; const authorMatch = !condition.author || item.author === condition.author;
const tagMatch = condition.tags.length === 0 || condition.tags.some(tag => item.tags.includes(tag)); const tagMatch = condition.tags.length === 0 || (Array.isArray(item.tags) && condition.tags.some(tag => item.tags.includes(tag)));
return nameMatch && authorMatch && tagMatch; const pathMatch = !condition.path || (item.path && item.path.startsWith(condition.path) && item.path !== condition.path);
if (nameMatch && authorMatch && tagMatch && pathMatch && (item.type === 'file' || (category.name === 'js' && item.type === 'directory'))) {
filtered.push(item);
}
}); });
filteredData[categoryName] = filtered;
}; };
const initializeSearchConditions = () => { const initializeSearchConditions = () => {
repoData.value.forEach(category => { repoData.value.forEach(category => {
searchConditions[category.type] = { searchConditions[category.name] = {
name: '', name: '',
author: '', author: '',
tags: [] tags: [],
path: ''
}; };
filteredData[category.type] = category.list; filteredData[category.name] = [];
traverseCategory(category, (item) => {
filteredData[category.name].push(item);
});
}); });
}; };
@@ -190,10 +230,8 @@ const getTagColor = (tag) => {
onMounted(() => { onMounted(() => {
// 默认选中第一个仓库 // 默认选中第一个仓库
selectedRepo.value = 'https://raw.githubusercontent.com/babalae/bettergi-scripts-list/refs/heads/main/repo/items.json'; selectedRepo.value = 'https://raw.githubusercontent.com/babalae/bettergi-scripts-list/refs/heads/main/build/tree.json';
fetchRepoData().then(() => { fetchRepoData();
initializeSearchConditions();
});
}); });
const downloadScript = (script) => { const downloadScript = (script) => {
@@ -208,7 +246,6 @@ const showDetails = (script) => {
{ label: '作者', value: script.author }, { label: '作者', value: script.author },
{ label: '版本', value: script.version }, { label: '版本', value: script.version },
{ label: '描述', value: script.description || '无描述' }, { label: '描述', value: script.description || '无描述' },
{ label: '路径', value: script.path },
{ label: '标签', value: script.tags }, { label: '标签', value: script.tags },
{ label: 'Hash', value: script.hash }, { label: 'Hash', value: script.hash },
]; ];
@@ -220,47 +257,50 @@ const closeDrawer = () => {
}; };
const getCategoryTree = (category) => { const getCategoryTree = (category) => {
const tags = getUniqueTags(category.list); const buildTree = (node) => {
if (tags.length === 0) { if (node.type === 'file') {
return []; return null;
} }
if (category.type === 'tcg') { return {
return [ title: node.name,
{ key: node.path,
title: getTabTitle(category.type), children: Array.isArray(node.children)
key: category.type, ? node.children
children: [{ title: '惊喜牌组', key: '惊喜牌组' }], .map(buildTree)
selectable: false .filter(Boolean)
} : undefined,
]; selectable: true
} else if (category.type === 'pathing') { };
return [ };
{ return [buildTree(category)].filter(Boolean);
title: getTabTitle(category.type),
key: category.type,
children: tags.map(tag => ({ title: tag, key: tag })),
selectable: false
}
];
}
return [];
}; };
const showTree = (category) => { const showTree = (category) => {
if (getCategoryTree(category).length === 0) { return category.name === 'pathing';
return false;
}
return getUniqueTags(category.list).length > 1;
}; };
const handleTreeSelect = (selectedKeys, categoryType) => { const handleTreeSelect = (selectedKeys, event, categoryName) => {
searchConditions[categoryType].tags = selectedKeys.filter(key => key !== categoryType); const selectedNode = event.node;
filterData(categoryType); searchConditions[categoryName].path = selectedNode.key;
filterData(categoryName);
}; };
const handleTagSelect = (categoryType) => { const handleTagSelect = (categoryName) => {
filterData(categoryType); filterData(categoryName);
};
// 添加类别名称映射
const categoryNameMap = {
'pathing': '地图追踪',
'js': 'JS脚本',
'combat': '战斗策略',
'tcg': '七圣召唤',
'onekey': '一键宏'
};
// 添加获取显示名称的函数
const getCategoryDisplayName = (name) => {
return categoryNameMap[name] || name;
}; };
</script> </script>
@@ -268,6 +308,5 @@ const handleTagSelect = (categoryType) => {
.arco-tree { .arco-tree {
background-color: var(--color-fill-2); background-color: var(--color-fill-2);
padding: 16px; padding: 16px;
/* border-radius: 4px; */
} }
</style> </style>