mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-09-05 15:36:49 +08:00
添加混合排序功能,重构插件列表显示逻辑,移除冗余代码并优化拖拽排序体验。
This commit is contained in:
@@ -0,0 +1,161 @@
|
|||||||
|
<template>
|
||||||
|
<div class="mixed-sort-card-wrapper">
|
||||||
|
<!-- 文件夹卡片 -->
|
||||||
|
<div
|
||||||
|
v-if="item.type === 'folder'"
|
||||||
|
class="drop-zone cursor-move"
|
||||||
|
:data-plugin-id="item.id"
|
||||||
|
@dragover="handleDragOver"
|
||||||
|
@dragenter="handleDragEnter"
|
||||||
|
@dragleave="handleDragLeave"
|
||||||
|
@drop="handleDropToFolder"
|
||||||
|
>
|
||||||
|
<PluginFolderCard
|
||||||
|
:folder-name="item.data.name"
|
||||||
|
:plugin-count="item.data.pluginCount"
|
||||||
|
:folder-config="item.data.config"
|
||||||
|
@open="$emit('openFolder', item.id)"
|
||||||
|
@delete="$emit('deleteFolder', item.id)"
|
||||||
|
@rename="(oldName, newName) => $emit('renameFolder', oldName, newName)"
|
||||||
|
@update-config="config => $emit('updateFolderConfig', item.id, config)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 插件卡片 -->
|
||||||
|
<div v-else-if="item.type === 'plugin'" class="plugin-item-wrapper cursor-move" :data-plugin-id="item.id">
|
||||||
|
<PluginCard
|
||||||
|
:count="pluginStatistics[item.id] || 0"
|
||||||
|
:plugin="item.data"
|
||||||
|
:action="pluginActions[item.id] || false"
|
||||||
|
@remove="$emit('refreshData')"
|
||||||
|
@save="$emit('refreshData')"
|
||||||
|
@action-done="$emit('actionDone', item.id)"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 移出文件夹按钮(仅在文件夹内显示) -->
|
||||||
|
<VBtn
|
||||||
|
v-if="showRemoveButton"
|
||||||
|
icon="mdi-folder-remove"
|
||||||
|
variant="text"
|
||||||
|
color="warning"
|
||||||
|
size="small"
|
||||||
|
class="remove-from-folder-btn"
|
||||||
|
@click="$emit('removeFromFolder', item.id)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import PluginCard from './PluginCard.vue'
|
||||||
|
import PluginFolderCard from './PluginFolderCard.vue'
|
||||||
|
|
||||||
|
interface MixedSortItem {
|
||||||
|
type: 'folder' | 'plugin'
|
||||||
|
id: string
|
||||||
|
data: any
|
||||||
|
order: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
item: MixedSortItem
|
||||||
|
pluginStatistics?: { [key: string]: number }
|
||||||
|
pluginActions?: { [key: string]: boolean }
|
||||||
|
showRemoveButton?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
pluginStatistics: () => ({}),
|
||||||
|
pluginActions: () => ({}),
|
||||||
|
showRemoveButton: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
openFolder: [folderName: string]
|
||||||
|
deleteFolder: [folderName: string]
|
||||||
|
renameFolder: [oldName: string, newName: string]
|
||||||
|
updateFolderConfig: [folderName: string, config: any]
|
||||||
|
refreshData: []
|
||||||
|
actionDone: [pluginId: string]
|
||||||
|
removeFromFolder: [pluginId: string]
|
||||||
|
dropToFolder: [event: DragEvent, folderName: string]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
// 拖拽事件处理
|
||||||
|
function handleDragOver(event: DragEvent) {
|
||||||
|
// 只有当拖拽的是插件时才允许放入文件夹
|
||||||
|
if (props.item.type === 'folder') {
|
||||||
|
event.preventDefault()
|
||||||
|
event.stopPropagation()
|
||||||
|
event.dataTransfer!.dropEffect = 'move'
|
||||||
|
const target = event.currentTarget as HTMLElement
|
||||||
|
target.classList.add('drag-over')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDragEnter(event: DragEvent) {
|
||||||
|
if (props.item.type === 'folder') {
|
||||||
|
event.preventDefault()
|
||||||
|
event.stopPropagation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDragLeave(event: DragEvent) {
|
||||||
|
if (props.item.type === 'folder') {
|
||||||
|
event.preventDefault()
|
||||||
|
event.stopPropagation()
|
||||||
|
const target = event.currentTarget as HTMLElement
|
||||||
|
target.classList.remove('drag-over')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDropToFolder(event: DragEvent) {
|
||||||
|
if (props.item.type === 'folder') {
|
||||||
|
event.preventDefault()
|
||||||
|
event.stopPropagation()
|
||||||
|
const target = event.currentTarget as HTMLElement
|
||||||
|
target.classList.remove('drag-over')
|
||||||
|
|
||||||
|
emit('dropToFolder', event, props.item.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.mixed-sort-card-wrapper {
|
||||||
|
block-size: 100%;
|
||||||
|
inline-size: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拖拽相关样式
|
||||||
|
.drop-zone {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&.drag-over {
|
||||||
|
border: 2px dashed #2196f3;
|
||||||
|
border-radius: 16px;
|
||||||
|
box-shadow: 0 0 20px rgba(33, 150, 243, 50%);
|
||||||
|
transform: scale(1.02);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.plugin-item-wrapper {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.remove-from-folder-btn {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 10;
|
||||||
|
border-radius: 50%;
|
||||||
|
backdrop-filter: blur(4px);
|
||||||
|
background: rgba(255, 255, 255, 10%);
|
||||||
|
inset-block-start: 4px;
|
||||||
|
inset-inline-end: 4px;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover .remove-from-folder-btn {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -14,6 +14,7 @@ import PluginMarketSettingDialog from '@/components/dialog/PluginMarketSettingDi
|
|||||||
import { useDynamicButton } from '@/composables/useDynamicButton'
|
import { useDynamicButton } from '@/composables/useDynamicButton'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import PluginFolderCard from '@/components/cards/PluginFolderCard.vue'
|
import PluginFolderCard from '@/components/cards/PluginFolderCard.vue'
|
||||||
|
import MixedSortCard from '@/components/cards/MixedSortCard.vue'
|
||||||
|
|
||||||
// 国际化
|
// 国际化
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
@@ -194,8 +195,16 @@ const displayedPlugins = computed(() => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 可拖拽的插件列表(主列表用)
|
// 混合排序项目类型
|
||||||
const draggableMainPlugins = ref<Plugin[]>([])
|
interface MixedSortItem {
|
||||||
|
type: 'folder' | 'plugin'
|
||||||
|
id: string
|
||||||
|
data: any
|
||||||
|
order: number
|
||||||
|
}
|
||||||
|
|
||||||
|
// 混合排序列表(包含文件夹和插件)
|
||||||
|
const mixedSortList = ref<MixedSortItem[]>([])
|
||||||
|
|
||||||
// 可拖拽的插件列表(文件夹内用)
|
// 可拖拽的插件列表(文件夹内用)
|
||||||
const draggableFolderPlugins = ref<Plugin[]>([])
|
const draggableFolderPlugins = ref<Plugin[]>([])
|
||||||
@@ -203,30 +212,6 @@ const draggableFolderPlugins = ref<Plugin[]>([])
|
|||||||
// 是否正在拖拽排序中
|
// 是否正在拖拽排序中
|
||||||
const isDraggingSortMode = ref(false)
|
const isDraggingSortMode = ref(false)
|
||||||
|
|
||||||
// 监听displayedPlugins变化,更新可拖拽列表(避免拖拽时的循环更新)
|
|
||||||
watch(
|
|
||||||
displayedPlugins,
|
|
||||||
newPlugins => {
|
|
||||||
if (isDraggingSortMode.value) return // 拖拽排序时跳过更新
|
|
||||||
|
|
||||||
if (!currentFolder.value) {
|
|
||||||
draggableMainPlugins.value = [...newPlugins]
|
|
||||||
} else {
|
|
||||||
draggableFolderPlugins.value = [...newPlugins]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ immediate: true },
|
|
||||||
)
|
|
||||||
|
|
||||||
// 监听文件夹切换,更新可拖拽列表
|
|
||||||
watch(currentFolder, () => {
|
|
||||||
if (!currentFolder.value) {
|
|
||||||
draggableMainPlugins.value = [...displayedPlugins.value]
|
|
||||||
} else {
|
|
||||||
draggableFolderPlugins.value = [...displayedPlugins.value]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// 显示的文件夹列表(按排序显示)
|
// 显示的文件夹列表(按排序显示)
|
||||||
const displayedFolders = computed(() => {
|
const displayedFolders = computed(() => {
|
||||||
if (currentFolder.value) return [] // 在文件夹内不显示其他文件夹
|
if (currentFolder.value) return [] // 在文件夹内不显示其他文件夹
|
||||||
@@ -252,6 +237,136 @@ const displayedFolders = computed(() => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 更新混合排序列表
|
||||||
|
function updateMixedSortList() {
|
||||||
|
if (isDraggingSortMode.value) return // 拖拽排序时跳过更新
|
||||||
|
|
||||||
|
if (!currentFolder.value) {
|
||||||
|
// 主列表:创建混合列表
|
||||||
|
const items: MixedSortItem[] = []
|
||||||
|
|
||||||
|
// 创建统一的排序索引
|
||||||
|
let globalOrder = 0
|
||||||
|
|
||||||
|
// 先按现有的混合排序列表顺序创建,如果没有则按默认顺序
|
||||||
|
if (mixedSortList.value.length > 0) {
|
||||||
|
// 如果已有混合排序列表,保持现有顺序
|
||||||
|
mixedSortList.value.forEach((existingItem, index) => {
|
||||||
|
if (existingItem.type === 'folder') {
|
||||||
|
// 检查文件夹是否仍然存在
|
||||||
|
const folder = displayedFolders.value.find(f => f.name === existingItem.id)
|
||||||
|
if (folder) {
|
||||||
|
items.push({
|
||||||
|
type: 'folder',
|
||||||
|
id: folder.name,
|
||||||
|
data: folder,
|
||||||
|
order: index,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else if (existingItem.type === 'plugin') {
|
||||||
|
// 检查插件是否仍然存在
|
||||||
|
const plugin = displayedPlugins.value.find(p => p.id === existingItem.id)
|
||||||
|
if (plugin) {
|
||||||
|
items.push({
|
||||||
|
type: 'plugin',
|
||||||
|
id: plugin.id || '',
|
||||||
|
data: plugin,
|
||||||
|
order: index,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 添加新的文件夹(不在现有列表中的)
|
||||||
|
displayedFolders.value.forEach(folder => {
|
||||||
|
if (!items.find(item => item.type === 'folder' && item.id === folder.name)) {
|
||||||
|
items.push({
|
||||||
|
type: 'folder',
|
||||||
|
id: folder.name,
|
||||||
|
data: folder,
|
||||||
|
order: items.length,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 添加新的插件(不在现有列表中的)
|
||||||
|
displayedPlugins.value.forEach(plugin => {
|
||||||
|
if (!items.find(item => item.type === 'plugin' && item.id === plugin.id)) {
|
||||||
|
items.push({
|
||||||
|
type: 'plugin',
|
||||||
|
id: plugin.id || '',
|
||||||
|
data: plugin,
|
||||||
|
order: items.length,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// 初次创建混合列表,按文件夹排序和插件排序交替排列
|
||||||
|
const sortedFolders = [...displayedFolders.value].sort((a, b) => {
|
||||||
|
const aIndex = folderOrder.value.indexOf(a.name)
|
||||||
|
const bIndex = folderOrder.value.indexOf(b.name)
|
||||||
|
return (aIndex === -1 ? 999 : aIndex) - (bIndex === -1 ? 999 : bIndex)
|
||||||
|
})
|
||||||
|
|
||||||
|
const sortedPlugins = [...displayedPlugins.value].sort((a, b) => {
|
||||||
|
const aIndex = orderConfig.value.findIndex((item: { id: string }) => item.id === a.id)
|
||||||
|
const bIndex = orderConfig.value.findIndex((item: { id: string }) => item.id === b.id)
|
||||||
|
return (aIndex === -1 ? 999 : aIndex) - (bIndex === -1 ? 999 : bIndex)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 先添加所有文件夹
|
||||||
|
sortedFolders.forEach(folder => {
|
||||||
|
items.push({
|
||||||
|
type: 'folder',
|
||||||
|
id: folder.name,
|
||||||
|
data: folder,
|
||||||
|
order: globalOrder++,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// 再添加所有插件
|
||||||
|
sortedPlugins.forEach(plugin => {
|
||||||
|
items.push({
|
||||||
|
type: 'plugin',
|
||||||
|
id: plugin.id || '',
|
||||||
|
data: plugin,
|
||||||
|
order: globalOrder++,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按order排序
|
||||||
|
items.sort((a, b) => a.order - b.order)
|
||||||
|
mixedSortList.value = items
|
||||||
|
} else {
|
||||||
|
// 文件夹内:只更新插件列表
|
||||||
|
draggableFolderPlugins.value = [...displayedPlugins.value]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听相关数据变化,更新混合排序列表
|
||||||
|
watch(
|
||||||
|
[displayedPlugins, displayedFolders, orderConfig, folderOrder],
|
||||||
|
() => {
|
||||||
|
// 只有在非拖拽状态下才更新
|
||||||
|
if (!isDraggingSortMode.value) {
|
||||||
|
updateMixedSortList()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
deep: true,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// 监听文件夹切换,更新列表
|
||||||
|
watch(currentFolder, () => {
|
||||||
|
// 只有在非拖拽状态下才更新
|
||||||
|
if (!isDraggingSortMode.value) {
|
||||||
|
updateMixedSortList()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// 加载插件顺序
|
// 加载插件顺序
|
||||||
async function loadPluginOrderConfig() {
|
async function loadPluginOrderConfig() {
|
||||||
// 顺序配置
|
// 顺序配置
|
||||||
@@ -301,37 +416,57 @@ async function savePluginOrder() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存主列表插件顺序
|
// 保存混合排序
|
||||||
async function saveMainPluginOrder() {
|
async function saveMixedSortOrder() {
|
||||||
try {
|
try {
|
||||||
// 更新主列表数据
|
// 分离文件夹和插件
|
||||||
const newOrderedList = [...draggableMainPlugins.value]
|
const newFolderOrder: string[] = []
|
||||||
|
const newPluginOrder: Plugin[] = []
|
||||||
|
|
||||||
// 添加文件夹中的插件到末尾
|
mixedSortList.value.forEach(item => {
|
||||||
|
if (item.type === 'folder') {
|
||||||
|
newFolderOrder.push(item.id)
|
||||||
|
} else if (item.type === 'plugin') {
|
||||||
|
newPluginOrder.push(item.data)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 更新文件夹排序
|
||||||
|
folderOrder.value = newFolderOrder
|
||||||
|
|
||||||
|
// 添加文件夹中的插件到插件列表末尾
|
||||||
Object.values(pluginFolders.value).forEach(folderData => {
|
Object.values(pluginFolders.value).forEach(folderData => {
|
||||||
const plugins = Array.isArray(folderData) ? folderData : folderData.plugins || []
|
const plugins = Array.isArray(folderData) ? folderData : folderData.plugins || []
|
||||||
plugins.forEach((id: string) => {
|
plugins.forEach((id: string) => {
|
||||||
const folderPlugin = dataList.value.find(p => p.id === id)
|
const folderPlugin = dataList.value.find(p => p.id === id)
|
||||||
if (folderPlugin && !newOrderedList.find(p => p.id === id)) {
|
if (folderPlugin && !newPluginOrder.find(p => p.id === id)) {
|
||||||
newOrderedList.push(folderPlugin)
|
newPluginOrder.push(folderPlugin)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
filteredDataList.value = newOrderedList
|
// 更新插件列表
|
||||||
|
filteredDataList.value = newPluginOrder
|
||||||
|
|
||||||
// 保存排序配置
|
// 保存插件排序配置
|
||||||
const orderObj = newOrderedList.map(item => ({ id: item.id || '' }))
|
const orderObj = newPluginOrder.map(item => ({ id: item.id || '' }))
|
||||||
orderConfig.value = orderObj
|
orderConfig.value = orderObj
|
||||||
const orderString = JSON.stringify(orderObj)
|
const orderString = JSON.stringify(orderObj)
|
||||||
localStorage.setItem('MP_PLUGIN_ORDER', orderString)
|
localStorage.setItem('MP_PLUGIN_ORDER', orderString)
|
||||||
|
|
||||||
// 保存到服务端
|
// 保存到服务端
|
||||||
await api.post('/user/config/PluginOrder', orderObj)
|
await api.post('/user/config/PluginOrder', orderObj)
|
||||||
|
|
||||||
|
// 保存文件夹排序
|
||||||
|
await savePluginFolders()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.error('保存排序失败:', error)
|
||||||
} finally {
|
} finally {
|
||||||
// 清除拖拽标志
|
// 清除拖拽标志
|
||||||
isDraggingSortMode.value = false
|
isDraggingSortMode.value = false
|
||||||
|
|
||||||
|
// 在清除拖拽标志后更新混合排序列表显示
|
||||||
|
updateMixedSortList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -885,33 +1020,10 @@ async function updateFolderConfig(folderName: string, config: any) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 文件夹拖拽排序结束事件
|
|
||||||
function onFolderSortEnd() {
|
|
||||||
// 保存新的文件夹顺序
|
|
||||||
savePluginFolders()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 当前拖拽的插件ID
|
// 当前拖拽的插件ID
|
||||||
const currentDraggedPluginId = ref('')
|
const currentDraggedPluginId = ref('')
|
||||||
|
|
||||||
// 处理拖拽到文件夹的事件
|
// 处理拖拽到文件夹的事件
|
||||||
function handleDragOver(event: DragEvent) {
|
|
||||||
event.preventDefault()
|
|
||||||
event.dataTransfer!.dropEffect = 'move'
|
|
||||||
const target = event.currentTarget as HTMLElement
|
|
||||||
target.classList.add('drag-over')
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleDragEnter(event: DragEvent) {
|
|
||||||
event.preventDefault()
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleDragLeave(event: DragEvent) {
|
|
||||||
event.preventDefault()
|
|
||||||
const target = event.currentTarget as HTMLElement
|
|
||||||
target.classList.remove('drag-over')
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleDropToFolder(event: DragEvent, folderName: string) {
|
async function handleDropToFolder(event: DragEvent, folderName: string) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
@@ -964,9 +1076,9 @@ async function handleDropToFolder(event: DragEvent, folderName: string) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// 从主列表中移除(如果存在)
|
// 从主列表中移除(如果存在)
|
||||||
const mainIndex = draggableMainPlugins.value.findIndex(p => p.id === pluginId)
|
const mainIndex = mixedSortList.value.findIndex(item => item.type === 'plugin' && item.id === pluginId)
|
||||||
if (mainIndex > -1) {
|
if (mainIndex > -1) {
|
||||||
draggableMainPlugins.value.splice(mainIndex, 1)
|
mixedSortList.value.splice(mainIndex, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加到目标文件夹
|
// 添加到目标文件夹
|
||||||
@@ -993,6 +1105,9 @@ async function handleDropToFolder(event: DragEvent, folderName: string) {
|
|||||||
// 保存配置
|
// 保存配置
|
||||||
await savePluginFolders()
|
await savePluginFolders()
|
||||||
|
|
||||||
|
// 更新混合排序列表
|
||||||
|
updateMixedSortList()
|
||||||
|
|
||||||
$toast.success(`插件已移动到文件夹 "${folderName}"`)
|
$toast.success(`插件已移动到文件夹 "${folderName}"`)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('拖拽到文件夹失败:', error)
|
console.error('拖拽到文件夹失败:', error)
|
||||||
@@ -1008,10 +1123,18 @@ function onDragStartPlugin(evt: any) {
|
|||||||
// 从oldIndex获取插件ID
|
// 从oldIndex获取插件ID
|
||||||
const oldIndex = evt.oldIndex
|
const oldIndex = evt.oldIndex
|
||||||
if (oldIndex !== undefined) {
|
if (oldIndex !== undefined) {
|
||||||
const plugin = currentFolder.value ? draggableFolderPlugins.value[oldIndex] : draggableMainPlugins.value[oldIndex]
|
if (currentFolder.value) {
|
||||||
if (plugin && plugin.id) {
|
const plugin = draggableFolderPlugins.value[oldIndex]
|
||||||
currentDraggedPluginId.value = plugin.id
|
if (plugin && plugin.id) {
|
||||||
return
|
currentDraggedPluginId.value = plugin.id
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const item = mixedSortList.value[oldIndex]
|
||||||
|
if (item && item.id) {
|
||||||
|
currentDraggedPluginId.value = item.id
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1038,8 +1161,8 @@ function onDragStartPlugin(evt: any) {
|
|||||||
// 拖拽结束事件
|
// 拖拽结束事件
|
||||||
function onDragEndPlugin(evt: any) {
|
function onDragEndPlugin(evt: any) {
|
||||||
currentDraggedPluginId.value = ''
|
currentDraggedPluginId.value = ''
|
||||||
// 清除拖拽标志
|
// 注意:不在这里清除拖拽标志,而是在保存函数中清除
|
||||||
isDraggingSortMode.value = false
|
// 这样可以避免在拖拽过程中触发watch监听器
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -1207,66 +1330,37 @@ function onDragEndPlugin(evt: any) {
|
|||||||
<LoadingBanner v-if="!isRefreshed" class="mt-12" />
|
<LoadingBanner v-if="!isRefreshed" class="mt-12" />
|
||||||
|
|
||||||
<!-- 文件夹和插件网格 -->
|
<!-- 文件夹和插件网格 -->
|
||||||
<div v-if="displayedFolders.length > 0 || displayedPlugins.length > 0" class="grid gap-4 grid-plugin-card">
|
<div v-if="mixedSortList.length > 0 || displayedPlugins.length > 0">
|
||||||
<!-- 文件夹卡片 - 使用draggable进行排序 -->
|
<!-- 混合排序列表(文件夹和插件) -->
|
||||||
<draggable
|
|
||||||
v-if="displayedFolders.length > 0 && isRefreshed"
|
|
||||||
v-model="folderOrder"
|
|
||||||
@end="onFolderSortEnd"
|
|
||||||
handle=".cursor-move"
|
|
||||||
item-key="name"
|
|
||||||
tag="div"
|
|
||||||
:component-data="{ style: 'display: contents;' }"
|
|
||||||
:disabled="currentFolder !== ''"
|
|
||||||
group="folders"
|
|
||||||
>
|
|
||||||
<template #item="{ element: folderName }">
|
|
||||||
<div
|
|
||||||
v-if="displayedFolders.find(f => f.name === folderName)"
|
|
||||||
class="drop-zone"
|
|
||||||
@dragover="handleDragOver($event)"
|
|
||||||
@dragenter="handleDragEnter($event)"
|
|
||||||
@dragleave="handleDragLeave($event)"
|
|
||||||
@drop="handleDropToFolder($event, folderName)"
|
|
||||||
>
|
|
||||||
<PluginFolderCard
|
|
||||||
:folder-name="folderName"
|
|
||||||
:plugin-count="displayedFolders.find(f => f.name === folderName)?.pluginCount || 0"
|
|
||||||
:folder-config="displayedFolders.find(f => f.name === folderName)?.config || {}"
|
|
||||||
@open="openFolder"
|
|
||||||
@delete="deleteFolder"
|
|
||||||
@rename="renameFolder"
|
|
||||||
@update-config="updateFolderConfig"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</draggable>
|
|
||||||
|
|
||||||
<!-- 插件卡片 -->
|
|
||||||
<template v-if="!currentFolder">
|
<template v-if="!currentFolder">
|
||||||
<!-- 主列表:使用draggable进行排序 -->
|
<!-- 主列表:使用draggable进行混合排序 -->
|
||||||
<draggable
|
<draggable
|
||||||
v-model="draggableMainPlugins"
|
v-model="mixedSortList"
|
||||||
@end="saveMainPluginOrder"
|
@end="saveMixedSortOrder"
|
||||||
@start="onDragStartPlugin"
|
@start="onDragStartPlugin"
|
||||||
@sort="onDragEndPlugin"
|
|
||||||
handle=".cursor-move"
|
handle=".cursor-move"
|
||||||
item-key="id"
|
item-key="id"
|
||||||
tag="div"
|
tag="div"
|
||||||
:component-data="{ style: 'display: contents;' }"
|
class="grid gap-4 grid-plugin-card"
|
||||||
group="plugins"
|
group="mixed"
|
||||||
>
|
>
|
||||||
<template #item="{ element }">
|
<template #item="{ element }">
|
||||||
<div class="plugin-item-wrapper" :data-plugin-id="element.id">
|
<MixedSortCard
|
||||||
<PluginCard
|
:item="element"
|
||||||
:count="PluginStatistics[element.id || '0']"
|
:plugin-statistics="PluginStatistics"
|
||||||
:plugin="element"
|
:plugin-actions="pluginActions"
|
||||||
:action="pluginActions[element.id || '0']"
|
@open-folder="openFolder"
|
||||||
@remove="refreshData"
|
@delete-folder="deleteFolder"
|
||||||
@save="refreshData"
|
@rename-folder="(oldName, newName) => renameFolder(oldName, newName)"
|
||||||
@action-done="pluginActions[element.id || '0'] = false"
|
@update-folder-config="(folderName, config) => updateFolderConfig(folderName, config)"
|
||||||
/>
|
@refresh-data="refreshData"
|
||||||
</div>
|
@action-done="
|
||||||
|
pluginId => {
|
||||||
|
pluginActions[pluginId] = false
|
||||||
|
}
|
||||||
|
"
|
||||||
|
@drop-to-folder="(event, folderName) => handleDropToFolder(event, folderName)"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</draggable>
|
</draggable>
|
||||||
</template>
|
</template>
|
||||||
@@ -1280,29 +1374,23 @@ function onDragEndPlugin(evt: any) {
|
|||||||
handle=".cursor-move"
|
handle=".cursor-move"
|
||||||
item-key="id"
|
item-key="id"
|
||||||
tag="div"
|
tag="div"
|
||||||
:component-data="{ style: 'display: contents;' }"
|
class="grid gap-4 grid-plugin-card"
|
||||||
group="plugins"
|
group="plugins"
|
||||||
>
|
>
|
||||||
<template #item="{ element }">
|
<template #item="{ element }">
|
||||||
<div class="plugin-item-wrapper" :data-plugin-id="element.id">
|
<MixedSortCard
|
||||||
<PluginCard
|
:item="{ type: 'plugin', id: element.id, data: element, order: 0 }"
|
||||||
:count="PluginStatistics[element.id || '0']"
|
:plugin-statistics="PluginStatistics"
|
||||||
:plugin="element"
|
:plugin-actions="pluginActions"
|
||||||
:action="pluginActions[element.id || '0']"
|
:show-remove-button="true"
|
||||||
@remove="refreshData"
|
@refresh-data="refreshData"
|
||||||
@save="refreshData"
|
@action-done="
|
||||||
@action-done="pluginActions[element.id || '0'] = false"
|
pluginId => {
|
||||||
/>
|
pluginActions[pluginId] = false
|
||||||
<!-- 移出文件夹按钮 -->
|
}
|
||||||
<VBtn
|
"
|
||||||
icon="mdi-folder-remove"
|
@remove-from-folder="removeFromFolder"
|
||||||
variant="text"
|
/>
|
||||||
color="warning"
|
|
||||||
size="small"
|
|
||||||
class="remove-from-folder-btn"
|
|
||||||
@click="removeFromFolder(element.id || '')"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
</draggable>
|
</draggable>
|
||||||
</template>
|
</template>
|
||||||
@@ -1477,35 +1565,5 @@ function onDragEndPlugin(evt: any) {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
// 拖拽相关样式
|
// 样式已移至 MixedSortCard 组件
|
||||||
.drop-zone {
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
|
|
||||||
&.drag-over {
|
|
||||||
transform: scale(1.02);
|
|
||||||
box-shadow: 0 0 20px rgba(33, 150, 243, 0.5);
|
|
||||||
border: 2px dashed #2196f3;
|
|
||||||
border-radius: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.plugin-item-wrapper {
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
.remove-from-folder-btn {
|
|
||||||
position: absolute;
|
|
||||||
top: 4px;
|
|
||||||
right: 4px;
|
|
||||||
z-index: 10;
|
|
||||||
background: rgba(255, 255, 255, 0.1);
|
|
||||||
backdrop-filter: blur(4px);
|
|
||||||
border-radius: 50%;
|
|
||||||
opacity: 0;
|
|
||||||
transition: opacity 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover .remove-from-folder-btn {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user