mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2025-05-06 10:35:15 +08:00
feat(editor): 支持拖拽调整页面顺序
This commit is contained in:
parent
0ffc223459
commit
0c5485b1d0
@ -61,13 +61,15 @@
|
|||||||
"keycon": "^1.4.0",
|
"keycon": "^1.4.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"moveable": "^0.53.0",
|
"moveable": "^0.53.0",
|
||||||
"serialize-javascript": "^6.0.0"
|
"serialize-javascript": "^6.0.0",
|
||||||
|
"sortablejs": "^1.15.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/events": "^3.0.0",
|
"@types/events": "^3.0.0",
|
||||||
"@types/lodash-es": "^4.17.4",
|
"@types/lodash-es": "^4.17.4",
|
||||||
"@types/node": "^18.19.0",
|
"@types/node": "^18.19.0",
|
||||||
"@types/serialize-javascript": "^5.0.1",
|
"@types/serialize-javascript": "^5.0.1",
|
||||||
|
"@types/sortablejs": "^1.15.8",
|
||||||
"@vitejs/plugin-vue": "^5.0.4",
|
"@vitejs/plugin-vue": "^5.0.4",
|
||||||
"@vue/compiler-sfc": "^3.4.27",
|
"@vue/compiler-sfc": "^3.4.27",
|
||||||
"@vue/test-utils": "^2.4.6",
|
"@vue/test-utils": "^2.4.6",
|
||||||
@ -86,8 +88,8 @@
|
|||||||
"@tmagic/stage": "workspace:*",
|
"@tmagic/stage": "workspace:*",
|
||||||
"@tmagic/utils": "workspace:*",
|
"@tmagic/utils": "workspace:*",
|
||||||
"monaco-editor": "^0.48.0",
|
"monaco-editor": "^0.48.0",
|
||||||
"vue": "^3.4.27",
|
"typescript": "*",
|
||||||
"typescript": "*"
|
"vue": "^3.4.27"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
"typescript": {
|
"typescript": {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<Framework :disabled-page-fragment="disabledPageFragment">
|
<Framework :disabled-page-fragment="disabledPageFragment" :page-bar-sort-options="pageBarSortOptions">
|
||||||
<template #header>
|
<template #header>
|
||||||
<slot name="header"></slot>
|
<slot name="header"></slot>
|
||||||
</template>
|
</template>
|
||||||
@ -106,6 +106,7 @@
|
|||||||
<template #page-bar><slot name="page-bar"></slot></template>
|
<template #page-bar><slot name="page-bar"></slot></template>
|
||||||
<template #page-bar-title="{ page }"><slot name="page-bar-title" :page="page"></slot></template>
|
<template #page-bar-title="{ page }"><slot name="page-bar-title" :page="page"></slot></template>
|
||||||
<template #page-bar-popover="{ page }"><slot name="page-bar-popover" :page="page"></slot></template>
|
<template #page-bar-popover="{ page }"><slot name="page-bar-popover" :page="page"></slot></template>
|
||||||
|
<template #page-list-popover="{ list }"><slot name="page-list-popover" :list="list"></slot></template>
|
||||||
</Framework>
|
</Framework>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ import type {
|
|||||||
MenuBarData,
|
MenuBarData,
|
||||||
MenuButton,
|
MenuButton,
|
||||||
MenuComponent,
|
MenuComponent,
|
||||||
|
PageBarSortOptions,
|
||||||
SideBarData,
|
SideBarData,
|
||||||
StageRect,
|
StageRect,
|
||||||
} from './type';
|
} from './type';
|
||||||
@ -90,6 +91,8 @@ export interface EditorProps {
|
|||||||
/** 用于自定义组件树与画布的右键菜单 */
|
/** 用于自定义组件树与画布的右键菜单 */
|
||||||
customContentMenu?: (menus: (MenuButton | MenuComponent)[], type: string) => (MenuButton | MenuComponent)[];
|
customContentMenu?: (menus: (MenuButton | MenuComponent)[], type: string) => (MenuButton | MenuComponent)[];
|
||||||
extendFormState?: (state: FormState) => Record<string, any> | Promise<Record<string, any>>;
|
extendFormState?: (state: FormState) => Record<string, any> | Promise<Record<string, any>>;
|
||||||
|
/** 页面顺序拖拽配置参数 */
|
||||||
|
pageBarSortOptions?: PageBarSortOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defaultEditorProps = {
|
export const defaultEditorProps = {
|
||||||
|
@ -38,9 +38,10 @@
|
|||||||
</slot>
|
</slot>
|
||||||
|
|
||||||
<slot name="page-bar">
|
<slot name="page-bar">
|
||||||
<PageBar :disabled-page-fragment="disabledPageFragment">
|
<PageBar :disabled-page-fragment="disabledPageFragment" :page-bar-sort-options="pageBarSortOptions">
|
||||||
<template #page-bar-title="{ page }"><slot name="page-bar-title" :page="page"></slot></template>
|
<template #page-bar-title="{ page }"><slot name="page-bar-title" :page="page"></slot></template>
|
||||||
<template #page-bar-popover="{ page }"><slot name="page-bar-popover" :page="page"></slot></template>
|
<template #page-bar-popover="{ page }"><slot name="page-bar-popover" :page="page"></slot></template>
|
||||||
|
<template #page-list-popover="{ list }"><slot name="page-list-popover" :list="list"></slot></template>
|
||||||
</PageBar>
|
</PageBar>
|
||||||
</slot>
|
</slot>
|
||||||
</template>
|
</template>
|
||||||
@ -63,7 +64,7 @@ import { computed, inject, onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
|||||||
import { TMagicScrollbar } from '@tmagic/design';
|
import { TMagicScrollbar } from '@tmagic/design';
|
||||||
|
|
||||||
import SplitView from '@editor/components/SplitView.vue';
|
import SplitView from '@editor/components/SplitView.vue';
|
||||||
import type { FrameworkSlots, GetColumnWidth, Services } from '@editor/type';
|
import type { FrameworkSlots, GetColumnWidth, PageBarSortOptions, Services } from '@editor/type';
|
||||||
import { getConfig } from '@editor/utils/config';
|
import { getConfig } from '@editor/utils/config';
|
||||||
|
|
||||||
import PageBar from './page-bar/PageBar.vue';
|
import PageBar from './page-bar/PageBar.vue';
|
||||||
@ -78,6 +79,7 @@ defineOptions({
|
|||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
disabledPageFragment: boolean;
|
disabledPageFragment: boolean;
|
||||||
|
pageBarSortOptions?: PageBarSortOptions;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const DEFAULT_LEFT_COLUMN_WIDTH = 310;
|
const DEFAULT_LEFT_COLUMN_WIDTH = 310;
|
||||||
|
@ -2,15 +2,19 @@
|
|||||||
<div class="m-editor-page-bar-tabs">
|
<div class="m-editor-page-bar-tabs">
|
||||||
<SwitchTypeButton v-if="!disabledPageFragment" v-model="active" />
|
<SwitchTypeButton v-if="!disabledPageFragment" v-model="active" />
|
||||||
|
|
||||||
<PageBarScrollContainer :type="active">
|
<PageBarScrollContainer :type="active" :page-bar-sort-options="pageBarSortOptions">
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<AddButton :type="active"></AddButton>
|
<AddButton :type="active"></AddButton>
|
||||||
|
<PageList :list="list">
|
||||||
|
<template #page-list-popover="{ list }"><slot name="page-list-popover" :list="list"></slot></template>
|
||||||
|
</PageList>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-for="item in list"
|
v-for="item in list"
|
||||||
class="m-editor-page-bar-item"
|
class="m-editor-page-bar-item"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
|
:page-id="item.id"
|
||||||
:class="{ active: page?.id === item.id }"
|
:class="{ active: page?.id === item.id }"
|
||||||
@click="switchPage(item.id)"
|
@click="switchPage(item.id)"
|
||||||
>
|
>
|
||||||
@ -61,11 +65,12 @@ import { Id, type MPage, type MPageFragment, NodeType } from '@tmagic/schema';
|
|||||||
import { isPage, isPageFragment } from '@tmagic/utils';
|
import { isPage, isPageFragment } from '@tmagic/utils';
|
||||||
|
|
||||||
import ToolButton from '@editor/components/ToolButton.vue';
|
import ToolButton from '@editor/components/ToolButton.vue';
|
||||||
import type { Services } from '@editor/type';
|
import type { PageBarSortOptions, Services } from '@editor/type';
|
||||||
import { getPageFragmentList, getPageList } from '@editor/utils';
|
import { getPageFragmentList, getPageList } from '@editor/utils';
|
||||||
|
|
||||||
import AddButton from './AddButton.vue';
|
import AddButton from './AddButton.vue';
|
||||||
import PageBarScrollContainer from './PageBarScrollContainer.vue';
|
import PageBarScrollContainer from './PageBarScrollContainer.vue';
|
||||||
|
import PageList from './PageList.vue';
|
||||||
import SwitchTypeButton from './SwitchTypeButton.vue';
|
import SwitchTypeButton from './SwitchTypeButton.vue';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
@ -74,6 +79,7 @@ defineOptions({
|
|||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
disabledPageFragment: boolean;
|
disabledPageFragment: boolean;
|
||||||
|
pageBarSortOptions?: PageBarSortOptions;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const active = ref<NodeType.PAGE | NodeType.PAGE_FRAGMENT>(NodeType.PAGE);
|
const active = ref<NodeType.PAGE | NodeType.PAGE_FRAGMENT>(NodeType.PAGE);
|
||||||
|
@ -34,11 +34,12 @@ import {
|
|||||||
type WatchStopHandle,
|
type WatchStopHandle,
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
import { ArrowLeftBold, ArrowRightBold } from '@element-plus/icons-vue';
|
import { ArrowLeftBold, ArrowRightBold } from '@element-plus/icons-vue';
|
||||||
|
import Sortable, { SortableEvent } from 'sortablejs';
|
||||||
|
|
||||||
import { NodeType } from '@tmagic/schema';
|
import { Id, NodeType } from '@tmagic/schema';
|
||||||
|
|
||||||
import Icon from '@editor/components/Icon.vue';
|
import Icon from '@editor/components/Icon.vue';
|
||||||
import type { Services } from '@editor/type';
|
import type { PageBarSortOptions, Services } from '@editor/type';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'MEditorPageBarScrollContainer',
|
name: 'MEditorPageBarScrollContainer',
|
||||||
@ -46,23 +47,29 @@ defineOptions({
|
|||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
type: NodeType.PAGE | NodeType.PAGE_FRAGMENT;
|
type: NodeType.PAGE | NodeType.PAGE_FRAGMENT;
|
||||||
|
pageBarSortOptions?: PageBarSortOptions;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const services = inject<Services>('services');
|
const services = inject<Services>('services');
|
||||||
const editorService = services?.editorService;
|
const editorService = services?.editorService;
|
||||||
const uiService = services?.uiService;
|
const uiService = services?.uiService;
|
||||||
|
|
||||||
const itemsContainer = ref<HTMLDivElement>();
|
const itemsContainer = ref<HTMLElement>();
|
||||||
const canScroll = ref(false);
|
const canScroll = ref(false);
|
||||||
|
|
||||||
const showAddPageButton = computed(() => uiService?.get('showAddPageButton'));
|
const showAddPageButton = computed(() => uiService?.get('showAddPageButton'));
|
||||||
|
const showPageListButton = computed(() => uiService?.get('showPageListButton'));
|
||||||
|
|
||||||
const itemsContainerWidth = ref(0);
|
const itemsContainerWidth = ref(0);
|
||||||
|
|
||||||
const setCanScroll = () => {
|
const setCanScroll = () => {
|
||||||
// 减去新增、左移、右移三个按钮的宽度
|
// 减去新增、左移、右移三个按钮的宽度
|
||||||
// 37 = icon width 16 + padding 10 * 2 + border-right 1
|
// 37 = icon width 16 + padding 10 * 2 + border-right 1
|
||||||
itemsContainerWidth.value = (pageBar.value?.clientWidth || 0) - 37 * 2 - (showAddPageButton.value ? 37 : 21);
|
itemsContainerWidth.value =
|
||||||
|
(pageBar.value?.clientWidth || 0) -
|
||||||
|
37 * 2 -
|
||||||
|
(showAddPageButton.value ? 37 : 21) -
|
||||||
|
(showPageListButton.value ? 37 : 0);
|
||||||
|
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (itemsContainer.value) {
|
if (itemsContainer.value) {
|
||||||
@ -126,6 +133,35 @@ const crateWatchLength = (length: ComputedRef<number>) =>
|
|||||||
} else {
|
} else {
|
||||||
scroll('end');
|
scroll('end');
|
||||||
}
|
}
|
||||||
|
if (length > 1) {
|
||||||
|
const el = document.querySelector('.m-editor-page-bar-items') as HTMLElement;
|
||||||
|
let beforeDragList: Id[] = [];
|
||||||
|
const options = {
|
||||||
|
...{
|
||||||
|
dataIdAttr: 'page-id', // 获取排序后的数据
|
||||||
|
onStart: async (event: SortableEvent) => {
|
||||||
|
if (typeof props.pageBarSortOptions?.beforeStart === 'function') {
|
||||||
|
await props.pageBarSortOptions.beforeStart(event, sortable);
|
||||||
|
}
|
||||||
|
beforeDragList = sortable.toArray();
|
||||||
|
},
|
||||||
|
onUpdate: async (event: SortableEvent) => {
|
||||||
|
await editorService?.sort(
|
||||||
|
beforeDragList[event.oldIndex as number],
|
||||||
|
beforeDragList[event.newIndex as number],
|
||||||
|
);
|
||||||
|
if (typeof props.pageBarSortOptions?.afterUpdate === 'function') {
|
||||||
|
await props.pageBarSortOptions.afterUpdate(event, sortable);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...{
|
||||||
|
...(props.pageBarSortOptions ? props.pageBarSortOptions : {}),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if (!el) return;
|
||||||
|
const sortable = new Sortable(el, options);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
55
packages/editor/src/layouts/page-bar/PageList.vue
Normal file
55
packages/editor/src/layouts/page-bar/PageList.vue
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
v-if="showPageListButton"
|
||||||
|
id="m-editor-page-bar-list-icon"
|
||||||
|
class="m-editor-page-bar-item m-editor-page-bar-item-icon"
|
||||||
|
>
|
||||||
|
<TMagicPopover popper-class="page-bar-popover" placement="top" :width="160" trigger="hover">
|
||||||
|
<div>
|
||||||
|
<slot name="page-list-popover" :list="list">
|
||||||
|
<ToolButton
|
||||||
|
v-for="(item, index) in list"
|
||||||
|
:data="{
|
||||||
|
type: 'button',
|
||||||
|
text: item.devconfig?.tabName || item.name || item.id,
|
||||||
|
handler: () => switchPage(item.id),
|
||||||
|
}"
|
||||||
|
:key="index"
|
||||||
|
></ToolButton>
|
||||||
|
</slot>
|
||||||
|
</div>
|
||||||
|
<template #reference>
|
||||||
|
<TMagicIcon class="m-editor-page-list-menu-icon">
|
||||||
|
<Files></Files>
|
||||||
|
</TMagicIcon>
|
||||||
|
</template>
|
||||||
|
</TMagicPopover>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, inject } from 'vue';
|
||||||
|
import { Files } from '@element-plus/icons-vue';
|
||||||
|
|
||||||
|
import { TMagicIcon, TMagicPopover } from '@tmagic/design';
|
||||||
|
import { Id, MPage, MPageFragment } from '@tmagic/schema';
|
||||||
|
|
||||||
|
import ToolButton from '@editor/components/ToolButton.vue';
|
||||||
|
import type { Services } from '@editor/type';
|
||||||
|
defineOptions({
|
||||||
|
name: 'MEditorPageList',
|
||||||
|
});
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
list: MPage[] | MPageFragment[];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const services = inject<Services>('services');
|
||||||
|
const uiService = services?.uiService;
|
||||||
|
const editorService = services?.editorService;
|
||||||
|
|
||||||
|
const showPageListButton = computed(() => uiService?.get('showPageListButton'));
|
||||||
|
const switchPage = (id: Id) => {
|
||||||
|
editorService?.select(id);
|
||||||
|
};
|
||||||
|
</script>
|
@ -47,6 +47,7 @@ const state = reactive<UiState>({
|
|||||||
showRule: true,
|
showRule: true,
|
||||||
propsPanelSize: 'small',
|
propsPanelSize: 'small',
|
||||||
showAddPageButton: true,
|
showAddPageButton: true,
|
||||||
|
showPageListButton: true,
|
||||||
hideSlideBar: false,
|
hideSlideBar: false,
|
||||||
sideBarItems: [],
|
sideBarItems: [],
|
||||||
navMenuRect: {
|
navMenuRect: {
|
||||||
|
@ -18,6 +18,19 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.m-editor-page-list-item {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
height: $--page-bar-height;
|
||||||
|
line-height: $--page-bar-height;
|
||||||
|
color: $--font-color;
|
||||||
|
z-index: 2;
|
||||||
|
overflow: hidden;
|
||||||
|
&:hover {
|
||||||
|
background-color: $--hover-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.m-editor-page-bar {
|
.m-editor-page-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
import type { Component } from 'vue';
|
import type { Component } from 'vue';
|
||||||
import type EventEmitter from 'events';
|
import type EventEmitter from 'events';
|
||||||
|
import Sortable, { Options, SortableEvent } from 'sortablejs';
|
||||||
import type { PascalCasedProperties } from 'type-fest';
|
import type { PascalCasedProperties } from 'type-fest';
|
||||||
|
|
||||||
import type { ChildConfig, ColumnConfig, FilterFunction, FormConfig, FormItem, FormState, Input } from '@tmagic/form';
|
import type { ChildConfig, ColumnConfig, FilterFunction, FormConfig, FormItem, FormState, Input } from '@tmagic/form';
|
||||||
@ -56,7 +57,6 @@ import type { StageOverlayService } from './services/stageOverlay';
|
|||||||
import type { StorageService } from './services/storage';
|
import type { StorageService } from './services/storage';
|
||||||
import type { UiService } from './services/ui';
|
import type { UiService } from './services/ui';
|
||||||
import type { UndoRedo } from './utils/undo-redo';
|
import type { UndoRedo } from './utils/undo-redo';
|
||||||
|
|
||||||
export interface FrameworkSlots {
|
export interface FrameworkSlots {
|
||||||
header(props: {}): any;
|
header(props: {}): any;
|
||||||
nav(props: {}): any;
|
nav(props: {}): any;
|
||||||
@ -71,6 +71,7 @@ export interface FrameworkSlots {
|
|||||||
'page-bar'(props: {}): any;
|
'page-bar'(props: {}): any;
|
||||||
'page-bar-title'(props: { page: MPage | MPageFragment }): any;
|
'page-bar-title'(props: { page: MPage | MPageFragment }): any;
|
||||||
'page-bar-popover'(props: { page: MPage | MPageFragment }): any;
|
'page-bar-popover'(props: { page: MPage | MPageFragment }): any;
|
||||||
|
'page-list-popover'(props: { list: MPage[] | MPageFragment[] }): any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WorkspaceSlots {
|
export interface WorkspaceSlots {
|
||||||
@ -239,6 +240,8 @@ export interface UiState {
|
|||||||
propsPanelSize: 'large' | 'default' | 'small';
|
propsPanelSize: 'large' | 'default' | 'small';
|
||||||
/** 是否显示新增页面按钮 */
|
/** 是否显示新增页面按钮 */
|
||||||
showAddPageButton: boolean;
|
showAddPageButton: boolean;
|
||||||
|
/** 是否在页面工具栏显示呼起页面列表按钮 */
|
||||||
|
showPageListButton: boolean;
|
||||||
/** 是否隐藏侧边栏 */
|
/** 是否隐藏侧边栏 */
|
||||||
hideSlideBar: boolean;
|
hideSlideBar: boolean;
|
||||||
/** 侧边栏面板配置 */
|
/** 侧边栏面板配置 */
|
||||||
@ -762,3 +765,11 @@ export interface EventBus extends EventEmitter {
|
|||||||
|
|
||||||
export type PropsFormConfigFunction = (data: { editorService: EditorService }) => FormConfig;
|
export type PropsFormConfigFunction = (data: { editorService: EditorService }) => FormConfig;
|
||||||
export type PropsFormValueFunction = (data: { editorService: EditorService }) => Partial<MNode>;
|
export type PropsFormValueFunction = (data: { editorService: EditorService }) => Partial<MNode>;
|
||||||
|
|
||||||
|
export type PartSortableOptions = Omit<Options, 'onStart' | 'onUpdate'>;
|
||||||
|
export interface PageBarSortOptions extends PartSortableOptions {
|
||||||
|
/** 在onUpdate之后调用 */
|
||||||
|
afterUpdate: (event: SortableEvent, sortable: Sortable) => void;
|
||||||
|
/** 在onStart之前调用 */
|
||||||
|
beforeStart: (event: SortableEvent, sortable: Sortable) => void;
|
||||||
|
}
|
||||||
|
6
pnpm-lock.yaml
generated
6
pnpm-lock.yaml
generated
@ -346,6 +346,9 @@ importers:
|
|||||||
serialize-javascript:
|
serialize-javascript:
|
||||||
specifier: ^6.0.0
|
specifier: ^6.0.0
|
||||||
version: 6.0.2
|
version: 6.0.2
|
||||||
|
sortablejs:
|
||||||
|
specifier: ^1.15.2
|
||||||
|
version: 1.15.2
|
||||||
typescript:
|
typescript:
|
||||||
specifier: '*'
|
specifier: '*'
|
||||||
version: 5.4.5
|
version: 5.4.5
|
||||||
@ -365,6 +368,9 @@ importers:
|
|||||||
'@types/serialize-javascript':
|
'@types/serialize-javascript':
|
||||||
specifier: ^5.0.1
|
specifier: ^5.0.1
|
||||||
version: 5.0.4
|
version: 5.0.4
|
||||||
|
'@types/sortablejs':
|
||||||
|
specifier: ^1.15.8
|
||||||
|
version: 1.15.8
|
||||||
'@vitejs/plugin-vue':
|
'@vitejs/plugin-vue':
|
||||||
specifier: ^5.0.4
|
specifier: ^5.0.4
|
||||||
version: 5.0.5(vite@5.3.1(@types/node@18.19.34)(sass@1.77.5)(terser@5.31.1))(vue@3.4.29(typescript@5.4.5))
|
version: 5.0.5(vite@5.3.1(@types/node@18.19.34)(sass@1.77.5)(terser@5.31.1))(vue@3.4.29(typescript@5.4.5))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user