换树结构数据
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
189
src/App.vue
189
src/App.vue
@@ -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 [
|
|
||||||
{
|
|
||||||
title: getTabTitle(category.type),
|
|
||||||
key: category.type,
|
|
||||||
children: [{ title: '惊喜牌组', key: '惊喜牌组' }],
|
|
||||||
selectable: false
|
|
||||||
}
|
}
|
||||||
];
|
return {
|
||||||
} else if (category.type === 'pathing') {
|
title: node.name,
|
||||||
return [
|
key: node.path,
|
||||||
{
|
children: Array.isArray(node.children)
|
||||||
title: getTabTitle(category.type),
|
? node.children
|
||||||
key: category.type,
|
.map(buildTree)
|
||||||
children: tags.map(tag => ({ title: tag, key: tag })),
|
.filter(Boolean)
|
||||||
selectable: false
|
: undefined,
|
||||||
}
|
selectable: true
|
||||||
];
|
};
|
||||||
}
|
};
|
||||||
return [];
|
return [buildTree(category)].filter(Boolean);
|
||||||
};
|
};
|
||||||
|
|
||||||
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>
|
||||||
Reference in New Issue
Block a user