597 lines
15 KiB
Vue
597 lines
15 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, { type GridCellInfo } from "@/gridCellRenderer/GridTooltipCell.vue";
|
|
import { formatConnectScore } from "@/utilGame/formatConnectScore";
|
|
|
|
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;
|
|
}
|
|
|
|
function naiveCheClassNameFilter(value: string): string {
|
|
const text = value.split("_").pop() ?? "None";
|
|
if (text === "None") {
|
|
return "-";
|
|
}
|
|
return text;
|
|
}
|
|
|
|
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: 80,
|
|
});
|
|
|
|
const columnRawDefs = ref<Partial<Record<headerType, GenColDef | GenColGroupDef>>>({
|
|
icon: {
|
|
headerName: "아이콘",
|
|
width: 64 + 16,
|
|
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: 120,
|
|
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: 88,
|
|
|
|
cellRenderer: (obj: GenValueParams) => {
|
|
const gen = obj.data;
|
|
return `${gen.leadership}|${gen.strength}|${gen.intel}`;
|
|
},
|
|
columnGroupShow: "closed",
|
|
},
|
|
{
|
|
headerName: "통솔",
|
|
field: "leadership",
|
|
...sortableNumber,
|
|
columnGroupShow: "open",
|
|
width: 60,
|
|
type: "numericColumn",
|
|
},
|
|
{
|
|
headerName: "무력",
|
|
field: "strength",
|
|
...sortableNumber,
|
|
columnGroupShow: "open",
|
|
width: 60,
|
|
},
|
|
{
|
|
headerName: "지력",
|
|
field: "intel",
|
|
...sortableNumber,
|
|
columnGroupShow: "open",
|
|
width: 60,
|
|
},
|
|
],
|
|
},
|
|
officerLevel: {
|
|
headerName: "관직",
|
|
field: "officerLevelText",
|
|
sortable: true,
|
|
comparator: (a, b, c, d) => c.data.officerLevel - d.data.officerLevel,
|
|
cellRenderer: ({ data }: GenValueParams) => {
|
|
if ([2, 3, 4].includes(data.permission)) {
|
|
const dataP2 = data as GeneralListItemP2;
|
|
if (2 <= dataP2.officerLevel && dataP2.officerLevel <= 4) {
|
|
const cityName = gameConstStore.value.cityConst[dataP2.officer_city].name;
|
|
return `${cityName}<br>${dataP2.officerLevelText}`;
|
|
}
|
|
}
|
|
return data.officerLevelText;
|
|
},
|
|
filterValueGetter: ({ data }) => {
|
|
if ([2, 3, 4].includes(data.permission)) {
|
|
const dataP2 = data as GeneralListItemP2;
|
|
if (2 <= dataP2.officerLevel && dataP2.officerLevel <= 4) {
|
|
const cityName = gameConstStore.value.cityConst[dataP2.officer_city].name;
|
|
return `${cityName} ${dataP2.officerLevelText}`;
|
|
}
|
|
}
|
|
return data.officerLevelText;
|
|
},
|
|
filter: true,
|
|
cellClass: centerCellClass,
|
|
width: 70,
|
|
},
|
|
expDedLv: {
|
|
headerName: "명성",
|
|
groupId: "expDedLv",
|
|
width: 70,
|
|
children: [
|
|
{
|
|
headerName: "/계급",
|
|
columnGroupShow: "closed",
|
|
width: 60,
|
|
cellRenderer: ({ data }: GenValueParams) => {
|
|
return `Lv ${data.explevel}<br>${data.dedLevelText}`;
|
|
},
|
|
},
|
|
{
|
|
headerName: "명성",
|
|
field: "explevel",
|
|
width: 60,
|
|
cellRenderer: ({ data }: GenValueParams) => {
|
|
return `Lv ${data.explevel}<br>(${data.honorText})`;
|
|
},
|
|
...sortableNumber,
|
|
cellClass: centerCellClass,
|
|
columnGroupShow: "open",
|
|
},
|
|
{
|
|
headerName: "계급",
|
|
field: "dedLevelText",
|
|
width: 70,
|
|
cellRenderer: ({ data }: GenValueParams) => {
|
|
return `${data.dedLevelText}<br>(${data.bill.toLocaleString()})`;
|
|
},
|
|
...sortableNumber,
|
|
cellClass: centerCellClass,
|
|
columnGroupShow: "open",
|
|
},
|
|
],
|
|
},
|
|
gold: { headerName: "금", field: "gold", ...sortableNumber, valueFormatter: numberFormatter, width: 70 },
|
|
rice: { headerName: "쌀", field: "rice", ...sortableNumber, valueFormatter: numberFormatter, width: 70 },
|
|
crew: {
|
|
headerName: "보유 병력",
|
|
children: [
|
|
{
|
|
headerName: "병종",
|
|
cellRenderer: GridTooltipCell,
|
|
cellRendererParams: {
|
|
cells: ((): GridCellInfo[][] => {
|
|
return [
|
|
[{ target: "crewtype", iActionMap: gameConstStore.value.iActionInfo.crewtype }],
|
|
[{ target: "crew", converter: (value) => [`${value.crew.toLocaleString()}명`, undefined] }],
|
|
];
|
|
})(),
|
|
},
|
|
},
|
|
{
|
|
headerName: "훈/사",
|
|
width: 60,
|
|
cellRenderer: ({ data }: GenValueParams) => {
|
|
if (data.permission == 0 || data.permission == 1) {
|
|
return "?";
|
|
}
|
|
return `${data.train}<br>${data.atmos}`;
|
|
},
|
|
},
|
|
],
|
|
},
|
|
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: 80,
|
|
columnGroupShow: "closed",
|
|
},
|
|
{
|
|
headerName: "성격",
|
|
field: "personal",
|
|
cellRenderer: SimpleTooltipCell,
|
|
cellRendererParams: {
|
|
iActionMap: gameConstStore.value.iActionInfo.personality,
|
|
},
|
|
width: 60,
|
|
sortable: true,
|
|
filter: true,
|
|
columnGroupShow: "open",
|
|
},
|
|
{
|
|
headerName: "내특",
|
|
field: "specialDomestic",
|
|
cellRenderer: SimpleTooltipCell,
|
|
cellRendererParams: {
|
|
iActionMap: gameConstStore.value.iActionInfo.specialDomestic,
|
|
},
|
|
width: 60,
|
|
sortable: true,
|
|
filter: true,
|
|
columnGroupShow: "open",
|
|
},
|
|
{
|
|
headerName: "전특",
|
|
field: "specialWar",
|
|
cellRenderer: SimpleTooltipCell,
|
|
cellRendererParams: {
|
|
iActionMap: gameConstStore.value.iActionInfo.specialWar,
|
|
},
|
|
width: 60,
|
|
sortable: true,
|
|
filter: true,
|
|
columnGroupShow: "open",
|
|
},
|
|
],
|
|
},
|
|
years: {
|
|
headerName: "연도",
|
|
children: [
|
|
{
|
|
headerName: "요약",
|
|
width: 60,
|
|
cellRenderer: ({ data }: GenValueParams) => {
|
|
return `${data.age}세<br>${data.belong}년`;
|
|
},
|
|
cellClass: centerCellClass,
|
|
columnGroupShow: "closed",
|
|
},
|
|
{
|
|
headerName: "연령",
|
|
field: "age",
|
|
...sortableNumber,
|
|
valueFormatter: (v: GenValueParams) => `${v.value}세`,
|
|
width: 60,
|
|
cellClass: centerCellClass,
|
|
columnGroupShow: "open",
|
|
},
|
|
{
|
|
headerName: "사관",
|
|
field: "belong",
|
|
...sortableNumber,
|
|
valueFormatter: (v: GenValueParams) => `${v.value}년`,
|
|
width: 60,
|
|
cellClass: centerCellClass,
|
|
columnGroupShow: "open",
|
|
},
|
|
],
|
|
},
|
|
reservedCommand: {
|
|
headerName: "명령",
|
|
children: [
|
|
{
|
|
headerName: "단축",
|
|
width: 70,
|
|
cellRenderer: ({ data }: GenValueParams) => {
|
|
if (data.npc >= 2) {
|
|
return "NPC 장수";
|
|
}
|
|
const commandList = data.reservedCommand;
|
|
if (!commandList) {
|
|
return "???";
|
|
}
|
|
return commandList.map(({ action }) => naiveCheClassNameFilter(action)).join("<br>");
|
|
},
|
|
cellStyle: {
|
|
lineHeight: "1em",
|
|
fontSize: "0.85em",
|
|
},
|
|
columnGroupShow: "closed",
|
|
},
|
|
{
|
|
headerName: "전체",
|
|
width: 120,
|
|
cellRenderer: ({ data }: GenValueParams) => {
|
|
if (data.npc >= 2) {
|
|
return "NPC 장수";
|
|
}
|
|
const commandList = data.reservedCommand;
|
|
if (!commandList) {
|
|
return "???";
|
|
}
|
|
return commandList.map(({ brief }) => brief).join("<br>");
|
|
},
|
|
cellStyle: {
|
|
lineHeight: "1em",
|
|
fontSize: "0.85em",
|
|
},
|
|
columnGroupShow: "open",
|
|
},
|
|
],
|
|
},
|
|
killturnAndConnect: {
|
|
headerName: "기타",
|
|
children: [
|
|
{
|
|
headerName: "삭/벌",
|
|
cellRenderer: ({ data }: GenValueParams) => {
|
|
return `${data.killturn.toLocaleString()}턴<br>${data.connect.toLocaleString()}점`;
|
|
},
|
|
cellClass: rightAlignClass,
|
|
columnGroupShow: "closed",
|
|
width: 70,
|
|
},
|
|
{
|
|
headerName: "삭턴",
|
|
field: "killturn",
|
|
cellRenderer: ({ data }: GenValueParams) => {
|
|
return `${data.killturn.toLocaleString()}턴`;
|
|
},
|
|
...sortableNumber,
|
|
width: 70,
|
|
columnGroupShow: "open",
|
|
},
|
|
{
|
|
headerName: "벌점",
|
|
field: "connect",
|
|
cellRenderer: ({ data }: GenValueParams) => {
|
|
return `${data.connect.toLocaleString()}점<br>(${formatConnectScore(data.connect)})`;
|
|
},
|
|
...sortableNumber,
|
|
width: 70,
|
|
columnGroupShow: "open",
|
|
},
|
|
],
|
|
},
|
|
});
|
|
setRowHeight();
|
|
|
|
const columnDefs = ref([...Object.values(columnRawDefs.value)]);
|
|
|
|
watch(columnRawDefs, (val) => {
|
|
setRowHeight();
|
|
columnDefs.value = [...Object.values(val)];
|
|
gridApi.value?.redrawRows();
|
|
});
|
|
|
|
</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;
|
|
}
|
|
|
|
.ag-header-cell,
|
|
.ag-header-group-cell,
|
|
.ag-cell {
|
|
padding-left: 4px;
|
|
padding-right: 4px;
|
|
}
|
|
|
|
.ag-header-cell-label,
|
|
.ag-header-group-cell-label {
|
|
justify-content: center;
|
|
}
|
|
|
|
.ag-ltr .ag-floating-filter-button {
|
|
margin-left: 2px;
|
|
}
|
|
|
|
.ag-rtl .ag-floating-filter-button {
|
|
margin-right: 2px;
|
|
}
|
|
}
|
|
</style>
|