feat(wip): 장수패널에 ag-grid
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<BContainer id="container" :toast="{ root: true }" class="pageNationGeneral bg0">
|
<BContainer id="container" :toast="{ root: true }" class="pageNationGeneral bg0">
|
||||||
<TopBackBar :title="title" />
|
<TopBackBar :title="title" />
|
||||||
<GeneralList :list="generalList" />
|
<GeneralList :list="generalList" :height="'fill'" />
|
||||||
<BottomBar />
|
<BottomBar />
|
||||||
</BContainer>
|
</BContainer>
|
||||||
</template>
|
</template>
|
||||||
@@ -23,7 +23,7 @@ import { merge2DArrToObjectArr } from "./util/merge2DArrToObjectArr";
|
|||||||
import type { GeneralListItem } from "./defs/API/Nation";
|
import type { GeneralListItem } from "./defs/API/Nation";
|
||||||
import GeneralList from "./components/GeneralList.vue";
|
import GeneralList from "./components/GeneralList.vue";
|
||||||
|
|
||||||
const generalList = ref(new Map<number, GeneralListItem>());
|
const generalList = ref<GeneralListItem[]>([]);
|
||||||
|
|
||||||
const title = "세력 장수";
|
const title = "세력 장수";
|
||||||
|
|
||||||
@@ -33,13 +33,13 @@ onMounted(async () => {
|
|||||||
//XXX: 로직상 똑같은데....
|
//XXX: 로직상 똑같은데....
|
||||||
if (permission == 0) {
|
if (permission == 0) {
|
||||||
const rawGeneralList = merge2DArrToObjectArr(column, list);
|
const rawGeneralList = merge2DArrToObjectArr(column, list);
|
||||||
generalList.value = new Map(rawGeneralList.map((v) => [v.no, { permission, ...v }]));
|
generalList.value = rawGeneralList.map((v) => {return { permission, ...v }});
|
||||||
} else if (permission == 1) {
|
} else if (permission == 1) {
|
||||||
const rawGeneralList = merge2DArrToObjectArr(column, list);
|
const rawGeneralList = merge2DArrToObjectArr(column, list);
|
||||||
generalList.value = new Map(rawGeneralList.map((v) => [v.no, { permission, ...v }]));
|
generalList.value = rawGeneralList.map((v) => {return { permission, ...v }});
|
||||||
} else if ([2, 3, 4].includes(permission)) {
|
} else if ([2, 3, 4].includes(permission)) {
|
||||||
const rawGeneralList = merge2DArrToObjectArr(column, list);
|
const rawGeneralList = merge2DArrToObjectArr(column, list);
|
||||||
generalList.value = new Map(rawGeneralList.map((v) => [v.no, { permission, ...v }]));
|
generalList.value = rawGeneralList.map((v) => {return { permission, ...v }});
|
||||||
} else {
|
} else {
|
||||||
throw `?? ${permission}`;
|
throw `?? ${permission}`;
|
||||||
}
|
}
|
||||||
@@ -50,3 +50,15 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
html, body, #app {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pageNationGeneral{
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: auto 1fr auto;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,56 +1,88 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="g-thead-tr bg1">
|
<div
|
||||||
<div class="row g-tr mx-0 gx-1">
|
class="component-general-list"
|
||||||
<template v-for="itemKey of displayColumns.values()" :key="itemKey">
|
:style="{
|
||||||
<template v-for="(itemInfo, _dummyIdx) in [headerMap[itemKey]]" :key="_dummyIdx">
|
height: props.height === 'fill' ? '100%' : props.height === 'static' ? undefined : `${props.height}px`,
|
||||||
<div v-if="itemInfo.subType" class="col d-flex flex-column justify-content-center center">
|
}"
|
||||||
<div
|
>
|
||||||
v-for="[subItemKey, subItemInfo] of Object.entries(itemInfo.subType)"
|
<AgGridVue
|
||||||
:key="subItemKey"
|
style="width: 100%; height: 100%"
|
||||||
:style="subItemInfo.style ?? itemInfo.style"
|
class="ag-theme-balham-dark"
|
||||||
>
|
:rowHeight="rowHeight"
|
||||||
<template v-if="subItemInfo.subType">Error: nestedType {{ subItemKey }}</template>
|
:columnDefs="columnDefs"
|
||||||
<template v-else>{{ subItemInfo.title }}</template>
|
:rowData="list"
|
||||||
</div>
|
:defaultColDef="defaultColDef"
|
||||||
</div>
|
@grid-ready="onGridReady"
|
||||||
<div v-else class="col align-self-center center" :style="itemInfo.style">{{ itemInfo.title }}</div>
|
/>
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style="position: relative">
|
|
||||||
<div v-for="[generalID, general] of list" :key="generalID" class="row g-tbody-tr g-tr mx-0 gx-1">
|
|
||||||
<template v-for="itemKey of displayColumns.values()" :key="itemKey">
|
|
||||||
<template v-for="(itemInfo, _dummyIdx) in [headerMap[itemKey]]" :key="_dummyIdx">
|
|
||||||
<div v-if="itemInfo.subType" class="col d-flex flex-column justify-content-center center">
|
|
||||||
<template v-for="[subItemKey, subItemInfo] of Object.entries(itemInfo.subType)" :key="subItemKey">
|
|
||||||
<div v-if="subItemInfo.subType">Error: nestedType {{ subItemKey }}</div>
|
|
||||||
<template v-else-if="subItemInfo.converter">
|
|
||||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
|
||||||
<div :style="subItemInfo.style ?? itemInfo.style" v-html="subItemInfo.converter(general)" />
|
|
||||||
</template>
|
|
||||||
<div v-else :style="subItemInfo.style ?? itemInfo.style">
|
|
||||||
{{general[subItemKey as keyof GeneralListItem]}}
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
<template v-else-if="itemInfo.converter">
|
|
||||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
|
||||||
<div :style="itemInfo.style" class="col align-self-center center" v-html="itemInfo.converter(general)" />
|
|
||||||
</template>
|
|
||||||
<div v-else class="col align-self-center center" :style="itemInfo.style">
|
|
||||||
{{general[itemKey as keyof GeneralListItem]}}
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
<script lang="ts"></script>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { GeneralListItem, GeneralListItemP2 } from "@/defs/API/Nation";
|
import type { GeneralListItem, GeneralListItemP2 } from "@/defs/API/Nation";
|
||||||
import { getIconPath } from "@/util/getIconPath";
|
import { getIconPath } from "@/util/getIconPath";
|
||||||
import { ref, type PropType, type StyleValue } from "vue";
|
import { ref, watch, type PropType, type StyleValue } from "vue";
|
||||||
|
import { AgGridVue } from "ag-grid-vue3";
|
||||||
|
import type { CellClassParams, CellStyle, ColDef, ColumnApi, GridApi, GridReadyEvent } from "ag-grid-community";
|
||||||
|
import { getNpcColor } from "@/common_legacy";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
list: {
|
||||||
|
type: Array as PropType<GeneralListItem[]>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
height: {
|
||||||
|
type: String as PropType<'static' | 'fill' | number | `${number}px`| `${number}%`>,
|
||||||
|
required: false,
|
||||||
|
default: 'static',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.list,
|
||||||
|
() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
gridApi.value?.redrawRows();
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.height,
|
||||||
|
(val) => {
|
||||||
|
if (val === 'static') {
|
||||||
|
gridApi.value?.setDomLayout("autoHeight");
|
||||||
|
} else {
|
||||||
|
gridApi.value?.setDomLayout("normal");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const gridApi = ref<GridApi>();
|
||||||
|
const columnApi = ref<ColumnApi>();
|
||||||
|
|
||||||
|
const rowHeight = ref(28);
|
||||||
|
|
||||||
|
function onGridReady(params: GridReadyEvent) {
|
||||||
|
gridApi.value = params.api;
|
||||||
|
columnApi.value = params.columnApi;
|
||||||
|
if (props.height === 'static') {
|
||||||
|
params.api.setDomLayout("autoHeight");
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
params.api.setDomLayout('normal');
|
||||||
|
}
|
||||||
|
setRowHeight();
|
||||||
|
params.api.sizeColumnsToFit();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setRowHeight() {
|
||||||
|
if (!columnRawDefs.value.icon?.hide) {
|
||||||
|
rowHeight.value = 64 + 4;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rowHeight.value = gridApi.value?.getSizesForCurrentTheme().rowHeight ?? 28;
|
||||||
|
}
|
||||||
|
|
||||||
type headerInfo = {
|
type headerInfo = {
|
||||||
title: string;
|
title: string;
|
||||||
@@ -67,14 +99,74 @@ type headerInfo = {
|
|||||||
type headerType =
|
type headerType =
|
||||||
| keyof Omit<GeneralListItemP2, "no" | "imgsvr" | "picture" | "lbonus" | "officerLevelText">
|
| keyof Omit<GeneralListItemP2, "no" | "imgsvr" | "picture" | "lbonus" | "officerLevelText">
|
||||||
| "stat"
|
| "stat"
|
||||||
| "imagePath"
|
| "icon"
|
||||||
| "goldRice"
|
| "goldRice"
|
||||||
| "expDedLv"
|
| "expDedLv"
|
||||||
| "specials"
|
| "specials"
|
||||||
| "killturnAndConnect";
|
| "killturnAndConnect";
|
||||||
|
|
||||||
|
type GenColDef = ColDef & {
|
||||||
|
field?: headerType;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface GenCellClassParams extends CellClassParams {
|
||||||
|
data: GeneralListItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultColDef = ref<ColDef>({
|
||||||
|
resizable: true,
|
||||||
|
headerClass: "default-cell-header",
|
||||||
|
cellClass: "cell-middle",
|
||||||
|
});
|
||||||
|
|
||||||
|
const sortableNumber: GenColDef = {
|
||||||
|
sortable: true,
|
||||||
|
comparator: (a, b)=>a-b,
|
||||||
|
sortingOrder: ['desc', 'asc', null],
|
||||||
|
filter: 'number'
|
||||||
|
}
|
||||||
|
|
||||||
|
const columnRawDefs = ref<Partial<Record<headerType, GenColDef>>>({
|
||||||
|
icon: {
|
||||||
|
headerName: "아이콘",
|
||||||
|
width: 64 + 24,
|
||||||
|
suppressSizeToFit: true,
|
||||||
|
resizable: false,
|
||||||
|
cellRenderer: (obj: { data: GeneralListItem }) => {
|
||||||
|
const { data: gen } = obj;
|
||||||
|
return `<img src="${getIconPath(gen.imgsvr, gen.picture)}" width="64">`;
|
||||||
|
},
|
||||||
|
pinned: "left",
|
||||||
|
lockPosition: true,
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
headerName: "장수명",
|
||||||
|
field: "name",
|
||||||
|
pinned: "left",
|
||||||
|
sortable: true,
|
||||||
|
sortingOrder: ['asc', 'desc', null],
|
||||||
|
lockPosition: true,
|
||||||
|
cellStyle: (val: GenCellClassParams) => {
|
||||||
|
const gen = val.data;
|
||||||
|
const style: StyleValue = {
|
||||||
|
color: getNpcColor(gen.npc),
|
||||||
|
};
|
||||||
|
return style as CellStyle;
|
||||||
|
},
|
||||||
|
filter: true,
|
||||||
|
},
|
||||||
|
npc: { headerName: "NPC", field: "npc", hide: true },
|
||||||
|
leadership: { headerName: '통솔', field: 'leadership', ...sortableNumber},
|
||||||
|
strength: { headerName: '무력', field: 'strength', ...sortableNumber},
|
||||||
|
intel: { headerName: '지력', field: 'intel', ...sortableNumber},
|
||||||
|
explevel: { headerName: '명성Lv', field: 'explevel', ...sortableNumber},
|
||||||
|
dedlevel: { headerName: '계급Lv', field: 'dedlevel', ...sortableNumber},
|
||||||
|
});
|
||||||
|
|
||||||
|
const columnDefs = ref([...Object.values(columnRawDefs.value)]);
|
||||||
|
|
||||||
const headerMap: Record<headerType, headerInfo> = {
|
const headerMap: Record<headerType, headerInfo> = {
|
||||||
imagePath: {
|
icon: {
|
||||||
title: "아이콘",
|
title: "아이콘",
|
||||||
type: "display",
|
type: "display",
|
||||||
style: {
|
style: {
|
||||||
@@ -155,25 +247,23 @@ const headerMap: Record<headerType, headerInfo> = {
|
|||||||
officer_city: { title: "부임지" },
|
officer_city: { title: "부임지" },
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
const defaultColumns: headerType[] = [
|
watch(columnRawDefs, (val) => {
|
||||||
"imagePath",
|
setRowHeight();
|
||||||
"name",
|
columnDefs.value = [...Object.values(val)];
|
||||||
"age",
|
|
||||||
"stat",
|
|
||||||
"goldRice",
|
|
||||||
"expDedLv",
|
|
||||||
"specials",
|
|
||||||
"killturnAndConnect",
|
|
||||||
];
|
|
||||||
|
|
||||||
const displayColumns = ref(new Set<headerType>(defaultColumns));
|
|
||||||
|
|
||||||
defineProps({
|
|
||||||
list: {
|
|
||||||
type: Object as PropType<Map<number, GeneralListItem>>,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
let currentWidth = -1;
|
||||||
|
window.addEventListener("resize", (e) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
const newWidth = document.querySelector(".container")?.clientWidth ?? -1;
|
||||||
|
if (currentWidth != newWidth) {
|
||||||
|
currentWidth = newWidth;
|
||||||
|
gridApi.value?.sizeColumnsToFit();
|
||||||
|
}
|
||||||
|
}, 1);
|
||||||
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@@ -187,3 +277,28 @@ defineProps({
|
|||||||
z-index: 5;
|
z-index: 5;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.component-general-list {
|
||||||
|
.ag-root-wrapper .cell-middle {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ag-root-wrapper {
|
||||||
|
font-family: "Pretendard", "Apple SD Gothic Neo", "Noto Sans KR", "Malgun Gothic";
|
||||||
|
font-size: 14px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ag-root{
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ag-header{
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import "@scss/nationGeneral.scss";
|
import "@scss/nationGeneral.scss";
|
||||||
|
import "ag-grid-community/dist/styles/ag-grid.css";
|
||||||
|
import "ag-grid-community/dist/styles/ag-theme-balham-dark.css";
|
||||||
|
|
||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue'
|
||||||
import PageNationGeneral from '@/PageNationGeneral.vue';
|
import PageNationGeneral from '@/PageNationGeneral.vue';
|
||||||
|
|||||||
Generated
+30
@@ -37,6 +37,8 @@
|
|||||||
"@typescript-eslint/parser": "^5.14.0",
|
"@typescript-eslint/parser": "^5.14.0",
|
||||||
"@vue/compiler-sfc": "^3.2.31",
|
"@vue/compiler-sfc": "^3.2.31",
|
||||||
"@vue/eslint-config-typescript": "^10.0.0",
|
"@vue/eslint-config-typescript": "^10.0.0",
|
||||||
|
"ag-grid-community": "^27.1.0",
|
||||||
|
"ag-grid-vue3": "^27.1.0",
|
||||||
"async-validator": "^4.0.7",
|
"async-validator": "^4.0.7",
|
||||||
"autoprefixer": "^10.4.2",
|
"autoprefixer": "^10.4.2",
|
||||||
"axios": "^0.26.1",
|
"axios": "^0.26.1",
|
||||||
@@ -3066,6 +3068,20 @@
|
|||||||
"node": ">=0.4.0"
|
"node": ">=0.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ag-grid-community": {
|
||||||
|
"version": "27.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ag-grid-community/-/ag-grid-community-27.1.0.tgz",
|
||||||
|
"integrity": "sha512-SWzIJTNa7C6Vinizelcoc1FAJQRt1pDn+A8XHQDO2GTQT+VjBnPL8fg94fLJy0EEvqaN5IhDybNS0nD07SKIQw=="
|
||||||
|
},
|
||||||
|
"node_modules/ag-grid-vue3": {
|
||||||
|
"version": "27.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ag-grid-vue3/-/ag-grid-vue3-27.1.0.tgz",
|
||||||
|
"integrity": "sha512-3kZCK4UiFc/jCGvMy7zXjNG130GLuLboqoo4jzdLnD9PNR4hzqYjKVnCFHAQVi9Zj3hRBAWDwEU18MHukZuwKA==",
|
||||||
|
"dependencies": {
|
||||||
|
"ag-grid-community": "~27.1.0",
|
||||||
|
"vue": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ajv": {
|
"node_modules/ajv": {
|
||||||
"version": "6.12.6",
|
"version": "6.12.6",
|
||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||||
@@ -12796,6 +12812,20 @@
|
|||||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.1.1.tgz",
|
||||||
"integrity": "sha512-FbJdceMlPHEAWJOILDk1fXD8lnTlEIWFkqtfk+MvmL5q/qlHfN7GEHcsFZWt/Tea9jRNPWUZG4G976nqAAmU9w=="
|
"integrity": "sha512-FbJdceMlPHEAWJOILDk1fXD8lnTlEIWFkqtfk+MvmL5q/qlHfN7GEHcsFZWt/Tea9jRNPWUZG4G976nqAAmU9w=="
|
||||||
},
|
},
|
||||||
|
"ag-grid-community": {
|
||||||
|
"version": "27.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ag-grid-community/-/ag-grid-community-27.1.0.tgz",
|
||||||
|
"integrity": "sha512-SWzIJTNa7C6Vinizelcoc1FAJQRt1pDn+A8XHQDO2GTQT+VjBnPL8fg94fLJy0EEvqaN5IhDybNS0nD07SKIQw=="
|
||||||
|
},
|
||||||
|
"ag-grid-vue3": {
|
||||||
|
"version": "27.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ag-grid-vue3/-/ag-grid-vue3-27.1.0.tgz",
|
||||||
|
"integrity": "sha512-3kZCK4UiFc/jCGvMy7zXjNG130GLuLboqoo4jzdLnD9PNR4hzqYjKVnCFHAQVi9Zj3hRBAWDwEU18MHukZuwKA==",
|
||||||
|
"requires": {
|
||||||
|
"ag-grid-community": "~27.1.0",
|
||||||
|
"vue": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"ajv": {
|
"ajv": {
|
||||||
"version": "6.12.6",
|
"version": "6.12.6",
|
||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||||
|
|||||||
@@ -50,6 +50,8 @@
|
|||||||
"@typescript-eslint/parser": "^5.14.0",
|
"@typescript-eslint/parser": "^5.14.0",
|
||||||
"@vue/compiler-sfc": "^3.2.31",
|
"@vue/compiler-sfc": "^3.2.31",
|
||||||
"@vue/eslint-config-typescript": "^10.0.0",
|
"@vue/eslint-config-typescript": "^10.0.0",
|
||||||
|
"ag-grid-community": "^27.1.0",
|
||||||
|
"ag-grid-vue3": "^27.1.0",
|
||||||
"async-validator": "^4.0.7",
|
"async-validator": "^4.0.7",
|
||||||
"autoprefixer": "^10.4.2",
|
"autoprefixer": "^10.4.2",
|
||||||
"axios": "^0.26.1",
|
"axios": "^0.26.1",
|
||||||
|
|||||||
Reference in New Issue
Block a user