diff --git a/hwe/sammo/API/Nation/GeneralList.php b/hwe/sammo/API/Nation/GeneralList.php index d29ab73d..9dea5450 100644 --- a/hwe/sammo/API/Nation/GeneralList.php +++ b/hwe/sammo/API/Nation/GeneralList.php @@ -147,7 +147,7 @@ class GeneralList extends \sammo\BaseAPI [$queryColumns, $rankColumns] = General::mergeQueryColumn(array_keys(static::$viewColumns), 1); - $rawGeneralList = Util::convertArrayToDict($db->query('SELECT %l from general WHERE nation = %i', Util::formatListOfBackticks($queryColumns), $nationID), 'no'); + $rawGeneralList = Util::convertArrayToDict($db->query('SELECT %l from general WHERE nation = %i ORDER BY turntime ASC', Util::formatListOfBackticks($queryColumns), $nationID), 'no'); $reservedCommand = []; if ($this->permission >= 2) { @@ -200,7 +200,6 @@ class GeneralList extends \sammo\BaseAPI $getRankVar = fn ($key) => (fn ($rawGeneral) => (($rankList[$rawGeneral['no']] ?? [])[$key] ?? 0)); - $specialViewFilter = [ 'officerLevel' => fn ($rawGeneral) => $this->getOfficerLevel($rawGeneral), 'officerLevelText' => fn ($rawGeneral) => getOfficerLevelText($this->getOfficerLevel($rawGeneral), $nationArr['level']), @@ -262,11 +261,20 @@ class GeneralList extends \sammo\BaseAPI $generalList[] = $item; } + if($this->permission >= 2){ + $troops = $db->queryAllLists('SELECT troop_leader,name FROM troop WHERE nation = %i', $nationID); + } + else{ + $troops = null; + } + + $result = [ 'result' => true, 'permission' => $this->permission, 'column' => array_keys($resultColumns), 'list' => $generalList, + 'troops' => $troops, 'env' => $env, ]; diff --git a/hwe/ts/PageNationGeneral.vue b/hwe/ts/PageNationGeneral.vue index d5ac1313..b1ba3c60 100644 --- a/hwe/ts/PageNationGeneral.vue +++ b/hwe/ts/PageNationGeneral.vue @@ -1,7 +1,8 @@ @@ -25,46 +26,57 @@ import GeneralList from "./components/GeneralList.vue"; import { type GameConstStore, getGameConstStore } from "./GameConstStore"; const generalList = ref([]); -const envVal = ref({ +const troopList = ref>({}); +const envVal = ref({ year: 1, month: 1, turnterm: 1, - turntime: '2022-02-22 22:22:22.22222' + turntime: "2022-02-22 22:22:22.22222", }); const gameConstStore = ref(); -provide('gameConstStore', gameConstStore); +provide("gameConstStore", gameConstStore); const asyncReady = ref(false); const title = "세력 장수"; -const storeP = getGameConstStore().then((store)=>{ +const storeP = getGameConstStore().then((store) => { gameConstStore.value = store; }); -void Promise.all([storeP]).then(()=>{ +void Promise.all([storeP]).then(() => { asyncReady.value = true; }); onMounted(async () => { try { - const { column, list, permission, env } = await SammoAPI.Nation.GeneralList(); + const { column, list, permission, troops, env } = await SammoAPI.Nation.GeneralList(); + troopList.value = {}; //XXX: 로직상 똑같은데.... if (permission == 0) { const rawGeneralList = merge2DArrToObjectArr(column, list); - generalList.value = rawGeneralList.map((v) => {return { permission, ...v }}); + generalList.value = rawGeneralList.map((v) => { + return { permission, ...v }; + }); } else if (permission == 1) { const rawGeneralList = merge2DArrToObjectArr(column, list); - generalList.value = rawGeneralList.map((v) => {return { permission, ...v }}); + generalList.value = rawGeneralList.map((v) => { + return { permission, ...v }; + }); } else if ([2, 3, 4].includes(permission)) { const rawGeneralList = merge2DArrToObjectArr(column, list); - generalList.value = rawGeneralList.map((v) => {return { permission, ...v }}); + generalList.value = rawGeneralList.map((v) => { + return { permission, ...v }; + }); + + for(const [troopLeader, troopName] of troops){ + troopList.value[troopLeader] = troopName; + } } else { throw `?? ${permission}`; } envVal.value = env; - } catch (e) { console.error(e); throw e; @@ -73,13 +85,15 @@ onMounted(async () => { \ No newline at end of file + diff --git a/hwe/ts/components/GeneralList.vue b/hwe/ts/components/GeneralList.vue index f122a5e3..eeac4d8f 100644 --- a/hwe/ts/components/GeneralList.vue +++ b/hwe/ts/components/GeneralList.vue @@ -32,6 +32,7 @@ import type { GetRowIdParams, GridApi, GridReadyEvent, + RowNode, } from "ag-grid-community"; import { getNpcColor } from "@/common_legacy"; import type { BaseWithValueColDefParams, ValueGetterParams } from "ag-grid-community/dist/lib/entities/colDef"; @@ -40,6 +41,9 @@ import { unwrap } from "@/util/unwrap"; import SimpleTooltipCell from "@/gridCellRenderer/SimpleTooltipCell.vue"; import GridTooltipCell, { type GridCellInfo } from "@/gridCellRenderer/GridTooltipCell.vue"; import { formatConnectScore } from "@/utilGame/formatConnectScore"; +import { convertSearch초성 } from "@/util/convertSearch초성"; +import { isString } from "lodash"; +import { formatDefenceTrain } from "@/utilGame/formatDefenceTrain"; const props = defineProps({ list: { @@ -47,6 +51,11 @@ const props = defineProps({ required: true, }, + troops: { + type: Object as PropType>, + required: true, + }, + height: { type: String as PropType<"static" | "fill" | number | `${number}px` | `${number}%`>, required: false, @@ -82,6 +91,18 @@ watch( } ); +const generalByID = ref(new Map()); + +function refineGeneralList(list: GeneralListItem[]) { + const map = new Map(); + for (const general of list) { + map.set(general.no, general); + } + generalByID.value = map; +} +refineGeneralList(props.list); +watch(() => props.list, refineGeneralList); + const gridApi = ref(); const columnApi = ref(); @@ -133,10 +154,15 @@ interface GenValueGetterParams extends ValueGetterParams { data: GeneralListItem; } +interface GenRowNode extends RowNode { + data: GeneralListItem; +} + interface GenColDef extends ColDef { field?: headerType; valueFormatter?: string | ((params: GenValueParams) => string); - filterValueGetter?: string | ((params: GenValueGetterParams) => string); + filterValueGetter?: string | ((params: GenValueGetterParams) => unknown); + valueGetter?: string | ((params: GenValueGetterParams) => unknown); } interface GenColGroupDef extends ColGroupDef { @@ -156,8 +182,32 @@ function naiveCheClassNameFilter(value: string): string { return text; } -function numberFormatter(value: GenValueParams): string { - return (value.value as number).toLocaleString(); +function numberFormatter(unit?: string) { + if (unit) { + return (value: GenValueParams): string => { + const valueText = (value.value as number).toLocaleString(); + return `${valueText} ${unit}`; + }; + } + return (value: GenValueParams): string => { + return (value.value as number).toLocaleString(); + }; +} + +function extractTroopInfo(value: GeneralListItem): [string, GeneralListItemP2] | undefined { + if (value.permission == 0 || value.permission == 1) { + return undefined; + } + const troopID = value.troop; + if (!(troopID in props.troops)) { + return undefined; + } + const troopName = props.troops[troopID]; + const troopLeader = generalByID.value.get(troopID) as GeneralListItemP2 | undefined; + if (troopLeader === undefined) { + return undefined; + } + return [troopName, troopLeader]; } const defaultCellClass = ["cell-middle"]; @@ -206,6 +256,7 @@ const columnRawDefs = ref }; return style as CellStyle; }, + filterValueGetter: ({ data }) => convertSearch초성(data.name), cellClass: defaultCellClass, filter: true, }, @@ -269,10 +320,10 @@ const columnRawDefs = ref 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 convertSearch초성(`${cityName} ${dataP2.officerLevelText}`); } } - return data.officerLevelText; + return convertSearch초성(data.officerLevelText); }, filter: true, cellClass: centerCellClass, @@ -315,8 +366,116 @@ const columnRawDefs = ref }, ], }, - gold: { headerName: "금", field: "gold", ...sortableNumber, valueFormatter: numberFormatter, width: 70 }, - rice: { headerName: "쌀", field: "rice", ...sortableNumber, valueFormatter: numberFormatter, width: 70 }, + goldRice: { + headerName: "자금", + + children: [ + { + headerName: "금/쌀", + cellRenderer: ({ data }: GenValueParams) => { + return `${data.gold.toLocaleString()} 금
${data.rice.toLocaleString()} 쌀`; + }, + width: 80, + cellClass: rightAlignClass, + columnGroupShow: "closed", + sortable: true, + sortingOrder: ["desc", "asc", null], + comparator(_a, _b, { data: lhs }: GenRowNode, { data: rhs }: GenRowNode) { + const lhsAmount = lhs.gold + lhs.rice; + const rhsAmount = rhs.gold + rhs.rice; + return lhsAmount - rhsAmount; + }, + }, + { + headerName: "금", + field: "gold", + ...sortableNumber, + valueFormatter: numberFormatter("금"), + width: 70, + columnGroupShow: "open", + }, + { + headerName: "쌀", + field: "rice", + ...sortableNumber, + valueFormatter: numberFormatter("쌀"), + width: 70, + columnGroupShow: "open", + }, + ], + }, + + city: { + headerName: "도시", + field: "city", + valueGetter: ({ data }) => { + if (data.permission == 0 || data.permission == 1) { + return "-"; + } + return gameConstStore.value.cityConst[data.city].name; + }, + filter: true, + sortable: true, + width: 60, + filterValueGetter: ({ data }) => { + if (data.permission == 0 || data.permission == 1) { + return "-"; + } + return convertSearch초성(gameConstStore.value.cityConst[data.city].name); + }, + }, + + troop: { + headerName: "부대", + field: "troop", + valueGetter: ({ data }: GenValueGetterParams) => { + const troopInfo = extractTroopInfo(data); + if (troopInfo === undefined) { + return "-"; + } + + const [troopName, troopLeader] = troopInfo; + const cityName = gameConstStore.value.cityConst[troopLeader.city].name; + return [troopName, cityName]; + }, + cellRenderer: ({ value }: { value: [string, string] | string }) => { + if (isString(value)) { + return value; + } + const [troopName, cityName] = value; + return `${troopName}
[${cityName}]`; + }, + width: 90, + sortable: true, + comparator: (valX, valB, { data: lhs }: GenRowNode, { data: rhs }: GenRowNode) => { + const troopInfoLhs = extractTroopInfo(lhs); + const troopInfoRhs = extractTroopInfo(rhs); + console.log(troopInfoLhs, troopInfoRhs); + if (troopInfoLhs === troopInfoRhs) { + return 0; + } + if (troopInfoLhs === undefined) { + return 1; + } + if (troopInfoRhs === undefined) { + return -1; + } + + return troopInfoLhs[0].localeCompare(troopInfoRhs[0]); + }, + filter: true, + filterValueGetter: ({ data }) => { + const troopInfo = extractTroopInfo(data); + if (troopInfo === undefined) { + return "-"; + } + const [troopName, troopLeader] = troopInfo; + const cityName = gameConstStore.value.cityConst[troopLeader.city].name; + + return convertSearch초성(`${troopName}$${cityName}`); + }, + }, + crew: { headerName: "보유 병력", children: [ @@ -331,6 +490,7 @@ const columnRawDefs = ref ]; })(), }, + columnGroupShow: "closed", }, { headerName: "훈/사", @@ -341,6 +501,57 @@ const columnRawDefs = ref } return `${data.train}
${data.atmos}`; }, + columnGroupShow: "closed", + }, + { + headerName: "병종", + field: "crewtype", + cellRenderer: SimpleTooltipCell, + cellRendererParams: { + iActionMap: gameConstStore.value.iActionInfo.crewtype, + }, + sortable: true, + columnGroupShow: "open", + filter: true, + filterValueGetter: ({ data }) => { + if (data.permission == 0 || data.permission == 1) { + return "-"; + } + const name = gameConstStore.value.iActionInfo.crewtype[data.crewtype].name; + return convertSearch초성(name); + }, + }, + { + headerName: "병력", + field: "crew", + ...sortableNumber, + valueFormatter: numberFormatter("명"), + width: 70, + columnGroupShow: "open", + }, + { + headerName: "훈련", + field: "train", + ...sortableNumber, + valueFormatter: numberFormatter(), + width: 70, + columnGroupShow: "open", + }, + { + headerName: "병력", + field: "atmos", + ...sortableNumber, + valueFormatter: numberFormatter(), + width: 70, + columnGroupShow: "open", + }, + { + headerName: "수비", + field: "defence_train", + sortable: true, + sortingOrder: ["desc", "asc", null], + valueFormatter: ({ value }) => formatDefenceTrain(value as number), + width: 50, }, ], }, @@ -375,6 +586,10 @@ const columnRawDefs = ref sortable: true, filter: true, columnGroupShow: "open", + filterValueGetter: ({ data }) => { + const name = gameConstStore.value.iActionInfo.personality[data.personal].name; + return convertSearch초성(name); + }, }, { headerName: "내특", @@ -387,6 +602,10 @@ const columnRawDefs = ref sortable: true, filter: true, columnGroupShow: "open", + filterValueGetter: ({ data }) => { + const name = gameConstStore.value.iActionInfo.specialDomestic[data.specialDomestic].name; + return convertSearch초성(name); + }, }, { headerName: "전특", @@ -399,38 +618,10 @@ const columnRawDefs = ref sortable: true, filter: true, columnGroupShow: "open", - }, - ], - }, - years: { - headerName: "연도", - children: [ - { - headerName: "요약", - width: 60, - cellRenderer: ({ data }: GenValueParams) => { - return `${data.age}세
${data.belong}년`; + filterValueGetter: ({ data }) => { + const name = gameConstStore.value.iActionInfo.specialWar[data.specialWar].name; + return convertSearch초성(name); }, - 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", }, ], }, @@ -477,6 +668,52 @@ const columnRawDefs = ref }, ], }, + turntime: { + headerName: "턴", + field: 'turntime', + width: 60, + valueFormatter: ({value, data}) => { + if(data.permission == 0 || data.permission == 1){ + return '-'; + } + const turntime = value as string; + return turntime.substring(14, 19); + }, + sortable: true, + cellClass: centerCellClass, + }, + years: { + headerName: "연도", + children: [ + { + headerName: "요약", + width: 60, + cellRenderer: ({ data }: GenValueParams) => { + return `${data.age}세
${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", + }, + ], + }, killturnAndConnect: { headerName: "기타", children: [ @@ -521,7 +758,6 @@ watch(columnRawDefs, (val) => { columnDefs.value = [...Object.values(val)]; gridApi.value?.redrawRows(); }); -