@arronqzy/vue-view 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -0
- package/package.json +49 -0
- package/src/env.d.ts +62 -0
- package/src/index.ts +4 -0
- package/src/panel/VueViewOnlinePreview.vue +276 -0
- package/src/panel/VueViewPanel.vue +871 -0
- package/src/panel/components/ConfigHintIcon.vue +34 -0
- package/src/panel/components/ElementsLayer.vue +165 -0
- package/src/panel/components/MaterialPreview.vue +135 -0
- package/src/panel/components/MaterialSidebar.vue +526 -0
- package/src/panel/components/MaterialSidebarTreeNode.vue +305 -0
- package/src/panel/components/MoveableLayer.vue +859 -0
- package/src/panel/components/PanelCanvas.vue +630 -0
- package/src/panel/components/PanelConfigSidebar.vue +397 -0
- package/src/panel/components/PanelRulers.vue +177 -0
- package/src/panel/components/SelectLayer.vue +115 -0
- package/src/panel/components/ViewElementScopePanel.vue +76 -0
- package/src/panel/components/WorkspaceConfigSidebar.vue +147 -0
- package/src/panel/components/WorkspaceProjectNav.vue +192 -0
- package/src/panel/components/WorkspaceStageSplit.vue +258 -0
- package/src/panel/components/config/ConfigColorField.vue +52 -0
- package/src/panel/components/config/ConfigFieldGroup.vue +20 -0
- package/src/panel/components/config/ConfigSection.vue +50 -0
- package/src/panel/components/config/PanelConfigAudioSection.vue +256 -0
- package/src/panel/components/config/PanelConfigChartSection.vue +650 -0
- package/src/panel/components/config/PanelConfigGeometrySection.vue +209 -0
- package/src/panel/components/config/PanelConfigGridChildSpan.vue +68 -0
- package/src/panel/components/config/PanelConfigGridSection.vue +103 -0
- package/src/panel/components/config/PanelConfigImageSection.vue +136 -0
- package/src/panel/components/config/PanelConfigMultiSelect.vue +434 -0
- package/src/panel/components/config/PanelConfigNodeInfo.vue +165 -0
- package/src/panel/components/config/PanelConfigReferenceSection.vue +77 -0
- package/src/panel/components/config/PanelConfigStyleSections.vue +208 -0
- package/src/panel/components/config/PanelConfigTextSection.vue +195 -0
- package/src/panel/components/config/PanelConfigVideoSection.vue +107 -0
- package/src/panel/components/config/shared.ts +74 -0
- package/src/panel/components/elementsLayerNodes.ts +830 -0
- package/src/panel/components/materialSidebarData.ts +85 -0
- package/src/panel/components/scope-config/ScopeConfigProvider.vue +153 -0
- package/src/panel/components/scope-config/ScopeTemplateAutocompleteHost.vue +234 -0
- package/src/panel/components/scope-config/ScopeTemplatePreviewHost.vue +192 -0
- package/src/panel/components/scope-config/ScopeTemplatePreviewPanel.vue +42 -0
- package/src/panel/components/scope-config/ScopeTemplateUsageHint.vue +20 -0
- package/src/panel/components/scope-config/ScopeTemplateWarningsPanel.vue +63 -0
- package/src/panel/components/scope-config/scopeConfigContext.ts +17 -0
- package/src/panel/components/scope-config/useScopeConfig.ts +11 -0
- package/src/panel/constants/messages.ts +34 -0
- package/src/panel/constants/zIndex.ts +6 -0
- package/src/panel/hooks/usePanelElements.ts +1075 -0
- package/src/panel/hooks/useRafThrottledScroll.ts +25 -0
- package/src/panel/hooks/useWorkspaceProjects.ts +240 -0
- package/src/panel/lib/panel-ruler-canvas.ts +139 -0
- package/src/panel/library/workspace-project-cache.ts +23 -0
- package/src/panel/library/workspace-project-db.ts +111 -0
- package/src/panel/library/workspace-project-sync.ts +41 -0
- package/src/panel/library/workspace-snapshot.ts +30 -0
- package/src/panel/parseOnlinePreviewSearchParams.ts +13 -0
- package/src/panel/scope/view-scope-store.ts +82 -0
- package/src/panel/types.ts +127 -0
- package/src/panel/utils/chartOptionBuilder.ts +327 -0
- package/src/panel/utils/gridPlacement.ts +189 -0
- package/src/panel/utils/mappingLayerOps.ts +142 -0
- package/src/panel/utils/panelElementDefaults.ts +161 -0
- package/src/panel/utils/panelElementNodes.ts +35 -0
- package/src/panel/utils/panelStateIO.ts +124 -0
- package/src/panel/utils/scope-autocomplete.ts +114 -0
- package/src/panel/utils/scope-field-labels.ts +46 -0
- package/src/panel/utils/scope-template-chart.ts +92 -0
- package/src/panel/utils/scope-template-preview.ts +124 -0
- package/src/panel/utils/scope-template-spread.ts +229 -0
- package/src/panel/utils/scope-template-warnings.ts +243 -0
- package/src/panel/utils/scope-template.ts +97 -0
- package/src/panel/utils/updateElementDraft.ts +221 -0
- package/src/panel/viewportZoom.ts +26 -0
- package/src/tailwind.css +43 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { Tooltip } from "ant-design-vue";
|
|
3
|
+
|
|
4
|
+
withDefaults(
|
|
5
|
+
defineProps<{
|
|
6
|
+
label?: string;
|
|
7
|
+
contentClass?: string;
|
|
8
|
+
buttonClass?: string;
|
|
9
|
+
}>(),
|
|
10
|
+
{ label: "说明" }
|
|
11
|
+
);
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<template>
|
|
15
|
+
<Tooltip
|
|
16
|
+
placement="top"
|
|
17
|
+
:overlay-class-name="`z-[10120] max-w-[360px] text-[11px] leading-5 ${contentClass ?? ''}`"
|
|
18
|
+
:mouse-enter-delay="0.12"
|
|
19
|
+
>
|
|
20
|
+
<template #title>
|
|
21
|
+
<slot />
|
|
22
|
+
</template>
|
|
23
|
+
<button
|
|
24
|
+
type="button"
|
|
25
|
+
:class="
|
|
26
|
+
buttonClass ??
|
|
27
|
+
'inline-flex h-4 w-4 shrink-0 items-center justify-center rounded-full border border-border text-[10px] leading-none text-muted-foreground hover:bg-accent/50'
|
|
28
|
+
"
|
|
29
|
+
:aria-label="`${label}说明`"
|
|
30
|
+
>
|
|
31
|
+
?
|
|
32
|
+
</button>
|
|
33
|
+
</Tooltip>
|
|
34
|
+
</template>
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, defineComponent, h, type PropType } from "vue";
|
|
3
|
+
import type { PanelElement } from "../types";
|
|
4
|
+
import {
|
|
5
|
+
AudioNodeContent,
|
|
6
|
+
ChartNodeContent,
|
|
7
|
+
CHART_TYPES,
|
|
8
|
+
EmptyNodePlaceholder,
|
|
9
|
+
GeometryNodeContent,
|
|
10
|
+
getNodeVisualStyle,
|
|
11
|
+
GridNodeContent,
|
|
12
|
+
ImageNodeContent,
|
|
13
|
+
ReferenceNodeContent,
|
|
14
|
+
TextNodeContent,
|
|
15
|
+
VideoNodeContent,
|
|
16
|
+
} from "./elementsLayerNodes";
|
|
17
|
+
|
|
18
|
+
const props = withDefaults(
|
|
19
|
+
defineProps<{
|
|
20
|
+
elements: PanelElement[];
|
|
21
|
+
allElements: PanelElement[];
|
|
22
|
+
selectedIds: string[];
|
|
23
|
+
updateElement: (
|
|
24
|
+
id: string,
|
|
25
|
+
patch: Partial<PanelElement>,
|
|
26
|
+
options?: { batchId?: string; meta?: Record<string, unknown> }
|
|
27
|
+
) => void;
|
|
28
|
+
layerLocked?: boolean;
|
|
29
|
+
previewMode?: boolean;
|
|
30
|
+
previewLayoutKey?: number;
|
|
31
|
+
}>(),
|
|
32
|
+
{ layerLocked: false, previewMode: false }
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
const emit = defineEmits<{
|
|
36
|
+
selectIds: [ids: string[]];
|
|
37
|
+
}>();
|
|
38
|
+
|
|
39
|
+
const NodeContent = defineComponent({
|
|
40
|
+
name: "NodeContent",
|
|
41
|
+
props: {
|
|
42
|
+
element: { type: Object as PropType<PanelElement>, required: true },
|
|
43
|
+
allElements: { type: Array as PropType<PanelElement[]>, required: true },
|
|
44
|
+
selected: { type: Boolean, default: false },
|
|
45
|
+
layerLocked: { type: Boolean, default: false },
|
|
46
|
+
previewMode: { type: Boolean, default: false },
|
|
47
|
+
previewLayoutKey: { type: Number, default: undefined },
|
|
48
|
+
updateElement: {
|
|
49
|
+
type: Function as PropType<
|
|
50
|
+
(
|
|
51
|
+
id: string,
|
|
52
|
+
patch: Partial<PanelElement>,
|
|
53
|
+
options?: { batchId?: string; meta?: Record<string, unknown> }
|
|
54
|
+
) => void
|
|
55
|
+
>,
|
|
56
|
+
required: true,
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
setup(p) {
|
|
60
|
+
return () => {
|
|
61
|
+
const el = p.element;
|
|
62
|
+
if (CHART_TYPES.has(el.materialType ?? "")) {
|
|
63
|
+
return h(ChartNodeContent, {
|
|
64
|
+
element: el,
|
|
65
|
+
previewLayoutKey: p.previewLayoutKey,
|
|
66
|
+
previewMode: p.previewMode,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
if (el.materialType === "reference") {
|
|
70
|
+
return h(ReferenceNodeContent, {
|
|
71
|
+
element: el,
|
|
72
|
+
allElements: p.allElements,
|
|
73
|
+
previewLayoutKey: p.previewLayoutKey,
|
|
74
|
+
previewMode: p.previewMode,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
if (el.materialType === "grid") {
|
|
78
|
+
return h(GridNodeContent, {
|
|
79
|
+
element: el,
|
|
80
|
+
allElements: p.allElements,
|
|
81
|
+
previewMode: p.previewMode,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
if (el.materialType === "text") {
|
|
85
|
+
return h(TextNodeContent, {
|
|
86
|
+
element: el,
|
|
87
|
+
editable: (el.textAllowInput ?? true) && !el.locked && !p.layerLocked,
|
|
88
|
+
onChange: (nextHtml: string) => p.updateElement(el.id, { textHtml: nextHtml }),
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
if (el.materialType === "audio") {
|
|
92
|
+
return h(AudioNodeContent, { element: el, selected: p.selected });
|
|
93
|
+
}
|
|
94
|
+
if (el.materialType === "video") {
|
|
95
|
+
return h(VideoNodeContent, { element: el, selected: p.selected });
|
|
96
|
+
}
|
|
97
|
+
if (el.materialType === "geometry") {
|
|
98
|
+
return h(GeometryNodeContent, { element: el });
|
|
99
|
+
}
|
|
100
|
+
if (el.materialType === "image") {
|
|
101
|
+
return h(ImageNodeContent, { element: el });
|
|
102
|
+
}
|
|
103
|
+
return h(EmptyNodePlaceholder, { element: el });
|
|
104
|
+
};
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const sortedElements = computed(() =>
|
|
109
|
+
[...props.elements].sort((a, b) => {
|
|
110
|
+
const za = a.zIndex ?? 1;
|
|
111
|
+
const zb = b.zIndex ?? 1;
|
|
112
|
+
if (za !== zb) return za - zb;
|
|
113
|
+
return a.id.localeCompare(b.id);
|
|
114
|
+
})
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
function onSelect(id: string, event: MouseEvent) {
|
|
118
|
+
if (props.previewMode) return;
|
|
119
|
+
if (event.button !== 0) return;
|
|
120
|
+
const isSelected = props.selectedIds.includes(id);
|
|
121
|
+
if (event.shiftKey) {
|
|
122
|
+
emit(
|
|
123
|
+
"selectIds",
|
|
124
|
+
isSelected ? props.selectedIds.filter((sid) => sid !== id) : [...props.selectedIds, id]
|
|
125
|
+
);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
emit("selectIds", [id]);
|
|
129
|
+
}
|
|
130
|
+
</script>
|
|
131
|
+
|
|
132
|
+
<template>
|
|
133
|
+
<div
|
|
134
|
+
v-for="el in sortedElements"
|
|
135
|
+
:key="el.id"
|
|
136
|
+
:class="[
|
|
137
|
+
'absolute select-none',
|
|
138
|
+
previewMode ? '' : 'rv-selectable',
|
|
139
|
+
!previewMode && selectedIds.includes(el.id) ? 'ring-2 ring-blue-500/90 ring-offset-0' : '',
|
|
140
|
+
]"
|
|
141
|
+
:data-element-id="el.id"
|
|
142
|
+
:style="{
|
|
143
|
+
left: `${el.x}px`,
|
|
144
|
+
top: `${el.y}px`,
|
|
145
|
+
width: `${Math.max(1, el.width)}px`,
|
|
146
|
+
height: `${Math.max(1, el.height)}px`,
|
|
147
|
+
zIndex: el.zIndex ?? 1,
|
|
148
|
+
transform: `rotate(${el.rotate ?? 0}deg)`,
|
|
149
|
+
transformOrigin: 'center center',
|
|
150
|
+
boxSizing: 'border-box',
|
|
151
|
+
...getNodeVisualStyle(el),
|
|
152
|
+
}"
|
|
153
|
+
@mousedown="onSelect(el.id, $event)"
|
|
154
|
+
>
|
|
155
|
+
<NodeContent
|
|
156
|
+
:element="el"
|
|
157
|
+
:all-elements="allElements"
|
|
158
|
+
:selected="selectedIds.includes(el.id)"
|
|
159
|
+
:layer-locked="layerLocked"
|
|
160
|
+
:preview-mode="previewMode"
|
|
161
|
+
:preview-layout-key="previewLayoutKey"
|
|
162
|
+
:update-element="updateElement"
|
|
163
|
+
/>
|
|
164
|
+
</div>
|
|
165
|
+
</template>
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
defineProps<{ id: string }>();
|
|
3
|
+
|
|
4
|
+
const common =
|
|
5
|
+
"relative aspect-[4/3] w-full shrink-0 overflow-hidden rounded-md border border-border bg-gradient-to-br from-card to-muted/40";
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<template>
|
|
9
|
+
<div v-if="id === 'bar'" :class="common">
|
|
10
|
+
<div class="absolute inset-0 grid grid-cols-5 items-end gap-1 px-2 pb-2 pt-1.5">
|
|
11
|
+
<div class="h-2 rounded-sm bg-primary/55" />
|
|
12
|
+
<div class="h-4 rounded-sm bg-primary/65" />
|
|
13
|
+
<div class="h-7 rounded-sm bg-primary/80" />
|
|
14
|
+
<div class="h-5 rounded-sm bg-primary/70" />
|
|
15
|
+
<div class="h-3 rounded-sm bg-primary/60" />
|
|
16
|
+
</div>
|
|
17
|
+
</div>
|
|
18
|
+
<div v-else-if="id === 'line' || id === 'area'" :class="common">
|
|
19
|
+
<svg viewBox="0 0 80 60" class="h-full w-full">
|
|
20
|
+
<polygon
|
|
21
|
+
v-if="id === 'area'"
|
|
22
|
+
points="6,42 18,28 34,34 50,20 66,24 74,18 74,56 6,56"
|
|
23
|
+
class="fill-primary/30"
|
|
24
|
+
/>
|
|
25
|
+
<polyline
|
|
26
|
+
points="6,42 18,28 34,34 50,20 66,24 74,18"
|
|
27
|
+
fill="none"
|
|
28
|
+
stroke="hsl(var(--primary))"
|
|
29
|
+
stroke-width="3"
|
|
30
|
+
stroke-linecap="round"
|
|
31
|
+
stroke-linejoin="round"
|
|
32
|
+
/>
|
|
33
|
+
</svg>
|
|
34
|
+
</div>
|
|
35
|
+
<div v-else-if="id === 'pie'" :class="common">
|
|
36
|
+
<svg viewBox="0 0 80 60" class="h-full w-full">
|
|
37
|
+
<circle cx="40" cy="30" r="16" class="fill-primary/20" />
|
|
38
|
+
<path d="M40 30 L40 14 A16 16 0 0 1 55 39 Z" class="fill-primary/75" />
|
|
39
|
+
<path d="M40 30 L55 39 A16 16 0 0 1 27 43 Z" class="fill-primary/45" />
|
|
40
|
+
</svg>
|
|
41
|
+
</div>
|
|
42
|
+
<div v-else-if="id === 'scatter'" :class="common">
|
|
43
|
+
<svg viewBox="0 0 80 60" class="h-full w-full">
|
|
44
|
+
<circle cx="14" cy="42" r="2.5" class="fill-primary/80" />
|
|
45
|
+
<circle cx="24" cy="34" r="2.5" class="fill-primary/70" />
|
|
46
|
+
<circle cx="36" cy="28" r="2.5" class="fill-primary/75" />
|
|
47
|
+
<circle cx="48" cy="20" r="2.5" class="fill-primary/65" />
|
|
48
|
+
<circle cx="62" cy="14" r="2.5" class="fill-primary/85" />
|
|
49
|
+
</svg>
|
|
50
|
+
</div>
|
|
51
|
+
<div v-else-if="id === 'radar'" :class="common">
|
|
52
|
+
<svg viewBox="0 0 80 60" class="h-full w-full">
|
|
53
|
+
<polygon points="40,10 58,20 54,40 26,40 22,20" class="fill-primary/20 stroke-primary/60" />
|
|
54
|
+
<polygon points="40,16 52,23 49,36 31,36 28,24" class="fill-primary/45 stroke-primary/80" />
|
|
55
|
+
</svg>
|
|
56
|
+
</div>
|
|
57
|
+
<div v-else-if="id === 'gauge'" :class="common">
|
|
58
|
+
<svg viewBox="0 0 80 60" class="h-full w-full">
|
|
59
|
+
<path d="M12 44a28 28 0 0 1 56 0" class="fill-none stroke-primary/35" stroke-width="6" />
|
|
60
|
+
<path d="M12 44a28 28 0 0 1 40-24" class="fill-none stroke-primary/80" stroke-width="6" />
|
|
61
|
+
<line x1="40" y1="44" x2="54" y2="28" class="stroke-primary" stroke-width="2" />
|
|
62
|
+
</svg>
|
|
63
|
+
</div>
|
|
64
|
+
<div v-else-if="id === 'funnel'" :class="common">
|
|
65
|
+
<svg viewBox="0 0 80 60" class="h-full w-full">
|
|
66
|
+
<polygon points="10,10 70,10 58,22 22,22" class="fill-primary/80" />
|
|
67
|
+
<polygon points="22,24 58,24 50,34 30,34" class="fill-primary/65" />
|
|
68
|
+
<polygon points="30,36 50,36 44,44 36,44" class="fill-primary/50" />
|
|
69
|
+
</svg>
|
|
70
|
+
</div>
|
|
71
|
+
<div v-else-if="id === 'text'" :class="common">
|
|
72
|
+
<div class="space-y-1.5 px-2 py-2">
|
|
73
|
+
<div class="h-2 w-10 rounded bg-primary/70" />
|
|
74
|
+
<div class="h-1.5 w-full rounded bg-muted-foreground/45" />
|
|
75
|
+
<div class="h-1.5 w-11/12 rounded bg-muted-foreground/45" />
|
|
76
|
+
<div class="h-1.5 w-8/12 rounded bg-muted-foreground/45" />
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
<div v-else-if="id === 'rect'" :class="common">
|
|
80
|
+
<div class="absolute inset-2 rounded-md border-2 border-primary/80 bg-primary/15" />
|
|
81
|
+
</div>
|
|
82
|
+
<div v-else-if="id === 'geometry'" :class="common">
|
|
83
|
+
<svg viewBox="0 0 80 60" class="h-full w-full">
|
|
84
|
+
<circle cx="22" cy="18" r="8" class="fill-primary/75" />
|
|
85
|
+
<rect x="36" y="10" width="16" height="16" rx="3" class="fill-primary/55" />
|
|
86
|
+
<polygon points="62,10 70,26 54,26" class="fill-primary/65" />
|
|
87
|
+
<polygon points="24,36 34,42 24,48 14,42" class="fill-primary/50" />
|
|
88
|
+
<polygon points="50,36 54,42 50,48 42,48 38,42 42,36" class="fill-primary/70" />
|
|
89
|
+
</svg>
|
|
90
|
+
</div>
|
|
91
|
+
<div v-else-if="id === 'reference'" :class="common">
|
|
92
|
+
<div class="absolute inset-2 rounded-md border border-dashed border-primary/70 bg-primary/10" />
|
|
93
|
+
<div class="absolute inset-0 flex items-center justify-center text-[10px] text-primary/80">
|
|
94
|
+
引用组件
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
<div v-else-if="id === 'image'" :class="common">
|
|
98
|
+
<div class="absolute inset-0 bg-gradient-to-br from-primary/25 via-muted/60 to-card" />
|
|
99
|
+
<svg viewBox="0 0 80 60" class="absolute inset-0 h-full w-full">
|
|
100
|
+
<circle cx="16" cy="14" r="5" class="fill-primary/70" />
|
|
101
|
+
<polygon points="0,60 24,34 38,50 52,30 80,60" class="fill-primary/35" />
|
|
102
|
+
<polygon points="0,60 18,42 30,54 44,36 62,60" class="fill-primary/55" />
|
|
103
|
+
</svg>
|
|
104
|
+
</div>
|
|
105
|
+
<div v-else-if="id === 'video'" :class="common">
|
|
106
|
+
<div class="absolute inset-0 bg-primary/12" />
|
|
107
|
+
<div class="absolute left-2 right-2 top-2 h-1.5 rounded bg-primary/35" />
|
|
108
|
+
<div class="absolute inset-0 flex items-center justify-center">
|
|
109
|
+
<div class="rounded-full bg-background/80 p-1">
|
|
110
|
+
<div
|
|
111
|
+
class="h-0 w-0 border-b-[7px] border-l-[11px] border-t-[7px] border-b-transparent border-l-primary/85 border-t-transparent"
|
|
112
|
+
/>
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
<div v-else-if="id === 'audio'" :class="common">
|
|
117
|
+
<div class="absolute inset-0 flex items-center justify-center gap-1">
|
|
118
|
+
<div class="h-3 w-1.5 rounded bg-primary/50" />
|
|
119
|
+
<div class="h-6 w-1.5 rounded bg-primary/75" />
|
|
120
|
+
<div class="h-8 w-1.5 rounded bg-primary/90" />
|
|
121
|
+
<div class="h-5 w-1.5 rounded bg-primary/70" />
|
|
122
|
+
<div class="h-4 w-1.5 rounded bg-primary/55" />
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
<div v-else-if="id === 'grid'" :class="common">
|
|
126
|
+
<div class="absolute inset-0 grid grid-cols-3 grid-rows-2 gap-1.5 p-2">
|
|
127
|
+
<div
|
|
128
|
+
v-for="idx in 6"
|
|
129
|
+
:key="idx"
|
|
130
|
+
class="rounded-sm border border-dashed border-primary/55 bg-primary/10"
|
|
131
|
+
/>
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
<div v-else :class="common" />
|
|
135
|
+
</template>
|