tab, toast grid 추가
This commit is contained in:
9
app.vue
9
app.vue
@@ -1,6 +1,8 @@
|
||||
<template>
|
||||
<NuxtLayout>
|
||||
<NuxtPage />
|
||||
<keepAlive>
|
||||
<NuxtPage :keepalive="true" />
|
||||
</keepAlive>
|
||||
</NuxtLayout>
|
||||
</template>
|
||||
|
||||
@@ -8,7 +10,8 @@
|
||||
@import "./assets/css/main.css";
|
||||
</style>
|
||||
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
|
||||
onMounted(() => {
|
||||
const script = document.createElement('script')
|
||||
script.src = '/dist/cy_custom.js'
|
||||
@@ -22,3 +25,5 @@
|
||||
document.head.appendChild(script)
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
|
95
components/ContentsWrapper.vue
Normal file
95
components/ContentsWrapper.vue
Normal file
@@ -0,0 +1,95 @@
|
||||
<template>
|
||||
<div class="wrapper">
|
||||
<!-- 경로 -->
|
||||
<nav class="breadcrumb">{{ breadcrumb }}</nav>
|
||||
|
||||
<!-- 화면 명 + 버튼 영역 -->
|
||||
<header class="header">
|
||||
<h1 class="title">{{ pageTitle }}</h1>
|
||||
<div class="header-actions">
|
||||
<slot name="actions" />
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- 메인 콘텐츠 -->
|
||||
<main class="content">
|
||||
<slot />
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRoute } from '#imports'
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
// 경로(메뉴 경로)
|
||||
const breadcrumb = computed(() => route.path)
|
||||
|
||||
// 화면명(meta.title 값)
|
||||
const pageTitle = computed(() => route.meta.title || 'Untitled Page')
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
padding: 16px;
|
||||
background: #f9f9f9;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.breadcrumb {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* 화면명과 버튼을 좌우 끝으로 배치 */
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.content {
|
||||
flex: 1;
|
||||
padding: 12px;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
/* 버튼 공통 스타일 */
|
||||
.header-actions ::v-deep button {
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
padding: 6px 12px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.header-actions ::v-deep button:hover {
|
||||
background-color: #45a049;
|
||||
}
|
||||
|
||||
.header-actions ::v-deep button:disabled {
|
||||
background-color: #ccc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
47
components/ToastGrid.vue
Normal file
47
components/ToastGrid.vue
Normal file
@@ -0,0 +1,47 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, defineExpose } from 'vue';
|
||||
import type { OptColumn, OptRow } from 'tui-grid/types/options';
|
||||
import type { TuiGridElement } from 'vue3-tui-grid';
|
||||
import type Grid from 'tui-grid';
|
||||
|
||||
interface TreeColumnOptions {
|
||||
name: string;
|
||||
useCascadingCheckbox?: boolean;
|
||||
}
|
||||
|
||||
const tuiGridRef = ref<TuiGridElement>();
|
||||
|
||||
const props = defineProps<{
|
||||
data: OptRow[];
|
||||
// editor: https://github.com/nhn/tui.grid/blob/master/packages/toast-ui.grid/docs/v4.0-migration-guide-kor.md
|
||||
columns: OptColumn[];
|
||||
treeColumnOptions?: TreeColumnOptions;
|
||||
rowHeaders?: string[];
|
||||
rowKey?: string;
|
||||
}>();
|
||||
|
||||
// grid api : https://nhn.github.io/tui.grid/latest/Grid
|
||||
// const ref = ref<InstanceType<typeof ToastGrid>>();
|
||||
// ref.value?.api()?.clear();
|
||||
// ref.value?.api()?.getModifiedRows();
|
||||
defineExpose({
|
||||
api: (): Grid | undefined => tuiGridRef.value?.gridInstance,
|
||||
clearGrid: () => clearGrid(),
|
||||
});
|
||||
|
||||
function clearGrid() {
|
||||
tuiGridRef.value?.gridInstance.clear();
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<tui-grid
|
||||
ref="tuiGridRef"
|
||||
:data="props.data"
|
||||
:columns="props.columns"
|
||||
:treeColumnOptions="props.treeColumnOptions"
|
||||
:rowHeaders="props.rowHeaders"
|
||||
:rowKey="props.rowKey"
|
||||
/>
|
||||
</template>
|
188
composables/grids/resourceGrid.ts
Normal file
188
composables/grids/resourceGrid.ts
Normal file
@@ -0,0 +1,188 @@
|
||||
import type { OptColumn } from 'tui-grid/types/options';
|
||||
|
||||
export const colDefs: OptColumn[] = [
|
||||
{
|
||||
name: 'seq',
|
||||
header: 'seq',
|
||||
width: 50,
|
||||
align: 'center',
|
||||
hidden: true,
|
||||
},
|
||||
{
|
||||
name: 'parentCode',
|
||||
header: '부모 코드',
|
||||
width: 200,
|
||||
editor: 'text',
|
||||
align: 'center',
|
||||
filter: { type: 'text' },
|
||||
},
|
||||
{
|
||||
name: 'level',
|
||||
header: '레벨',
|
||||
width: 100,
|
||||
editor: 'text',
|
||||
align: 'center',
|
||||
filter: { type: 'number' },
|
||||
},
|
||||
{
|
||||
name: 'code',
|
||||
header: '코드',
|
||||
minWidth: 250,
|
||||
editor: 'text',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
name: 'name',
|
||||
header: '이름',
|
||||
minWidth: 250,
|
||||
editor: 'text',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
name: 'useFlag',
|
||||
header: '사용 여부',
|
||||
width: 150,
|
||||
filter: { type: 'text' },
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
name: 'menuFlag',
|
||||
header: '메뉴 여부',
|
||||
width: 150,
|
||||
filter: { type: 'text' },
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
name: 'apiFlag',
|
||||
header: 'API 여부',
|
||||
width: 150,
|
||||
filter: { type: 'text' },
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
name: 'authExceptionFlag',
|
||||
header: '예외 허용 여부',
|
||||
width: 150,
|
||||
filter: { type: 'text' },
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
name: 'sortOrder',
|
||||
header: '표시 순서',
|
||||
width: 200,
|
||||
editor: 'text',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
name: 'uri',
|
||||
header: 'uri',
|
||||
width: 300,
|
||||
editor: 'text',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
name: 'field1',
|
||||
header: '필드1',
|
||||
width: 200,
|
||||
editor: 'text',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
name: 'field2',
|
||||
header: '필드2',
|
||||
width: 200,
|
||||
editor: 'text',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
name: 'field3',
|
||||
header: '필드3',
|
||||
width: 200,
|
||||
editor: 'text',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
name: 'field4',
|
||||
header: '필드4',
|
||||
width: 200,
|
||||
editor: 'text',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
name: 'field5',
|
||||
header: '필드5',
|
||||
width: 200,
|
||||
editor: 'text',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
name: 'userButton1',
|
||||
header: '사용자 버튼1',
|
||||
width: 200,
|
||||
editor: 'text',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
name: 'userButton2',
|
||||
header: '사용자 버튼2',
|
||||
width: 200,
|
||||
editor: 'text',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
name: 'userButton3',
|
||||
header: '사용자 버튼3',
|
||||
width: 200,
|
||||
editor: 'text',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
name: 'userButton4',
|
||||
header: '사용자 버튼4',
|
||||
width: 200,
|
||||
editor: 'text',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
name: 'userButton5',
|
||||
header: '사용자 버튼5',
|
||||
width: 200,
|
||||
editor: 'text',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
name: 'userButton6',
|
||||
header: '사용자 버튼6',
|
||||
width: 200,
|
||||
editor: 'text',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
name: 'userButton7',
|
||||
header: '사용자 버튼7',
|
||||
width: 200,
|
||||
editor: 'text',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
name: 'userButton8',
|
||||
header: '사용자 버튼8',
|
||||
width: 200,
|
||||
editor: 'text',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
name: 'userButton9',
|
||||
header: '사용자 버튼9',
|
||||
width: 200,
|
||||
editor: 'text',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
name: 'userButton10',
|
||||
header: '사용자 버튼10',
|
||||
width: 200,
|
||||
editor: 'text',
|
||||
align: 'center',
|
||||
},
|
||||
];
|
@@ -8,6 +8,7 @@ export default withNuxt(
|
||||
"vue/html-self-closing": "off",
|
||||
"vue/html-closing-bracket-newline": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
'import/no-duplicates': 'off'
|
||||
},
|
||||
}
|
||||
);
|
||||
|
@@ -1,42 +1,18 @@
|
||||
<template>
|
||||
<div class="layout">
|
||||
<AppHeader v-model="activeMenu" @update:model-value="onMenuClick" />
|
||||
<nav
|
||||
v-if="subMenus && subMenus.length && showSubmenuBar"
|
||||
class="submenu-bar"
|
||||
@click.stop
|
||||
>
|
||||
<NuxtLink
|
||||
v-for="sub in subMenus"
|
||||
:key="sub.key"
|
||||
:to="sub.to"
|
||||
class="submenu-btn"
|
||||
:class="{ active: $route.path === sub.to }"
|
||||
>
|
||||
{{ sub.label }}
|
||||
</NuxtLink>
|
||||
</nav>
|
||||
<main class="main">
|
||||
<slot />
|
||||
</main>
|
||||
<footer class="footer">
|
||||
<p>© 2024 Nuxt.js App</p>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import AppHeader from "../components/AppHeader.vue";
|
||||
import { ref, computed, watch, onMounted, onBeforeUnmount } from "vue";
|
||||
import { useRouter, useRoute } from "vue-router";
|
||||
import { useTabsStore } from "../stores/tab";
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const activeMenu = ref("home");
|
||||
const showSubmenuBar = ref(false);
|
||||
|
||||
const tabsStore = useTabsStore();
|
||||
|
||||
// HOME 메뉴가 선택되었을 때 최상단 경로로 이동
|
||||
watch(activeMenu, newValue => {
|
||||
watch(activeMenu, (newValue) => {
|
||||
if (newValue === "home") {
|
||||
router.push("/");
|
||||
}
|
||||
@@ -46,6 +22,7 @@ watch(route, () => {
|
||||
showSubmenuBar.value = false;
|
||||
});
|
||||
|
||||
// 서브메뉴 정의
|
||||
const subMenus = computed(() => {
|
||||
if (activeMenu.value === "test") {
|
||||
return [
|
||||
@@ -58,16 +35,10 @@ const subMenus = computed(() => {
|
||||
{ key: "pathway4", label: "pathway4", to: "/cultureGraph/pathway4" },
|
||||
{ key: "pathwayjson", label: "pathwayjson", to: "/test/pathwayjson" },
|
||||
{ key: "cultureGraph", label: "배양그래프", to: "/test/culture-graph" },
|
||||
{
|
||||
key: "cultureGraphMulti",
|
||||
label: "배양그래프 멀티",
|
||||
to: "/test/culture-graph-multi",
|
||||
},
|
||||
{
|
||||
key: "cultureGraphTab",
|
||||
label: "배양그래프 탭",
|
||||
to: "/test/culture-graph-tab",
|
||||
},
|
||||
{ key: "cultureGraphMulti", label: "배양그래프 멀티", to: "/test/culture-graph-multi" },
|
||||
{ key: "cultureGraphTab", label: "배양그래프 탭", to: "/test/culture-graph-tab" },
|
||||
{ key: "tui-grid", label: "tui-grid", to: "/tui" },
|
||||
{ key: "리소스", label: "리소스", to: "/admin/resource" },
|
||||
];
|
||||
} else if (activeMenu.value === "admin") {
|
||||
return [
|
||||
@@ -76,7 +47,6 @@ const subMenus = computed(() => {
|
||||
{ key: "programs", label: "프로그램", to: "/admin/programs" },
|
||||
];
|
||||
}
|
||||
// HOME 메뉴일 때는 서브메뉴 없음
|
||||
return [];
|
||||
});
|
||||
|
||||
@@ -85,9 +55,14 @@ function onMenuClick(menu: string) {
|
||||
showSubmenuBar.value = true;
|
||||
}
|
||||
|
||||
// 서브메뉴 클릭 시 탭 추가
|
||||
function onSubMenuClick(sub: { key: string; label: string; to: string, componentName:string }) {
|
||||
tabsStore.addTab(sub);
|
||||
router.push(sub.to);
|
||||
}
|
||||
|
||||
function handleClickOutsideSubmenuBar(event: MouseEvent) {
|
||||
const submenu = document.querySelector(".submenu-bar");
|
||||
// menu-btn(대메뉴) 클릭 시에는 닫히지 않도록 예외 처리
|
||||
if (
|
||||
submenu &&
|
||||
!submenu.contains(event.target as Node) &&
|
||||
@@ -103,14 +78,53 @@ onMounted(() => {
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener("click", handleClickOutsideSubmenuBar);
|
||||
});
|
||||
/*
|
||||
useHead({
|
||||
title: "Integrated Bio Foundry Platform",
|
||||
meta: [{ name: "description", content: "Integrated Bio Foundry Platform" }],
|
||||
});
|
||||
*/
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="layout">
|
||||
<AppHeader v-model="activeMenu" @update:model-value="onMenuClick" />
|
||||
|
||||
<!-- 서브메뉴 바 -->
|
||||
<nav
|
||||
v-if="subMenus && subMenus.length && showSubmenuBar"
|
||||
class="submenu-bar"
|
||||
@click.stop
|
||||
>
|
||||
<button
|
||||
v-for="sub in subMenus"
|
||||
:key="sub.key"
|
||||
class="submenu-btn"
|
||||
:class="{ active: $route.path === sub.to }"
|
||||
@click="onSubMenuClick({...sub, componentName : sub.key})"
|
||||
>
|
||||
{{ sub.label }}
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<!-- 동적 탭 바 -->
|
||||
<div v-if="tabsStore.tabs.length" class="tab-bar">
|
||||
<div
|
||||
v-for="tab in tabsStore.tabs"
|
||||
:key="tab.key"
|
||||
class="tab-item"
|
||||
:class="{ active: tabsStore.activeTab === tab.key }"
|
||||
@click="tabsStore.setActiveTab(tab.key); router.push(tab.to)"
|
||||
>
|
||||
{{ tab.label }}
|
||||
<span class="close-btn" @click.stop="tabsStore.removeTab(tab.key)">×</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<main class="main">
|
||||
<slot />
|
||||
</main>
|
||||
|
||||
<footer class="footer">
|
||||
<p>© 2024 Nuxt.js App</p>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.layout {
|
||||
min-height: 100vh;
|
||||
@@ -162,4 +176,29 @@ useHead({
|
||||
background: #e6f0fa;
|
||||
color: #1976d2;
|
||||
}
|
||||
|
||||
/* 탭바 스타일 */
|
||||
.tab-bar {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
padding: 0.4rem 0.8rem;
|
||||
background: #fff;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
.tab-item {
|
||||
padding: 0.3rem 0.8rem;
|
||||
background: #f2f2f2;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.tab-item.active {
|
||||
background: #1976d2;
|
||||
color: white;
|
||||
}
|
||||
.close-btn {
|
||||
margin-left: 6px;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
|
@@ -39,5 +39,10 @@ export default defineNuxtConfig({
|
||||
public: {
|
||||
apiBase: 'http://localhost'
|
||||
}
|
||||
}
|
||||
},
|
||||
typescript: {
|
||||
shim: false,
|
||||
strict: true,
|
||||
},
|
||||
plugins: ['~/plugins/vue3-tui-grid.client.ts']
|
||||
});
|
||||
|
188
package-lock.json
generated
188
package-lock.json
generated
@@ -22,8 +22,11 @@
|
||||
"eslint": "^9.29.0",
|
||||
"nuxt": "^3.17.5",
|
||||
"pinia": "^3.0.3",
|
||||
"tui-code-snippet": "^2.3.3",
|
||||
"tui-grid": "^4.21.22",
|
||||
"vue": "^3.5.17",
|
||||
"vue-router": "^4.5.1"
|
||||
"vue-router": "^4.5.1",
|
||||
"vue3-tui-grid": "^0.1.51"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^10.4.21",
|
||||
@@ -4451,6 +4454,22 @@
|
||||
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/adler-32": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.2.0.tgz",
|
||||
"integrity": "sha512-/vUqU/UY4MVeFsg+SsK6c+/05RZXIHZMGJA+PX5JyWI0ZRcBpupnRuPLU/NXXoFwMYCPCoxIfElM2eS+DUXCqQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"exit-on-epipe": "~1.0.1",
|
||||
"printj": "~1.1.0"
|
||||
},
|
||||
"bin": {
|
||||
"adler32": "bin/adler32.njs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/ag-charts-types": {
|
||||
"version": "12.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ag-charts-types/-/ag-charts-types-12.0.0.tgz",
|
||||
@@ -5172,6 +5191,28 @@
|
||||
],
|
||||
"license": "CC-BY-4.0"
|
||||
},
|
||||
"node_modules/cfb": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz",
|
||||
"integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"adler-32": "~1.3.0",
|
||||
"crc-32": "~1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/cfb/node_modules/adler-32": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz",
|
||||
"integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
@@ -5367,6 +5408,15 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/codepage": {
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz",
|
||||
"integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/color": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
|
||||
@@ -6330,6 +6380,12 @@
|
||||
"url": "https://github.com/fb55/domhandler?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/dompurify": {
|
||||
"version": "2.5.8",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.5.8.tgz",
|
||||
"integrity": "sha512-o1vSNgrmYMQObbSSvF/1brBYEQPHhV1+gsmrusO7/GXtp1T9rCS8cXFqVxK/9crT1jA6Ccv+5MTSjBNqr7Sovw==",
|
||||
"license": "(MPL-2.0 OR Apache-2.0)"
|
||||
},
|
||||
"node_modules/domutils": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz",
|
||||
@@ -7185,6 +7241,15 @@
|
||||
"url": "https://github.com/sindresorhus/execa?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/exit-on-epipe": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz",
|
||||
"integrity": "sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/expand-template": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
|
||||
@@ -7545,6 +7610,15 @@
|
||||
"node": ">=12.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/frac": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz",
|
||||
"integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/fraction.js": {
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
|
||||
@@ -11436,6 +11510,18 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/printj": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz",
|
||||
"integrity": "sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ==",
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"printj": "bin/printj.njs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/process": {
|
||||
"version": "0.11.10",
|
||||
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
||||
@@ -12549,6 +12635,18 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ssf": {
|
||||
"version": "0.11.2",
|
||||
"resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz",
|
||||
"integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"frac": "~1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/stack-trace": {
|
||||
"version": "0.0.10",
|
||||
"resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
|
||||
@@ -13092,6 +13190,45 @@
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/tui-code-snippet": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/tui-code-snippet/-/tui-code-snippet-2.3.3.tgz",
|
||||
"integrity": "sha512-5NEHTDFKillDNPy6MCgpXDNBTB7SZkHBFOF6vXfCDIFZcBdFYFXTd2xvAVvIeM3UYFRWu27xUf/Kxl5f9+RooQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tui-date-picker": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmjs.org/tui-date-picker/-/tui-date-picker-4.3.3.tgz",
|
||||
"integrity": "sha512-/2YoLnj5c1e+Ag1ZZYOgzEs2o0v7Ol7c5UAnBj438zGlkwkMxyH0HwP2pVqqIYX05WE7K0+6nTWVMybS8otBgw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tui-time-picker": "^2.1.6"
|
||||
}
|
||||
},
|
||||
"node_modules/tui-grid": {
|
||||
"version": "4.21.22",
|
||||
"resolved": "https://registry.npmjs.org/tui-grid/-/tui-grid-4.21.22.tgz",
|
||||
"integrity": "sha512-RxkFcveR2tWf3QrtuW9/zejXjeXL3SdrQPK89DbrrM6N7TlyUcb5HAf0U3MBn/hYT1p5kt5wyOwHPCQJuIBrsA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dompurify": "^2.3.9",
|
||||
"tui-date-picker": "^4.1.0",
|
||||
"tui-pagination": "^3.4.0",
|
||||
"xlsx": "^0.17.1"
|
||||
}
|
||||
},
|
||||
"node_modules/tui-pagination": {
|
||||
"version": "3.4.1",
|
||||
"resolved": "https://registry.npmjs.org/tui-pagination/-/tui-pagination-3.4.1.tgz",
|
||||
"integrity": "sha512-W09L0wPMSFstthBhQjcLNDnN1yuCEDn/tIXmaKdTpNFYa11eNrNo/rOwXzrugXvP2arZ60KmVhkFzoAOEuV0Sg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tui-time-picker": {
|
||||
"version": "2.1.6",
|
||||
"resolved": "https://registry.npmjs.org/tui-time-picker/-/tui-time-picker-2.1.6.tgz",
|
||||
"integrity": "sha512-4Jmo3wjGS+Ii4/qQgt5DaFEohHpB3U6BzWeTODVVFHD9sx3NOsbomY9K0xMobSLODi+tQEH7wfOtNU0IJmcQ6Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tunnel-agent": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
||||
@@ -14017,6 +14154,16 @@
|
||||
"integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/vue3-tui-grid": {
|
||||
"version": "0.1.51",
|
||||
"resolved": "https://registry.npmjs.org/vue3-tui-grid/-/vue3-tui-grid-0.1.51.tgz",
|
||||
"integrity": "sha512-UY9Ebli+3NmA9HheXbtQSsjNSmXn5H+Qv3lKQv4y35cDm+dME/sW7O5cXESdrfDFM0sSKFgTSBZF5+iWfmlE4g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tui-grid": "^4.21.2",
|
||||
"vue": "^3.2.37"
|
||||
}
|
||||
},
|
||||
"node_modules/web-streams-polyfill": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
|
||||
@@ -14139,6 +14286,24 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/wmf": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz",
|
||||
"integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/word": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz",
|
||||
"integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/word-wrap": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
|
||||
@@ -14276,6 +14441,27 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/xlsx": {
|
||||
"version": "0.17.5",
|
||||
"resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.17.5.tgz",
|
||||
"integrity": "sha512-lXNU0TuYsvElzvtI6O7WIVb9Zar1XYw7Xb3VAx2wn8N/n0whBYrCnHMxtFyIiUU1Wjf09WzmLALDfBO5PqTb1g==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"adler-32": "~1.2.0",
|
||||
"cfb": "^1.1.4",
|
||||
"codepage": "~1.15.0",
|
||||
"crc-32": "~1.2.0",
|
||||
"ssf": "~0.11.2",
|
||||
"wmf": "~1.0.1",
|
||||
"word": "~0.3.0"
|
||||
},
|
||||
"bin": {
|
||||
"xlsx": "bin/xlsx.njs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/xml-name-validator": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz",
|
||||
|
@@ -19,16 +19,19 @@
|
||||
"@pinia/nuxt": "^0.11.1",
|
||||
"ag-grid-community": "^34.0.0",
|
||||
"ag-grid-vue3": "^34.0.0",
|
||||
"echarts": "^5.6.0",
|
||||
"chart.js": "^4.5.0",
|
||||
"cytoscape": "^3.32.0",
|
||||
"cytoscape-layers": "^3.0.0",
|
||||
"cytoscape-overlays": "^2.0.0",
|
||||
"echarts": "^5.6.0",
|
||||
"eslint": "^9.29.0",
|
||||
"nuxt": "^3.17.5",
|
||||
"pinia": "^3.0.3",
|
||||
"tui-code-snippet": "^2.3.3",
|
||||
"tui-grid": "^4.21.22",
|
||||
"vue": "^3.5.17",
|
||||
"vue-router": "^4.5.1"
|
||||
"vue-router": "^4.5.1",
|
||||
"vue3-tui-grid": "^0.1.51"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^10.4.21",
|
||||
|
41
pages/admin/resource.vue
Normal file
41
pages/admin/resource.vue
Normal file
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<ContentsWrapper>
|
||||
<template #actions>
|
||||
<button @click="onAddClick">추가</button>
|
||||
<button @click="onUpdateClick">저장</button>
|
||||
</template>
|
||||
<input type="text" >
|
||||
<ToastGrid
|
||||
ref="grid1Ref"
|
||||
:data="data"
|
||||
:columns="colDefs"
|
||||
/>
|
||||
</ContentsWrapper>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {colDefs} from '../../composables/grids/resourceGrid'
|
||||
|
||||
definePageMeta({
|
||||
title: '리소스 관리'
|
||||
})
|
||||
|
||||
const data = [{}]
|
||||
|
||||
const grid1Ref = ref();
|
||||
|
||||
onMounted(async () => {
|
||||
await nextTick() // DOM 및 컴포넌트 렌더링 완료 대기
|
||||
grid1Ref.value?.api()?.setBodyHeight('700')
|
||||
})
|
||||
|
||||
function onAddClick() {
|
||||
grid1Ref.value?.api()?.appendRow({});
|
||||
}
|
||||
|
||||
function onUpdateClick() {
|
||||
//grid1Ref.value?.clearGrid();
|
||||
console.log(grid1Ref.value?.api()?.getModifiedRows());
|
||||
}
|
||||
</script>
|
||||
|
323
pages/tui.vue
Normal file
323
pages/tui.vue
Normal file
@@ -0,0 +1,323 @@
|
||||
<script setup lang="ts">
|
||||
import ToastGrid from '../components/ToastGrid.vue';
|
||||
|
||||
const data = [
|
||||
{
|
||||
id: 549731,
|
||||
name: 'Beautiful Lies',
|
||||
artist: 'Birdy',
|
||||
release: '2016.03.26',
|
||||
type: 'Deluxe',
|
||||
typeCode: '1',
|
||||
genre: 'Pop',
|
||||
genreCode: '1',
|
||||
grade: '4',
|
||||
price: 10000,
|
||||
downloadCount: 1000,
|
||||
listenCount: 5000,
|
||||
_attributes: {
|
||||
expanded: true,
|
||||
},
|
||||
_children: [
|
||||
{
|
||||
id: 491379,
|
||||
name: 'Chaos And The Calm',
|
||||
artist: 'James Bay',
|
||||
release: '2015.03.23',
|
||||
type: 'EP',
|
||||
typeCode: '2',
|
||||
genre: 'Pop,Rock',
|
||||
genreCode: '1,2',
|
||||
grade: '5',
|
||||
price: 12000,
|
||||
downloadCount: 1000,
|
||||
listenCount: 5000,
|
||||
_children: [],
|
||||
},
|
||||
{
|
||||
id: 498896,
|
||||
name: 'The Magic Whip',
|
||||
artist: 'Blur',
|
||||
release: '2015.04.27',
|
||||
type: 'EP',
|
||||
typeCode: '2',
|
||||
genre: 'Rock',
|
||||
genreCode: '2',
|
||||
grade: '3',
|
||||
price: 15000,
|
||||
downloadCount: 1000,
|
||||
listenCount: 5000,
|
||||
_attributes: {
|
||||
expanded: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 450720,
|
||||
name: "I'm Not The Only One",
|
||||
artist: 'Sam Smith',
|
||||
release: '2014.09.15',
|
||||
type: 'Single',
|
||||
typeCode: '3',
|
||||
genre: 'Pop,R&B',
|
||||
genreCode: '1,3',
|
||||
grade: '4',
|
||||
price: 8000,
|
||||
downloadCount: 1000,
|
||||
listenCount: 5000,
|
||||
_attributes: {
|
||||
expanded: true,
|
||||
},
|
||||
_children: [
|
||||
{
|
||||
id: 587871,
|
||||
name: 'This Is Acting',
|
||||
artist: 'Sia',
|
||||
release: '2016.10.22',
|
||||
type: 'EP',
|
||||
typeCode: '2',
|
||||
genre: 'Pop',
|
||||
genreCode: '1',
|
||||
grade: '3',
|
||||
price: 20000,
|
||||
downloadCount: 1000,
|
||||
listenCount: 5000,
|
||||
_attributes: {
|
||||
expanded: true,
|
||||
},
|
||||
_children: [
|
||||
{
|
||||
id: 490500,
|
||||
name: 'Blue Skies',
|
||||
release: '2015.03.18',
|
||||
artist: 'Lenka',
|
||||
type: 'Single',
|
||||
typeCode: '3',
|
||||
genre: 'Pop,Rock',
|
||||
genreCode: '1,2',
|
||||
grade: '5',
|
||||
price: 6000,
|
||||
downloadCount: 1000,
|
||||
listenCount: 5000,
|
||||
},
|
||||
{
|
||||
id: 317659,
|
||||
name: "I Won't Give Up",
|
||||
artist: 'Jason Mraz',
|
||||
release: '2012.01.03',
|
||||
type: 'Single',
|
||||
typeCode: '3',
|
||||
genre: 'Pop',
|
||||
genreCode: '1',
|
||||
grade: '2',
|
||||
price: 7000,
|
||||
downloadCount: 1000,
|
||||
listenCount: 5000,
|
||||
},
|
||||
{
|
||||
id: 583551,
|
||||
name: 'Following My Intuition',
|
||||
artist: 'Craig David',
|
||||
release: '2016.10.01',
|
||||
type: 'Deluxe',
|
||||
typeCode: '1',
|
||||
genre: 'R&B,Electronic',
|
||||
genreCode: '3,4',
|
||||
grade: '5',
|
||||
price: 15000,
|
||||
downloadCount: 1000,
|
||||
listenCount: 5000,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 436461,
|
||||
name: 'X',
|
||||
artist: 'Ed Sheeran',
|
||||
release: '2014.06.24',
|
||||
type: 'Deluxe',
|
||||
typeCode: '1',
|
||||
genre: 'Pop',
|
||||
genreCode: '1',
|
||||
grade: '5',
|
||||
price: 20000,
|
||||
downloadCount: 1000,
|
||||
listenCount: 5000,
|
||||
},
|
||||
{
|
||||
id: 295651,
|
||||
name: 'Moves Like Jagger',
|
||||
release: '2011.08.08',
|
||||
artist: 'Maroon5',
|
||||
type: 'Single',
|
||||
typeCode: '3',
|
||||
genre: 'Pop,Rock',
|
||||
genreCode: '1,2',
|
||||
grade: '2',
|
||||
price: 7000,
|
||||
downloadCount: 1000,
|
||||
listenCount: 5000,
|
||||
},
|
||||
{
|
||||
id: 541713,
|
||||
name: 'A Head Full Of Dreams',
|
||||
artist: 'Coldplay',
|
||||
release: '2015.12.04',
|
||||
type: 'Deluxe',
|
||||
typeCode: '1',
|
||||
genre: 'Rock',
|
||||
genreCode: '2',
|
||||
grade: '3',
|
||||
price: 25000,
|
||||
downloadCount: 1000,
|
||||
listenCount: 5000,
|
||||
_attributes: {
|
||||
expanded: false,
|
||||
},
|
||||
_children: [
|
||||
{
|
||||
id: 294574,
|
||||
name: '4',
|
||||
artist: 'Beyoncé',
|
||||
release: '2011.07.26',
|
||||
type: 'Deluxe',
|
||||
typeCode: '1',
|
||||
genre: 'Pop',
|
||||
genreCode: '1',
|
||||
grade: '3',
|
||||
price: 12000,
|
||||
downloadCount: 1000,
|
||||
listenCount: 5000,
|
||||
},
|
||||
{
|
||||
id: 265289,
|
||||
name: '21',
|
||||
artist: 'Adele',
|
||||
release: '2011.01.21',
|
||||
type: 'Deluxe',
|
||||
typeCode: '1',
|
||||
genre: 'Pop,R&B',
|
||||
genreCode: '1,3',
|
||||
grade: '5',
|
||||
price: 15000,
|
||||
downloadCount: 1000,
|
||||
listenCount: 5000,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 555871,
|
||||
name: 'Warm On A Cold Night',
|
||||
artist: 'HONNE',
|
||||
release: '2016.07.22',
|
||||
type: 'EP',
|
||||
typeCode: '1',
|
||||
genre: 'R&B,Electronic',
|
||||
genreCode: '3,4',
|
||||
grade: '4',
|
||||
price: 11000,
|
||||
downloadCount: 1000,
|
||||
listenCount: 5000,
|
||||
},
|
||||
{
|
||||
id: 550571,
|
||||
name: 'Take Me To The Alley',
|
||||
artist: 'Gregory Porter',
|
||||
release: '2016.09.02',
|
||||
type: 'Deluxe',
|
||||
typeCode: '1',
|
||||
genre: 'Jazz',
|
||||
genreCode: '5',
|
||||
grade: '3',
|
||||
price: 30000,
|
||||
downloadCount: 1000,
|
||||
listenCount: 5000,
|
||||
},
|
||||
{
|
||||
id: 544128,
|
||||
name: 'Make Out',
|
||||
artist: 'LANY',
|
||||
release: '2015.12.11',
|
||||
type: 'EP',
|
||||
typeCode: '2',
|
||||
genre: 'Electronic',
|
||||
genreCode: '4',
|
||||
grade: '2',
|
||||
price: 12000,
|
||||
downloadCount: 1000,
|
||||
listenCount: 5000,
|
||||
},
|
||||
{
|
||||
id: 366374,
|
||||
name: 'Get Lucky',
|
||||
artist: 'Daft Punk',
|
||||
release: '2013.04.23',
|
||||
type: 'Single',
|
||||
typeCode: '3',
|
||||
genre: 'Pop,Funk',
|
||||
genreCode: '1,5',
|
||||
grade: '3',
|
||||
price: 9000,
|
||||
downloadCount: 1000,
|
||||
listenCount: 5000,
|
||||
},
|
||||
{
|
||||
id: 8012747,
|
||||
name: 'Valtari',
|
||||
artist: 'Sigur Rós',
|
||||
release: '2012.05.31',
|
||||
type: 'EP',
|
||||
typeCode: '3',
|
||||
genre: 'Rock',
|
||||
genreCode: '2',
|
||||
grade: '5',
|
||||
price: 10000,
|
||||
downloadCount: 1000,
|
||||
listenCount: 5000,
|
||||
},
|
||||
];
|
||||
|
||||
const columns = [
|
||||
{ header: 'Name', name: 'name', width: 300 },
|
||||
{ header: 'Artist', name: 'artist' },
|
||||
{ header: 'Type', name: 'type' },
|
||||
{ header: 'Release', name: 'release' },
|
||||
{ header: 'Genre', name: 'genre' },
|
||||
{ header: 'checkbox', name: 'checkbox', editor:{ type: 'checkbox', options: {
|
||||
listItems: [
|
||||
{ text: 'true', value: true },
|
||||
]
|
||||
}}}
|
||||
];
|
||||
const treeColumnOptions = { name: 'name', useCascadingCheckbox: true };
|
||||
|
||||
const grid1Ref = ref();
|
||||
|
||||
function onClearClick() {
|
||||
//grid1Ref.value?.clearGrid();
|
||||
grid1Ref.value?.api()?.clear();
|
||||
}
|
||||
|
||||
function onUpdateClick() {
|
||||
//grid1Ref.value?.clearGrid();
|
||||
console.log(grid1Ref.value?.api()?.getModifiedRows());
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<button @click="onClearClick">clear api</button>
|
||||
<br>
|
||||
<button @click="onUpdateClick">update list</button>
|
||||
<ToastGrid
|
||||
ref="grid1Ref"
|
||||
:data="data"
|
||||
:columns="columns"
|
||||
:treeColumnOptions="treeColumnOptions"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
7
plugins/vue3-tui-grid.client.ts
Normal file
7
plugins/vue3-tui-grid.client.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { defineNuxtPlugin } from '#app'
|
||||
import TuiGrid from 'vue3-tui-grid'
|
||||
import 'tui-grid/dist/tui-grid.css'
|
||||
|
||||
export default defineNuxtPlugin(nuxtApp => {
|
||||
nuxtApp.vueApp.use(TuiGrid)
|
||||
})
|
36
stores/tab.ts
Normal file
36
stores/tab.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
// stores/tabs.ts
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
export interface TabItem {
|
||||
key: string
|
||||
label: string
|
||||
to: string
|
||||
componentName: string
|
||||
}
|
||||
|
||||
export const useTabsStore = defineStore('tabs', {
|
||||
state: () => ({
|
||||
activeTab: '' as string,
|
||||
tabs: [] as { key: string; label: string; to: string; componentName: string }[]
|
||||
}),
|
||||
actions: {
|
||||
addTab(tab: TabItem) {
|
||||
if (!this.tabs.find(t => t.key === tab.key)) {
|
||||
this.tabs.push(tab)
|
||||
}
|
||||
this.activeTab = tab.key
|
||||
},
|
||||
removeTab(key: string) {
|
||||
const idx = this.tabs.findIndex(t => t.key === key)
|
||||
if (idx !== -1) {
|
||||
this.tabs.splice(idx, 1)
|
||||
if (this.activeTab === key && this.tabs.length) {
|
||||
this.activeTab = this.tabs[Math.max(idx - 1, 0)].key
|
||||
}
|
||||
}
|
||||
},
|
||||
setActiveTab(key: string) {
|
||||
this.activeTab = key
|
||||
}
|
||||
}
|
||||
})
|
@@ -1,4 +1,9 @@
|
||||
{
|
||||
// https://nuxt.com/docs/guide/concepts/typescript
|
||||
"extends": "./.nuxt/tsconfig.json"
|
||||
"extends": "./.nuxt/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"typeRoots": ["./src/types", "./node_modules/@types"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true
|
||||
}
|
||||
}
|
||||
|
9
types/vue3-tui-grid.d.ts
vendored
Normal file
9
types/vue3-tui-grid.d.ts
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
// src/types/vue3-tui-grid.d.ts
|
||||
declare module 'vue3-tui-grid' {
|
||||
import type { Plugin } from 'vue';
|
||||
const TuiGrid: Plugin;
|
||||
export default TuiGrid;
|
||||
export type TuiGridElement = any;
|
||||
export type GridEvent = any;
|
||||
}
|
||||
|
Reference in New Issue
Block a user