diff --git a/app.vue b/app.vue index 397872d..418c502 100644 --- a/app.vue +++ b/app.vue @@ -1,6 +1,8 @@ @@ -8,7 +10,8 @@ @import "./assets/css/main.css"; - \ No newline at end of file + + + diff --git a/components/ContentsWrapper.vue b/components/ContentsWrapper.vue new file mode 100644 index 0000000..541727f --- /dev/null +++ b/components/ContentsWrapper.vue @@ -0,0 +1,95 @@ + + + + + + \ No newline at end of file diff --git a/components/ToastGrid.vue b/components/ToastGrid.vue new file mode 100644 index 0000000..f53482f --- /dev/null +++ b/components/ToastGrid.vue @@ -0,0 +1,47 @@ + + + diff --git a/composables/grids/resourceGrid.ts b/composables/grids/resourceGrid.ts new file mode 100644 index 0000000..344026f --- /dev/null +++ b/composables/grids/resourceGrid.ts @@ -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', + }, +]; diff --git a/eslint.config.mjs b/eslint.config.mjs index 1b0d8d6..0b0fc1e 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -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' }, } ); diff --git a/layouts/default.vue b/layouts/default.vue index aad2a28..cbe5228 100644 --- a/layouts/default.vue +++ b/layouts/default.vue @@ -1,42 +1,18 @@ - - + + diff --git a/nuxt.config.ts b/nuxt.config.ts index a408990..da71411 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -39,5 +39,10 @@ export default defineNuxtConfig({ public: { apiBase: 'http://localhost' } - } + }, + typescript: { + shim: false, + strict: true, + }, + plugins: ['~/plugins/vue3-tui-grid.client.ts'] }); diff --git a/package-lock.json b/package-lock.json index 62426c2..1b91b37 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index ca7e59a..111f51a 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/pages/admin/resource.vue b/pages/admin/resource.vue new file mode 100644 index 0000000..6d8b12d --- /dev/null +++ b/pages/admin/resource.vue @@ -0,0 +1,41 @@ + + + + \ No newline at end of file diff --git a/pages/tui.vue b/pages/tui.vue new file mode 100644 index 0000000..d344eb2 --- /dev/null +++ b/pages/tui.vue @@ -0,0 +1,323 @@ + + + \ No newline at end of file diff --git a/plugins/vue3-tui-grid.client.ts b/plugins/vue3-tui-grid.client.ts new file mode 100644 index 0000000..0918c53 --- /dev/null +++ b/plugins/vue3-tui-grid.client.ts @@ -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) +}) diff --git a/stores/tab.ts b/stores/tab.ts new file mode 100644 index 0000000..13da245 --- /dev/null +++ b/stores/tab.ts @@ -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 + } + } +}) diff --git a/tsconfig.json b/tsconfig.json index a746f2a..d200e1b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -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 + } } diff --git a/types/vue3-tui-grid.d.ts b/types/vue3-tui-grid.d.ts new file mode 100644 index 0000000..9a868bd --- /dev/null +++ b/types/vue3-tui-grid.d.ts @@ -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; + } + \ No newline at end of file