feat(wip): 세력 장수에서 '열 선택' 기능 추가

This commit is contained in:
2022-04-07 03:03:38 +09:00
parent 9133bbc3d4
commit 96b1a9a8bc
3 changed files with 292 additions and 48 deletions
+8 -5
View File
@@ -1,9 +1,9 @@
<template>
<BContainer id="container" :toast="{ root: true }" class="pageNationGeneral bg0">
<TopBackBar :title="title" />
<TopBackBar :title="title" :teleport-zone="toolbarID" />
<!-- eslint-disable-next-line vue/max-attributes-per-line -->
<GeneralList v-if="asyncReady" :list="generalList" :troops="troopList" :env="envVal" :height="'fill'" />
<BottomBar />
<GeneralList v-if="asyncReady" :list="generalList" :troops="troopList" :env="envVal" :toolbarID="toolbarID" :height="'fill'" />
<!--<BottomBar />-->
</BContainer>
</template>
@@ -16,7 +16,7 @@
</script>
<script lang="ts" setup>
import TopBackBar from "@/components/TopBackBar.vue";
import BottomBar from "@/components/BottomBar.vue";
//import BottomBar from "@/components/BottomBar.vue";
import { BContainer } from "bootstrap-vue-3";
import { onMounted, provide, ref } from "vue";
import { SammoAPI } from "./SammoAPI";
@@ -34,6 +34,8 @@ const envVal = ref<GeneralListResponse["env"]>({
turntime: "2022-02-22 22:22:22.22222",
});
const toolbarID = 'toolbar-id';
const gameConstStore = ref<GameConstStore>();
provide("gameConstStore", gameConstStore);
@@ -93,7 +95,8 @@ body,
.pageNationGeneral {
display: grid;
grid-template-rows: auto 1fr auto;
//grid-template-rows: auto 1fr auto;
grid-template-rows: auto 1fr;
height: 100%;
}
</style>
+255 -31
View File
@@ -1,4 +1,39 @@
<template>
<Teleport v-if="toolbarID" :to="`#${toolbarID}`">
<BButtonGroup class="d-flex general-list-toolbar">
<BDropdown class="w-50" variant="primary" text="보기 모드">
<BDropdownItem></BDropdownItem>
<BDropdownItem></BDropdownItem>
<BDropdownDivider />
<BDropdownItem></BDropdownItem>
<BDropdownItem></BDropdownItem>
</BDropdown>
<BDropdown class="w-50" variant="info" text="열 선택" menuClass="column-menu" right>
<template v-for="[colID, col, depth] of getColumnList()" :key="[colID, depth]">
<BDropdownItem v-if="col instanceof ColumnGroup" disabled>
<span :style="{ marginLeft: depth ? `${12 * depth}px` : undefined }">
{{ col.getColGroupDef()?.headerName }}</span
></BDropdownItem
>
<BDropdownItem v-else>
<div :style="{ marginLeft: depth ? `${12 * depth}px` : undefined }" class="form-check" @click.stop="1">
<input
:id="`column-type-${colID}`"
class="form-check-input"
type="checkbox"
:checked="col.isVisible()"
@change.stop="toggleColumn(colID, col)"
/>
<label class="form-check-label" :for="`column-type-${colID}`">
{{ col.getColDef().headerName }}
</label>
</div></BDropdownItem
>
</template>
<BDropdownDivider />
</BDropdown>
</BButtonGroup>
</Teleport>
<div
class="component-general-list"
:style="{
@@ -24,6 +59,7 @@ 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 {
Column,
CellClassParams,
CellStyle,
ColDef,
@@ -34,6 +70,7 @@ import type {
GridReadyEvent,
RowNode,
} from "ag-grid-community";
import { ColumnGroup } 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";
@@ -44,6 +81,9 @@ import { formatConnectScore } from "@/utilGame/formatConnectScore";
import { convertSearch초성 } from "@/util/convertSearch초성";
import { isString } from "lodash";
import { formatDefenceTrain } from "@/utilGame/formatDefenceTrain";
import { BDropdownItem, BDropdownDivider, BButtonGroup, BDropdown } from "bootstrap-vue-3";
import { unwrap_err } from "@/util/unwrap_err";
import { RuntimeError } from "@/util/RuntimeError";
const props = defineProps({
list: {
@@ -65,6 +105,12 @@ const props = defineProps({
type: Object as PropType<GeneralListResponse["env"]>,
required: true,
},
toolbarID: {
type: String,
required: false,
default: undefined,
},
});
const gameConstStore = unwrap(inject<Ref<GameConstStore>>("gameConstStore"));
@@ -116,20 +162,29 @@ function getRowId(params: GetRowIdParams): string {
function onGridReady(params: GridReadyEvent) {
gridApi.value = params.api;
columnApi.value = params.columnApi;
setRowHeight();
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;
if (columnApi.value === undefined) {
console.error("아직 준비 안됨?");
return 28;
}
rowHeight.value = gridApi.value?.getSizesForCurrentTheme().rowHeight ?? 28;
let height = gridApi.value?.getSizesForCurrentTheme().rowHeight ?? 28;
if (columnApi.value.getColumn("icon")?.isVisible()) {
height = Math.max(height, 64 + 4);
}
if (columnApi.value.getColumn("reservedCommand")?.isVisible()) {
height = Math.max(height, 64 + 4);
}
rowHeight.value = height;
}
function getRowHeight(): number {
@@ -142,7 +197,10 @@ type headerType =
| "icon"
| "goldRice"
| "expDedLv"
| "crewtypeAndCrew"
| "trainAtmos"
| "specials"
| "reservedCommandShort"
| "killturnAndConnect"
| "years";
@@ -159,14 +217,17 @@ interface GenRowNode extends RowNode {
}
interface GenColDef extends ColDef {
colId: headerType;
field?: headerType;
headerName: string;
valueFormatter?: string | ((params: GenValueParams) => string);
filterValueGetter?: string | ((params: GenValueGetterParams) => unknown);
valueGetter?: string | ((params: GenValueGetterParams) => unknown);
}
interface GenColGroupDef extends ColGroupDef {
children: (GenColDef | GenColGroupDef)[];
headerName: string;
children: GenColDef[]; //1단만 할꺼다!
groupId: headerType;
}
@@ -174,6 +235,39 @@ interface GenCellClassParams extends CellClassParams {
data: GeneralListItem;
}
function getColumnList(): [headerType, ColumnGroup | Column, number?][] {
const result: [headerType, ColumnGroup | Column, number?][] = [];
if (!columnApi.value) {
return result;
}
for (const [rawColKey, rawColDef] of Object.entries(columnRawDefs.value)) {
if (rawColKey === "name") {
continue;
}
if (!("children" in rawColDef)) {
const col = unwrap_err(columnApi.value.getColumn(rawColDef.colId), RuntimeError, `no col: ${rawColDef.colId}`);
result.push([rawColDef.colId, col]);
continue;
}
const colGroup = unwrap_err(
columnApi.value.getColumnGroup(rawColDef.groupId),
RuntimeError,
`no colGroup: ${rawColDef.groupId}`
);
result.push([rawColDef.groupId, colGroup]);
for (const subColDef of rawColDef.children) {
const subColId = subColDef.colId;
if (rawColDef.groupId == subColId) {
continue;
}
const col = unwrap_err(columnApi.value.getColumn(subColId), RuntimeError, `no subCol: ${subColDef.colId}`);
result.push([subColId, col, 1]);
}
}
return result;
}
function naiveCheClassNameFilter(value: string): string {
const text = value.split("_").pop() ?? "None";
if (text === "None") {
@@ -210,10 +304,44 @@ function extractTroopInfo(value: GeneralListItem): [string, GeneralListItemP2] |
return [troopName, troopLeader];
}
function toggleColumn(colID: headerType, col: Column) {
const newState = !col.isVisible();
const target: string[] = [colID];
const parent = col.getParent();
if (newState) {
for (const child of (parent.getChildren() ?? []) as Column[]) {
if (parent.getGroupId() == child.getColDef().colId) {
target.push(child.getColId());
break;
}
}
} else {
let stillVisible = false;
let header: string | null = null;
for (const child of (parent.getChildren() ?? []) as Column[]) {
if (child.getColId() == colID) {
continue;
}
if (parent.getGroupId() == child.getColDef().colId) {
header = child.getColId();
continue;
}
stillVisible = true;
break;
}
if (!stillVisible && header) {
target.push(header);
}
}
columnApi.value?.setColumnsVisible(target, newState);
}
const defaultCellClass = ["cell-middle"];
const centerCellClass = [...defaultCellClass, "cell-center"];
const rightAlignClass = [...defaultCellClass, "cell-right"];
const sortableNumber: GenColDef = {
const sortableNumber: Omit<GenColDef, "colId" | "headerName"> = {
sortable: true,
comparator: (a, b) => a - b,
sortingOrder: ["desc", "asc", null],
@@ -226,10 +354,12 @@ const defaultColDef = ref<ColDef>({
cellClass: centerCellClass,
floatingFilter: true,
width: 80,
//initialHide: true,
});
const columnRawDefs = ref<Partial<Record<headerType, GenColDef | GenColGroupDef>>>({
icon: {
colId: "icon",
headerName: "아이콘",
width: 64 + 16,
suppressSizeToFit: true,
@@ -243,6 +373,7 @@ const columnRawDefs = ref<Partial<Record<headerType, GenColDef | GenColGroupDef>
},
name: {
headerName: "장수명",
colId: "name",
field: "name",
pinned: "left",
sortable: true,
@@ -259,14 +390,17 @@ const columnRawDefs = ref<Partial<Record<headerType, GenColDef | GenColGroupDef>
filterValueGetter: ({ data }) => convertSearch초성(data.name),
cellClass: defaultCellClass,
filter: true,
hide: false,
lockVisible: true,
},
npc: { headerName: "NPC", field: "npc", hide: true },
//npc: { headerName: "NPC", colId: "npc", field: "npc" },
stat: {
groupId: "stat",
openByDefault: false,
headerName: "능력치",
children: [
{
colId: "stat",
headerName: "통|무|지",
width: 88,
@@ -277,6 +411,7 @@ const columnRawDefs = ref<Partial<Record<headerType, GenColDef | GenColGroupDef>
columnGroupShow: "closed",
},
{
colId: "leadership",
headerName: "통솔",
field: "leadership",
...sortableNumber,
@@ -285,6 +420,7 @@ const columnRawDefs = ref<Partial<Record<headerType, GenColDef | GenColGroupDef>
type: "numericColumn",
},
{
colId: "strength",
headerName: "무력",
field: "strength",
...sortableNumber,
@@ -292,6 +428,7 @@ const columnRawDefs = ref<Partial<Record<headerType, GenColDef | GenColGroupDef>
width: 60,
},
{
colId: "intel",
headerName: "지력",
field: "intel",
...sortableNumber,
@@ -302,6 +439,7 @@ const columnRawDefs = ref<Partial<Record<headerType, GenColDef | GenColGroupDef>
},
officerLevel: {
headerName: "관직",
colId: "officerLevel",
field: "officerLevelText",
sortable: true,
comparator: (a, b, c, d) => c.data.officerLevel - d.data.officerLevel,
@@ -330,12 +468,13 @@ const columnRawDefs = ref<Partial<Record<headerType, GenColDef | GenColGroupDef>
width: 70,
},
expDedLv: {
headerName: "명성",
headerName: "명성/계급",
groupId: "expDedLv",
width: 70,
children: [
{
headerName: "/계급",
colId: "expDedLv",
headerName: "",
columnGroupShow: "closed",
width: 60,
cellRenderer: ({ data }: GenValueParams) => {
@@ -343,6 +482,7 @@ const columnRawDefs = ref<Partial<Record<headerType, GenColDef | GenColGroupDef>
},
},
{
colId: "explevel",
headerName: "명성",
field: "explevel",
width: 60,
@@ -354,6 +494,7 @@ const columnRawDefs = ref<Partial<Record<headerType, GenColDef | GenColGroupDef>
columnGroupShow: "open",
},
{
colId: "dedlevel",
headerName: "계급",
field: "dedLevelText",
width: 70,
@@ -368,9 +509,10 @@ const columnRawDefs = ref<Partial<Record<headerType, GenColDef | GenColGroupDef>
},
goldRice: {
headerName: "자금",
groupId: "goldRice",
children: [
{
colId: "goldRice",
headerName: "금/쌀",
cellRenderer: ({ data }: GenValueParams) => {
return `${data.gold.toLocaleString()} 금<br>${data.rice.toLocaleString()}`;
@@ -387,6 +529,7 @@ const columnRawDefs = ref<Partial<Record<headerType, GenColDef | GenColGroupDef>
},
},
{
colId: "gold",
headerName: "금",
field: "gold",
...sortableNumber,
@@ -395,6 +538,7 @@ const columnRawDefs = ref<Partial<Record<headerType, GenColDef | GenColGroupDef>
columnGroupShow: "open",
},
{
colId: "rice",
headerName: "쌀",
field: "rice",
...sortableNumber,
@@ -406,6 +550,7 @@ const columnRawDefs = ref<Partial<Record<headerType, GenColDef | GenColGroupDef>
},
city: {
colId: "city",
headerName: "도시",
field: "city",
valueGetter: ({ data }) => {
@@ -426,6 +571,7 @@ const columnRawDefs = ref<Partial<Record<headerType, GenColDef | GenColGroupDef>
},
troop: {
colId: "troop",
headerName: "부대",
field: "troop",
valueGetter: ({ data }: GenValueGetterParams) => {
@@ -476,10 +622,12 @@ const columnRawDefs = ref<Partial<Record<headerType, GenColDef | GenColGroupDef>
},
},
crew: {
crewtypeAndCrew: {
groupId: "crewtypeAndCrew",
headerName: "보유 병력",
children: [
{
colId: "crewtypeAndCrew",
headerName: "병종",
cellRenderer: GridTooltipCell,
cellRendererParams: {
@@ -493,17 +641,7 @@ const columnRawDefs = ref<Partial<Record<headerType, GenColDef | GenColGroupDef>
columnGroupShow: "closed",
},
{
headerName: "훈/사",
width: 60,
cellRenderer: ({ data }: GenValueParams) => {
if (data.permission == 0 || data.permission == 1) {
return "?";
}
return `${data.train}<br>${data.atmos}`;
},
columnGroupShow: "closed",
},
{
colId: "crewtype",
headerName: "병종",
field: "crewtype",
cellRenderer: SimpleTooltipCell,
@@ -522,6 +660,7 @@ const columnRawDefs = ref<Partial<Record<headerType, GenColDef | GenColGroupDef>
},
},
{
colId: "crew",
headerName: "병력",
field: "crew",
...sortableNumber,
@@ -529,7 +668,26 @@ const columnRawDefs = ref<Partial<Record<headerType, GenColDef | GenColGroupDef>
width: 70,
columnGroupShow: "open",
},
],
},
trainAtmos: {
groupId: "trainAtmos",
headerName: "훈/사",
children: [
{
colId: "trainAtmos",
headerName: "훈/사",
width: 60,
cellRenderer: ({ data }: GenValueParams) => {
if (data.permission == 0 || data.permission == 1) {
return "?";
}
return `${data.train}<br>${data.atmos}`;
},
columnGroupShow: "closed",
},
{
colId: "train",
headerName: "훈련",
field: "train",
...sortableNumber,
@@ -538,7 +696,8 @@ const columnRawDefs = ref<Partial<Record<headerType, GenColDef | GenColGroupDef>
columnGroupShow: "open",
},
{
headerName: "병력",
colId: "atmos",
headerName: "사기",
field: "atmos",
...sortableNumber,
valueFormatter: numberFormatter(),
@@ -546,6 +705,7 @@ const columnRawDefs = ref<Partial<Record<headerType, GenColDef | GenColGroupDef>
columnGroupShow: "open",
},
{
colId: "defence_train",
headerName: "수비",
field: "defence_train",
sortable: true,
@@ -556,9 +716,11 @@ const columnRawDefs = ref<Partial<Record<headerType, GenColDef | GenColGroupDef>
],
},
specials: {
groupId: "specials",
headerName: "특성",
children: [
{
colId: "specials",
headerName: "요약",
cellRenderer: GridTooltipCell,
cellRendererParams: {
@@ -576,6 +738,7 @@ const columnRawDefs = ref<Partial<Record<headerType, GenColDef | GenColGroupDef>
columnGroupShow: "closed",
},
{
colId: "personal",
headerName: "성격",
field: "personal",
cellRenderer: SimpleTooltipCell,
@@ -592,6 +755,7 @@ const columnRawDefs = ref<Partial<Record<headerType, GenColDef | GenColGroupDef>
},
},
{
colId: "specialDomestic",
headerName: "내특",
field: "specialDomestic",
cellRenderer: SimpleTooltipCell,
@@ -608,6 +772,7 @@ const columnRawDefs = ref<Partial<Record<headerType, GenColDef | GenColGroupDef>
},
},
{
colId: "specialWar",
headerName: "전특",
field: "specialWar",
cellRenderer: SimpleTooltipCell,
@@ -625,10 +790,12 @@ const columnRawDefs = ref<Partial<Record<headerType, GenColDef | GenColGroupDef>
},
],
},
reservedCommand: {
reservedCommandShort: {
groupId: "reservedCommandShort",
headerName: "명령",
children: [
{
colId: "reservedCommandShort",
headerName: "단축",
width: 70,
cellRenderer: ({ data }: GenValueParams) => {
@@ -648,6 +815,7 @@ const columnRawDefs = ref<Partial<Record<headerType, GenColDef | GenColGroupDef>
columnGroupShow: "closed",
},
{
colId: "reservedCommand",
headerName: "전체",
width: 120,
cellRenderer: ({ data }: GenValueParams) => {
@@ -669,12 +837,13 @@ const columnRawDefs = ref<Partial<Record<headerType, GenColDef | GenColGroupDef>
],
},
turntime: {
colId: "turntime",
headerName: "턴",
field: 'turntime',
field: "turntime",
width: 60,
valueFormatter: ({value, data}) => {
if(data.permission == 0 || data.permission == 1){
return '-';
valueFormatter: ({ value, data }) => {
if (data.permission == 0 || data.permission == 1) {
return "-";
}
const turntime = value as string;
return turntime.substring(14, 19);
@@ -683,9 +852,11 @@ const columnRawDefs = ref<Partial<Record<headerType, GenColDef | GenColGroupDef>
cellClass: centerCellClass,
},
years: {
groupId: "years",
headerName: "연도",
children: [
{
colId: "years",
headerName: "요약",
width: 60,
cellRenderer: ({ data }: GenValueParams) => {
@@ -695,6 +866,7 @@ const columnRawDefs = ref<Partial<Record<headerType, GenColDef | GenColGroupDef>
columnGroupShow: "closed",
},
{
colId: "age",
headerName: "연령",
field: "age",
...sortableNumber,
@@ -704,6 +876,7 @@ const columnRawDefs = ref<Partial<Record<headerType, GenColDef | GenColGroupDef>
columnGroupShow: "open",
},
{
colId: "belong",
headerName: "사관",
field: "belong",
...sortableNumber,
@@ -715,9 +888,11 @@ const columnRawDefs = ref<Partial<Record<headerType, GenColDef | GenColGroupDef>
],
},
killturnAndConnect: {
groupId: "killturnAndConnect",
headerName: "기타",
children: [
{
colId: "killturnAndConnect",
headerName: "삭/벌",
cellRenderer: ({ data }: GenValueParams) => {
return `${data.killturn.toLocaleString()}턴<br>${data.connect.toLocaleString()}`;
@@ -727,6 +902,7 @@ const columnRawDefs = ref<Partial<Record<headerType, GenColDef | GenColGroupDef>
width: 70,
},
{
colId: "killturn",
headerName: "삭턴",
field: "killturn",
cellRenderer: ({ data }: GenValueParams) => {
@@ -737,6 +913,7 @@ const columnRawDefs = ref<Partial<Record<headerType, GenColDef | GenColGroupDef>
columnGroupShow: "open",
},
{
colId: "connect",
headerName: "벌점",
field: "connect",
cellRenderer: ({ data }: GenValueParams) => {
@@ -749,14 +926,55 @@ const columnRawDefs = ref<Partial<Record<headerType, GenColDef | GenColGroupDef>
],
},
});
setRowHeight();
type DisplaySetting = {
showColumnOrder?: headerType[];
openedGroup?: headerType[];
explicitHide?: headerType[];
sort?: [headerType, "asc" | "desc" | null][];
};
const defaultSettings: Record<"war" | "light", DisplaySetting> = {
war: {
showColumnOrder: [
"stat",
"troop",
"goldRice",
"city",
"defence_train",
"crew",
"reservedCommand",
"killturn",
"turntime",
],
openedGroup: ["goldRice", "crew"],
sort: [["turntime", "asc"]],
},
light: {
showColumnOrder: [
"icon",
"officerLevel",
"expDedLv",
"stat",
"goldRice",
"specials",
"years",
"killturnAndConnect",
],
openedGroup: ["expDedLv", "stat", "goldRice", "specials", "years", "killturnAndConnect"],
explicitHide: ["killturn"],
sort: [["connect", "desc"]],
},
};
const defaultSettingID: keyof typeof defaultSettings = "war";
const columnDefs = ref([...Object.values(columnRawDefs.value)]);
watch(columnRawDefs, (val) => {
setRowHeight();
columnDefs.value = [...Object.values(val)];
gridApi.value?.redrawRows();
gridApi.value?.refreshCells();
});
</script>
@@ -829,4 +1047,10 @@ watch(columnRawDefs, (val) => {
margin-right: 2px;
}
}
.general-list-toolbar {
.column-menu {
column-count: 3;
}
}
</style>
+29 -12
View File
@@ -1,21 +1,24 @@
<template>
<div class="bg0 back_bar">
<div :class="['bg0', 'back_bar', teleportZone?'back_bar_teleport':undefined]">
<button type="button" class="btn btn-sammo-base2 back_btn" @click="back">돌아가기</button
><button v-if="reloadable" type="button" class="btn btn-sammo-base2 reload_btn" @click="reload">갱신</button>
<div v-else />
<h2 class="title">
{{ title }}
</h2>
<div>&nbsp;</div>
<b-button
v-if="toggleSearch !== undefined"
class="btn-toggle-zoom"
:variant="toggleSearch ? 'info' : 'secondary'"
:pressed="toggleSearch"
@click="toggleSearch = !toggleSearch"
>
{{ toggleSearch ? "검색 켜짐" : "검색 꺼짐" }}
</b-button>
<div v-if="teleportZone" :id="teleportZone" class="teleport-zone"></div>
<template v-else>
<div>&nbsp;</div>
<b-button
v-if="toggleSearch !== undefined"
class="btn-toggle-zoom"
:variant="toggleSearch ? 'info' : 'secondary'"
:pressed="toggleSearch"
@click="toggleSearch = !toggleSearch"
>
{{ toggleSearch ? "검색 켜짐" : "검색 꺼짐" }}
</b-button>
</template>
</div>
</template>
@@ -41,6 +44,11 @@ const props = defineProps({
default: undefined,
required: false,
},
teleportZone: {
type: String,
default: undefined,
required: false,
},
});
const emit = defineEmits(["update:searchable", "reload"]);
@@ -72,16 +80,24 @@ function reload() {
width: 100%;
margin: auto;
display: grid;
grid-template-columns: 80px 80px 1fr 80px 80px;
grid-template-columns: 90px 90px 1fr 90px 90px;
position: relative;
height: 24pt;
}
.back_bar.back_bar_teleport {
grid-template-columns: 90px 90px 1fr 180px;
}
.reload_btn {
height: 24pt;
margin-right: 2px;
}
.teleport-zone{
height: 24pt;
}
.back_btn {
height: 24pt;
margin-right: 2px;
@@ -89,6 +105,7 @@ function reload() {
.btn-toggle-zoom {
height: 24pt;
position: relative;
}
.title {