Files
core/hwe/ts/components/GeneralList.vue
T

500 lines
12 KiB
Vue

<template>
<div
class="component-general-list"
:style="{
height: props.height === 'fill' ? '100%' : props.height === 'static' ? undefined : `${props.height}px`,
}"
>
<AgGridVue
style="width: 100%; height: 100%"
class="ag-theme-balham-dark"
:getRowId="getRowId"
:getRowHeight="getRowHeight"
:columnDefs="columnDefs"
:rowData="list"
:defaultColDef="defaultColDef"
@grid-ready="onGridReady"
/>
</div>
</template>
<script lang="ts"></script>
<script lang="ts" setup>
import type { GeneralListItem, GeneralListItemP2, GeneralListResponse } from "@/defs/API/Nation";
import { getIconPath } from "@/util/getIconPath";
import { inject, ref, watch, type PropType, type Ref, type StyleValue } from "vue";
import { AgGridVue } from "ag-grid-vue3";
import type {
CellClassParams,
CellStyle,
ColDef,
ColGroupDef,
ColumnApi,
GetRowIdParams,
GridApi,
GridReadyEvent,
} from "ag-grid-community";
import { getNpcColor } from "@/common_legacy";
import type { BaseWithValueColDefParams, ValueGetterParams } from "ag-grid-community/dist/lib/entities/colDef";
import type { GameConstStore } from "@/GameConstStore";
import { unwrap } from "@/util/unwrap";
import SimpleTooltipCell from "@/gridCellRenderer/SimpleTooltipCell.vue";
import GridTooltipCell from "@/gridCellRenderer/GridTooltipCell.vue";
import type { GameIActionInfo } from "@/defs/GameObj";
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",
},
env: {
type: Object as PropType<GeneralListResponse['env']>,
required: true,
}
});
const gameConstStore = unwrap(inject<Ref<GameConstStore>>("gameConstStore"));
//debug
watch(
() => props.list,
() => {
setTimeout(() => {
//debug
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 getRowId(params: GetRowIdParams): string {
const genID = (params.data as GeneralListItem).no;
return `${genID}`;
}
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();
}
function setRowHeight() {
if (!(columnRawDefs.value.icon as GenColDef | undefined)?.hide) {
rowHeight.value = 64 + 4;
return;
}
rowHeight.value = gridApi.value?.getSizesForCurrentTheme().rowHeight ?? 28;
}
function getRowHeight():number{
return rowHeight.value;
}
type headerType =
| keyof Omit<GeneralListItemP2, "no" | "imgsvr" | "picture" | "lbonus">
| "stat"
| "icon"
| "goldRice"
| "expDedLv"
| "specials"
| "killturnAndConnect"
| "years";
interface GenValueParams extends BaseWithValueColDefParams {
data: GeneralListItem;
}
interface GenValueGetterParams extends ValueGetterParams {
data: GeneralListItem;
}
interface GenColDef extends ColDef {
field?: headerType;
valueFormatter?: string | ((params: GenValueParams) => string);
filterValueGetter?: string | ((params: GenValueGetterParams) => string);
}
interface GenColGroupDef extends ColGroupDef {
children: (GenColDef | GenColGroupDef)[];
groupId: headerType;
}
interface GenCellClassParams extends CellClassParams {
data: GeneralListItem;
}
type GridCellInfo = {
iActionMap?: Record<string, GameIActionInfo>;
info?: string;
target: keyof GeneralListItem;
};
function numberFormatter(value: GenValueParams): string {
return (value.value as number).toLocaleString();
}
const defaultCellClass = ["cell-middle"];
const centerCellClass = [...defaultCellClass, "cell-center"];
const rightAlignClass = [...defaultCellClass, "cell-right"];
const sortableNumber: GenColDef = {
sortable: true,
comparator: (a, b) => a - b,
sortingOrder: ["desc", "asc", null],
filter: "number",
cellClass: rightAlignClass,
};
const defaultColDef = ref<ColDef>({
resizable: true,
headerClass: "default-cell-header",
cellClass: centerCellClass,
floatingFilter: true,
width: 90,
});
const columnRawDefs = ref<Partial<Record<headerType, GenColDef | GenColGroupDef>>>({
icon: {
headerName: "아이콘",
width: 64 + 24,
suppressSizeToFit: true,
resizable: false,
cellRenderer: (obj: GenValueParams) => {
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,
width: 130,
sortingOrder: ["asc", "desc", null],
lockPosition: true,
cellStyle: (val: GenCellClassParams) => {
const gen = val.data;
const style: StyleValue = {
color: getNpcColor(gen.npc),
};
return style as CellStyle;
},
cellClass: defaultCellClass,
filter: true,
},
npc: { headerName: "NPC", field: "npc", hide: true },
stat: {
groupId: "stat",
openByDefault: false,
headerName: "능력치",
children: [
{
headerName: "통|무|지",
width: 90,
cellRenderer: (obj: GenValueParams) => {
const gen = obj.data;
return `${gen.leadership}|${gen.strength}|${gen.intel}`;
},
columnGroupShow: "closed",
},
{
headerName: "통솔",
field: "leadership",
...sortableNumber,
columnGroupShow: "open",
width: 70,
type: "numericColumn",
},
{
headerName: "무력",
field: "strength",
...sortableNumber,
columnGroupShow: "open",
width: 70,
},
{
headerName: "지력",
field: "intel",
...sortableNumber,
columnGroupShow: "open",
width: 70,
},
],
},
officerLevel: {
headerName: "관직",
field: "officerLevelText",
sortable: true,
comparator: (a, b, c, d) => c.data.officerLevel - d.data.officerLevel,
cellRenderer: ({ data }: GenValueParams) => {
if (data.permission == 2 && 2 <= data.officerLevel && 4 <= data.officerLevel) {
return `${data.officer_city} ${data.officerLevelText}`;
}
return data.officerLevelText;
},
filter: true,
cellClass: centerCellClass,
width: 90,
},
expDedLv: {
headerName: "명성",
groupId: "expDedLv",
width: 70,
children: [
{
headerName: "/계급",
columnGroupShow: "closed",
width: 70,
cellRenderer: ({ data }: GenValueParams) => {
return `Lv ${data.explevel}<br>${data.dedLevelText}`;
},
},
{
headerName: "명성",
field: "explevel",
width: 75,
cellRenderer: ({ data }: GenValueParams) => {
return `Lv ${data.explevel}<br>(${data.honorText})`;
},
...sortableNumber,
columnGroupShow: "open",
},
{
headerName: "계급",
field: "dedLevelText",
width: 75,
cellRenderer: ({ data }: GenValueParams) => {
return `${data.dedLevelText}<br>(${data.bill.toLocaleString()})`;
},
...sortableNumber,
cellClass: centerCellClass,
columnGroupShow: "open",
},
],
},
gold: { headerName: "금", field: "gold", ...sortableNumber, valueFormatter: numberFormatter, width: 75 },
rice: { headerName: "쌀", field: "rice", ...sortableNumber, valueFormatter: numberFormatter, width: 75 },
specials: {
headerName: "성격/내특/전특",
children: [
{
headerName: "요약",
cellRenderer: GridTooltipCell,
cellRendererParams: {
cells: ((): GridCellInfo[][] => {
return [
[{ target: "personal", iActionMap: gameConstStore.value.iActionInfo.personality }],
[
{ target: "specialDomestic", iActionMap: gameConstStore.value.iActionInfo.specialDomestic },
{ target: "specialWar", iActionMap: gameConstStore.value.iActionInfo.specialWar },
],
];
})(),
},
width: 90,
columnGroupShow: "closed",
},
{
headerName: "성격",
field: "personal",
cellRenderer: SimpleTooltipCell,
cellRendererParams: {
iActionMap: gameConstStore.value.iActionInfo.personality,
},
width: 70,
sortable: true,
filter: true,
columnGroupShow: "open",
},
{
headerName: "내특",
field: "specialDomestic",
cellRenderer: SimpleTooltipCell,
cellRendererParams: {
iActionMap: gameConstStore.value.iActionInfo.specialDomestic,
},
width: 70,
sortable: true,
filter: true,
columnGroupShow: "open",
},
{
headerName: "전특",
field: "specialWar",
cellRenderer: SimpleTooltipCell,
cellRendererParams: {
iActionMap: gameConstStore.value.iActionInfo.specialWar,
},
width: 70,
sortable: true,
filter: true,
columnGroupShow: "open",
},
],
},
years: {
headerName: "연도",
children: [
{
headerName: "요약",
cellRenderer: ({ data }: GenValueParams) => {
return `${data.age}세<br>${data.belong}년`;
},
cellClass: centerCellClass,
columnGroupShow: "closed",
},
{
headerName: "연령",
field: "age",
...sortableNumber,
valueFormatter: (v: GenValueParams) => `${v.value}세`,
width: 70,
cellClass: centerCellClass,
columnGroupShow: "open",
},
{
headerName: "사관",
field: "belong",
...sortableNumber,
valueFormatter: (v: GenValueParams) => `${v.value}년`,
width: 70,
cellClass: centerCellClass,
columnGroupShow: "open",
},
],
},
killturnAndConnect: {
headerName: "기타",
children: [
{
headerName: "삭/벌",
cellRenderer: ({ data }: GenValueParams) => {
return `${data.killturn.toLocaleString()}턴<br>${data.connect.toLocaleString()}점`;
},
cellClass: rightAlignClass,
columnGroupShow: "closed",
},
{
headerName: "삭턴",
field: "killturn",
cellRenderer: ({ data }: GenValueParams) => {
return `${data.killturn.toLocaleString()}턴`;
},
...sortableNumber,
columnGroupShow: "open",
},
{
headerName: "벌점",
field: "connect",
cellRenderer: ({ data }: GenValueParams) => {
return `${data.connect.toLocaleString()}점`;
},
...sortableNumber,
columnGroupShow: "open",
},
],
},
});
setRowHeight();
const columnDefs = ref([...Object.values(columnRawDefs.value)]);
watch(columnRawDefs, (val) => {
setRowHeight();
columnDefs.value = [...Object.values(val)];
gridApi.value?.redrawRows();
});
/*
let currentWidth = -1;
window.addEventListener("resize", (e) => {
setTimeout(() => {
const newWidth = document.querySelector(".container")?.clientWidth ?? -1;
if (currentWidth != newWidth) {
currentWidth = newWidth;
columnApi.value?.autoSizeAllColumns();
}
}, 1);
});
*/
</script>
<style scoped lang="scss">
.g-tr {
border-bottom: solid gray 1px;
}
.g-thead-tr {
position: sticky;
top: 0px;
z-index: 5;
}
</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;
}
.cell-center {
justify-content: space-around;
text-align: center;
}
.cell-right {
justify-content: flex-end;
text-align: right;
}
.cell-sp .col {
min-width: 30px;
}
}
</style>