refac: linter 관련 설정 변경 및 적용, map_theme 변수 제거

- eslint에 prettier 조합
- prettierrc에 width 120, tabWidth 2
- gameStor->map_theme 제거
- map_theme, mapTheme를 GameConst::$mapName으로 대체
- eslint에서 vue/vue3-essential 대신 vue3-recommended 적용
  - vue/max-attributes-per-line 완화
  - vue/v-on-event-hyphenation 해제
  - vue/attribute-hyphenation 해제
- 일부 tsc import type warning 해결
- 일부 vue template type warning 해결
- 일부 vue SFC를 script setup으로 변경
  - TipTap
  - TopBackBar
  - BottomBackBar
  - BoardArticle
  - ProcessCity
This commit is contained in:
2022-03-29 02:06:47 +09:00
parent e569ffe8f4
commit 4f4533e533
79 changed files with 4541 additions and 3020 deletions
+13 -3
View File
@@ -11,13 +11,15 @@ module.exports = {
files: ['*.ts', '*.tsx', "*.vue"],
}],
plugins: [
"@typescript-eslint"
"@typescript-eslint",
"prettier"
],
extends: [
"eslint:recommended",
"plugin:vue/vue3-essential",
"plugin:@typescript-eslint/recommended"
"plugin:vue/vue3-recommended",
"plugin:@typescript-eslint/recommended",
"prettier",
],
parserOptions: {
"sourceType": "module",
@@ -29,6 +31,14 @@ module.exports = {
rules: {
'@typescript-eslint/no-floating-promises': 'error',
'vue/script-setup-uses-vars': 'error',
'vue/max-attributes-per-line': [
'warn',
{
singleline: 4, //prettier와 싸우지 말자
},
],
'vue/v-on-event-hyphenation': 'off', //vue3에선 필요없다고 생각
'vue/attribute-hyphenation': 'off', //vue3에선 필요없다고 생각
},
settings: {
'import/resolver': {
+5
View File
@@ -0,0 +1,5 @@
{
"tabWidth": 2,
"useTabs": false,
"printWidth": 120
}
+5 -5
View File
@@ -23,7 +23,7 @@ if ($serverID === UniqueConst::$serverID) {
increaseRefresh("연감", 1);
}
$admin = $gameStor->getValues(['startyear', 'year', 'month', 'map_theme']);
$admin = $gameStor->getValues(['startyear', 'year', 'month']);
$me = $db->queryFirstRow('SELECT con, turntime FROM general WHERE owner = %i', $userID);
@@ -47,9 +47,9 @@ if ($s_year === null) {
$e = Util::joinYearMonth($e_year, $e_month);
if ($serverID !== UniqueConst::$serverID) {
$mapTheme = $db->queryFirstField('SELECT map FROM ng_games WHERE server_id=%s', $serverID) ?: 'che';
$mapName = $db->queryFirstField('SELECT map FROM ng_games WHERE server_id=%s', $serverID) ?: 'che';
} else {
$mapTheme = $admin['map_theme'] ?? 'che';
$mapName = GameConst::$mapName;
}
//FIXME: $yearmonth가 올바르지 않을 경우에 처리가 필요.
@@ -104,7 +104,7 @@ $nations = $history['nations'];
<meta name="viewport" content="width=1024" />
<title><?= UniqueConst::$serverName ?>: 연감</title>
<?= WebUtil::printJS('../d_shared/common_path.js') ?>
<?= WebUtil::printJS("js/map/theme_{$mapTheme}.js") ?>
<?= WebUtil::printJS("js/map/theme_{$mapName}.js") ?>
<?= WebUtil::printCSS('../d_shared/common.css') ?>
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard/dist/web/static/pretendard.css" />
<?= WebUtil::printCSS('css/map.css') ?>
@@ -155,7 +155,7 @@ $nations = $history['nations'];
<tbody>
<tr height=520>
<td width=698>
<?= getMapHtml($mapTheme) ?>
<?= getMapHtml($mapName) ?>
</td>
<td id='nation_list_frame'>
<table id='nation_list'>
+4 -4
View File
@@ -13,8 +13,6 @@ $gameStor = KVStorage::getStorage($db, 'game_env');
increaseRefresh("중원정보", 1);
$mapTheme = $gameStor->map_theme ?? 'che';
$me = $db->queryFirstRow('SELECT no,nation FROM general WHERE owner=%i', $userID);
$myNationID = $me['nation'];
@@ -100,7 +98,9 @@ $neutralStateCharMap = [
<?= WebUtil::printStaticValues([
'staticValues' => [
'serverNick' => DB::prefix(),
'serverID' => UniqueConst::$serverID
'serverID' => UniqueConst::$serverID,
'unitSet' => GameConst::$unitSet,
'mapName' => GameConst::$mapName,
],
]) ?>
<?= WebUtil::printDist('ts', ['common', 'map']) ?>
@@ -187,7 +187,7 @@ $neutralStateCharMap = [
</tr>
<tr>
<td width=698 height=420>
<?= getMapHtml($mapTheme) ?>
<?= getMapHtml() ?>
</td>
<td id='nation_list_frame'>
<table id='nation_list'>
+5 -10
View File
@@ -235,24 +235,19 @@ function formatLeadershipBonus(int $value): string
function getMapTheme(): string
{
$db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env');
$mapTheme = $gameStor->map_theme ?? 'che';
return $mapTheme;
return GameConst::$mapName;
}
function getMapHtml(?string $mapTheme = null)
function getMapHtml(?string $mapName = null)
{
$templates = new \League\Plates\Engine(__DIR__ . '/templates');
if ($mapTheme === null) {
$db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env');
$mapTheme = $gameStor->map_theme ?? 'che';
if($mapName === null){
$mapName = GameConst::$mapName;
}
return $templates->render('map', [
'mapTheme' => $mapTheme
'mapName' => $mapName
]);
}
+1 -2
View File
@@ -83,7 +83,6 @@ if ($gameStor->npcmode == 0) {
$npcmode = "선택 생성";
}
$color = "cyan";
$mapTheme = $gameStor->map_theme;
$serverName = UniqueConst::$serverName;
$serverCnt = $gameStor->server_cnt;
@@ -257,7 +256,7 @@ if (!$otherTextInfo) {
</div>
<?php endif; ?>
<div id="map_view" class="gx-0">
<div id="mapZone" class="view-item"><?= getMapHtml($mapTheme) ?></div>
<div id="mapZone" class="view-item"><?= getMapHtml() ?></div>
<div class="view-item" id="reservedCommandZone">
<div id="reservedCommandList"></div>
<div id="actionMiniPlate" class="gx-0 row">
+9 -10
View File
@@ -31,15 +31,15 @@ $now = time();
if($mapInfo && ($now - $mapInfo['timestamp'] < 600)){
$mapEtag = $mapInfo['etag'];
$mapModified = $mapInfo['timestamp'];
header("Last-Modified: ".gmdate("D, d M Y H:i:s", $mapModified)." GMT");
header("Etag: $mapEtag");
header("Last-Modified: ".gmdate("D, d M Y H:i:s", $mapModified)." GMT");
header("Etag: $mapEtag");
if (
strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']??'2000-01-01') === $mapModified ||
strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']??'2000-01-01') === $mapModified ||
trim($_SERVER['HTTP_IF_NONE_MATCH']??'') === $mapEtag
) {
header("HTTP/1.1 304 Not Modified");
) {
header("HTTP/1.1 304 Not Modified");
die();
}
@@ -65,10 +65,9 @@ $rawMap = getWorldMap([
$db = DB::db();
$gameStor = KVStorage::getStorage($db, 'game_env');
$mapTheme = $gameStor->map_theme ?? 'che';
$rawMap['history'] = $history;
$rawMap['theme'] = $mapTheme;
$rawMap['theme'] = GameConst::$mapName;
$etag = hash('sha256', $serverID.$now);
$map = [
@@ -77,7 +76,7 @@ $map = [
'data'=>$rawMap,
];
$cache->save("recent_map", $map);
header("Last-Modified: ".gmdate("D, d M Y H:i:s", $now)." GMT");
header("Etag: $etag");
header("Last-Modified: ".gmdate("D, d M Y H:i:s", $now)." GMT");
header("Etag: $etag");
Json::die($map['data'], 0);
-1
View File
@@ -173,7 +173,6 @@ class che_강행 extends Command\GeneralCommand
public function exportJSVars(): array
{
return [
'mapTheme' => \sammo\getMapTheme(),
'procRes' => [
'cities' => \sammo\JSOptionsForCities(),
'distanceList' => \sammo\JSCitiesBasedOnDistance($this->generalObj->getCityID(), 3),
-1
View File
@@ -181,7 +181,6 @@ class che_이동 extends Command\GeneralCommand
public function exportJSVars(): array
{
return [
'mapTheme' => \sammo\getMapTheme(),
'procRes' => [
'cities' => \sammo\JSOptionsForCities(),
'distanceList' => \sammo\JSCitiesBasedOnDistance($this->generalObj->getCityID(), 1),
-1
View File
@@ -225,7 +225,6 @@ class che_임관 extends Command\GeneralCommand
$nationList[] = $nationTarget;
}
return [
'mapTheme' => \sammo\getMapTheme(),
'procRes' => [
'nationList' => $nationList,
'startYear' => $this->env['startyear'],
-1
View File
@@ -228,7 +228,6 @@ class che_첩보 extends Command\GeneralCommand
public function exportJSVars(): array
{
return [
'mapTheme' => \sammo\getMapTheme(),
'procRes' => [
'cities' => \sammo\JSOptionsForCities(),
'distanceList' => \sammo\JSCitiesBasedOnDistance($this->generalObj->getCityID(), 3),
-1
View File
@@ -244,7 +244,6 @@ class che_출병 extends Command\GeneralCommand
public function exportJSVars(): array
{
return [
'mapTheme' => \sammo\getMapTheme(),
'procRes' => [
'cities' => \sammo\JSOptionsForCities(),
'distanceList' => \sammo\JSCitiesBasedOnDistance($this->generalObj->getCityID(), 1),
-1
View File
@@ -342,7 +342,6 @@ class che_화계 extends Command\GeneralCommand
public function exportJSVars(): array
{
return [
'mapTheme' => \sammo\getMapTheme(),
'procRes' => [
'cities' => \sammo\JSOptionsForCities(),
'distanceList' => \sammo\JSCitiesBasedOnDistance($this->generalObj->getCityID(), 3),
-1
View File
@@ -221,7 +221,6 @@ class che_급습 extends Command\NationCommand
$nationList[] = $nationTarget;
}
return [
'mapTheme' => \sammo\getMapTheme(),
'procRes' => [
'nationList' => $nationList,
'startYear' => $this->env['startyear'],
@@ -291,7 +291,6 @@ class che_물자원조 extends Command\NationCommand
}
return [
'mapTheme' => \sammo\getMapTheme(),
'procRes' => [
'nationList' => $nationList,
'currentNationLevel' => $currentNationLevel,
-1
View File
@@ -177,7 +177,6 @@ class che_발령 extends Command\NationCommand
$troops = Util::convertArrayToDict($db->query('SELECT * FROM troop WHERE nation=%i', $nationID), 'troop_leader');
$destRawGenerals = $db->queryAllLists('SELECT no,name,officer_level,npc,gold,rice,leadership,strength,intel,city,crew,train,atmos,troop FROM general WHERE nation = %i ORDER BY npc,binary(name)', $nationID);
return [
'mapTheme' => \sammo\getMapTheme(),
'procRes' => [
'distanceList' => \sammo\JSCitiesBasedOnDistance($this->generalObj->getCityID(), 1),
'cities' => \sammo\JSOptionsForCities(),
@@ -172,7 +172,6 @@ class che_백성동원 extends Command\NationCommand
public function exportJSVars(): array
{
return [
'mapTheme' => \sammo\getMapTheme(),
'procRes' => [
'cities' => \sammo\JSOptionsForCities(),
'distanceList' => new \stdClass(),
@@ -255,7 +255,6 @@ class che_불가침제의 extends Command\NationCommand
$nationList[] = $nationTarget;
}
return [
'mapTheme' => \sammo\getMapTheme(),
'procRes' => [
'nationList' => $nationList,
'startYear' => $this->env['startyear'],
@@ -200,7 +200,6 @@ class che_불가침파기제의 extends Command\NationCommand{
$nationList[] = $nationTarget;
}
return [
'mapTheme' => \sammo\getMapTheme(),
'procRes' => [
'nationList' => $nationList,
'startYear' => $this->env['startyear'],
@@ -219,7 +219,6 @@ class che_선전포고 extends Command\NationCommand
$nationList[] = $nationTarget;
}
return [
'mapTheme' => \sammo\getMapTheme(),
'procRes' => [
'nationList' => $nationList,
'startYear' => $this->env['startyear'],
-1
View File
@@ -199,7 +199,6 @@ class che_수몰 extends Command\NationCommand
public function exportJSVars(): array
{
return [
'mapTheme' => \sammo\getMapTheme(),
'procRes' => [
'cities' => \sammo\JSOptionsForCities(),
'distanceList' => new \stdClass(),
@@ -224,7 +224,6 @@ class che_이호경식 extends Command\NationCommand
$nationList[] = $nationTarget;
}
return [
'mapTheme' => \sammo\getMapTheme(),
'procRes' => [
'nationList' => $nationList,
'startYear' => $this->env['startyear'],
@@ -193,7 +193,6 @@ class che_종전제의 extends Command\NationCommand{
$nationList[] = $nationTarget;
}
return [
'mapTheme' => \sammo\getMapTheme(),
'procRes' => [
'nationList' => $nationList,
'startYear' => $this->env['startyear'],
-1
View File
@@ -233,7 +233,6 @@ class che_천도 extends Command\NationCommand
public function exportJSVars(): array
{
return [
'mapTheme' => \sammo\getMapTheme(),
'procRes' => [
'cities' => \sammo\JSOptionsForCities(),
'distanceList' => new \stdClass(),
@@ -199,7 +199,6 @@ class che_초토화 extends Command\NationCommand{
public function exportJSVars(): array
{
return [
'mapTheme' => \sammo\getMapTheme(),
'procRes' => [
'cities' => \sammo\JSOptionsForCities(),
'distanceList' => new \stdClass(),
@@ -280,7 +280,6 @@ class che_피장파장 extends Command\NationCommand
}
return [
'mapTheme' => \sammo\getMapTheme(),
'procRes' => [
'nationList' => $nationList,
'startYear' => $this->env['startyear'],
-1
View File
@@ -202,7 +202,6 @@ class che_허보 extends Command\NationCommand
public function exportJSVars(): array
{
return [
'mapTheme' => \sammo\getMapTheme(),
'procRes' => [
'cities' => \sammo\JSOptionsForCities(),
'distanceList' => new \stdClass(),
+1 -1
View File
@@ -250,7 +250,7 @@ class ResetHelper{
'month'=> $month,
'init_year'=> $year,
'init_month'=>$month,
'map_theme' => $scenarioObj->getMapTheme(),
'map_theme' => $scenarioObj->getMapTheme(), //@deprecated
'season'=>$seasonIdx,
'msg'=>'공지사항',//TODO:공지사항
'maxgeneral'=>GameConst::$defaultMaxGeneral,
+1 -1
View File
@@ -1,4 +1,4 @@
<div class="world_map map_theme_<?= $mapTheme ?> draw_required">
<div class="world_map map_theme_<?= $mapName ?> draw_required">
<div class="map_title obj_tooltip" data-bs-toggle="tooltip" data-bs-placement="top" data-tooltip-class="map_title_tooltiptext">
<span class="map_title_text ">
</span>
+12 -11
View File
@@ -1,8 +1,8 @@
<template>
<div
v-for="(officer, _idx) in [chiefList[chiefLevel]]"
:key="_idx"
:class="[`chiefBox${chiefLevel}`, 'subRows']"
v-for="(officer, idx) in [chiefList[chiefLevel]]"
:key="idx"
:style="style"
@click="$emit('click', this)"
>
@@ -13,21 +13,22 @@
textDecoration: isMe ? 'underline' : undefined,
}"
>
{{ officer?(officer?.name ?? "-"):'' }}
{{ officer ? officer?.name ?? "-" : "" }}
</div>
<div class="bg1 center row gx-0">
<div class="col">{{ officer?.officerLevelText }}</div>
<div class="col">{{ officer?((officer?.turnTime ?? " - ").slice(-5)):'' }}</div>
<div class="col">
{{ officer?.officerLevelText }}
</div>
<div class="col">
{{ officer ? (officer?.turnTime ?? " - ").slice(-5) : "" }}
</div>
</div>
<div
class="tableCell align-self-center turn_pad"
v-for="(turn, idx) in officer?.turn??[]"
v-for="(turn, idx) in officer?.turn ?? []"
:key="idx"
class="tableCell align-self-center turn_pad"
:style="{
fontSize:
mb_strwidth(turn.brief) > 28
? `${28 / mb_strwidth(turn.brief)}em`
: undefined,
fontSize: mb_strwidth(turn.brief) > 28 ? `${28 / mb_strwidth(turn.brief)}em` : undefined,
}"
>
{{ turn.brief }}
+33 -25
View File
@@ -1,30 +1,39 @@
<template>
<div style="position:relative">
<div style="position: relative">
<DragSelect
v-slot="{ selected }"
:class="['subRows', 'chiefCommand']"
:style="style"
:disabled="!props.officer || !isEditMode"
attribute="turnIdx"
@dragStart="dragStart()"
@dragDone="dragDone(...$event)"
v-slot="{ selected }"
>
<div class="bg1 center row gx-0" style="font-size: 1.2em">
<div
class="col-5 align-self-center text-end"
>{{ officer ? `${officer.officerLevelText} : ` : "" }}</div>
<div class="col-5 align-self-center text-end">
{{ officer ? `${officer.officerLevelText} : ` : "" }}
</div>
<div
class="col-7 align-self-center"
:style="{
color: getNpcColor(officer?.npcType ?? 0),
}"
>{{ officer?.name }}</div>
>
{{ officer?.name }}
</div>
</div>
<div :turnIdx="vidx" class="row c-bg2 gx-0" v-for="vidx in maxTurn" :key="vidx">
<div v-for="vidx in maxTurn" :key="vidx" :turnIdx="vidx" class="row c-bg2 gx-0">
<div
:class="['col-2', 'time_pad', 'f_tnum', ((isDragToggle || isCopyButtonShown) && selected.has(vidx.toString())) ? 'inverted' : undefined]"
>{{ turnTimes[vidx - 1] }}</div>
<div class="center" v-if="!officer || (!officer.turn) || !(vidx - 1 in officer.turn)"></div>
:class="[
'col-2',
'time_pad',
'f_tnum',
(isDragToggle || isCopyButtonShown) && selected.has(vidx.toString()) ? 'inverted' : undefined,
]"
>
{{ turnTimes[vidx - 1] }}
</div>
<div v-if="!officer || !officer.turn || !(vidx - 1 in officer.turn)" class="center" />
<div
v-else
class="tableCell align-self-center col-10 center turn_pad"
@@ -34,7 +43,9 @@
? `${28 / mb_strwidth(officer.turn[vidx - 1].brief)}em`
: undefined,
}"
>{{ officer.turn[vidx - 1].brief }}</div>
>
{{ officer.turn[vidx - 1].brief }}
</div>
</div>
</DragSelect>
<BButton
@@ -47,7 +58,9 @@
}"
@blur="isCopyButtonShown = false"
@click="tryCopy()"
>복사하기</BButton>
>
복사하기
</BButton>
</div>
</template>
<script setup lang="ts">
@@ -65,21 +78,21 @@ import DragSelect from "@/components/DragSelect.vue";
import { BButton } from "bootstrap-vue-3";
import { QueryActionHelper } from "@/util/QueryActionHelper";
const props = defineProps({
style: VueTypes.object.isRequired,
officer: {
type: Object as PropType<ChiefResponse["chiefList"][0]>,
default: undefined,
},
turnTerm: VueTypes.integer.isRequired,
maxTurn: VueTypes.integer.isRequired,
})
});
const btnPos = ref(0);
const btnCopy = ref<InstanceType<typeof BButton> | null>(null);
const storedActionsHelper = inject<StoredActionsHelper>('storedNationActionsHelper');
const isEditMode = (storedActionsHelper?.isEditMode) ?? ref(false);
const storedActionsHelper = inject<StoredActionsHelper>("storedNationActionsHelper");
const isEditMode = storedActionsHelper?.isEditMode ?? ref(false);
const isDragToggle = ref(false);
const isCopyButtonShown = ref(false);
@@ -91,16 +104,15 @@ onMounted(() => {
if (props.officer === undefined) {
return;
}
queryActionHelper.reservedCommandList.value = props.officer.turn.map(rawTurn => {
queryActionHelper.reservedCommandList.value = props.officer.turn.map((rawTurn) => {
return {
...rawTurn,
time: '',
}
time: "",
};
});
console.log(queryActionHelper.reservedCommandList.value);
});
function dragStart() {
isDragToggle.value = true;
}
@@ -139,7 +151,6 @@ function dragDone(...rawSelectedTurn: string[]) {
}
function tryCopy() {
const actions = queryActionHelper.extractQueryActions();
isCopyButtonShown.value = false;
@@ -160,10 +171,7 @@ if (!props.officer || !props.officer.turnTime) {
const baseTurnTime = parseTime(props.officer.turnTime);
for (const idx of range(props.officer.turn.length)) {
turnTimes.value.push(
formatTime(
addMinutes(baseTurnTime, idx * props.turnTerm),
props.turnTerm >= 5 ? "HH:mm" : "mm:ss"
)
formatTime(addMinutes(baseTurnTime, idx * props.turnTerm), props.turnTerm >= 5 ? "HH:mm" : "mm:ss")
);
}
}
+50 -56
View File
@@ -7,29 +7,23 @@
<div class="row gx-0">
<div class="col-2 col-md-1 articleTitle bg1 center">제목</div>
<div class="col-10 col-md-11">
<input
class="titleInput"
type="text"
maxlength="250"
placeholder="제목"
v-model="newArticle.title"
/>
<input v-model="newArticle.title" class="titleInput" type="text" maxlength="250" placeholder="제목" />
</div>
</div>
<div class="row gx-0">
<div class="col-2 col-md-1 bg1 center">내용</div>
<div class="col-10 col-md-11">
<textarea
class="contentInput autosize"
ref="newArticleTextForm"
placeholder="내용"
v-model="newArticle.text"
class="contentInput autosize"
placeholder="내용"
@input="autoResizeTextarea"
/>
</div>
</div>
<div class="row">
<div class="col-8 col-md-10"></div>
<div class="col-8 col-md-10" />
<div class="col-4 col-md-2 d-grid">
<b-button id="submitArticle" @click="submitArticle"> 등록 </b-button>
</div>
@@ -41,13 +35,13 @@
v-for="article in articles"
:key="article.no"
:article="article"
@submit-comment="reloadArticles"
@submitComment="reloadArticles"
/>
</template>
<template v-else> 게시물이 없습니다. </template>
</div>
<BottomBar/>
<BottomBar />
</div>
</template>
@@ -103,6 +97,49 @@ export default defineComponent({
required: true,
},
},
setup(props) {
const newArticleTextForm = ref<HTMLInputElement>();
const articles = reactive<BoardArticleItem[]>([]);
const reloadArticles = async () => {
let boardResponse: BoardResponse;
try {
const response = await axios({
url: "j_board_get_articles.php",
responseType: "json",
method: "post",
data: convertFormData({
isSecret: props.isSecretBoard,
}),
});
const result: InvalidResponse | BoardResponse = response.data;
if (!result.result) {
throw result.reason;
}
boardResponse = result;
} catch (e) {
console.error(e);
alert(`에러: ${e}`);
return;
}
articles.length = 0;
articles.push(...Object.values(boardResponse.articles));
articles.reverse();
};
onMounted(async () => {
await reloadArticles();
});
return {
newArticleTextForm,
articles,
reloadArticles,
};
},
data() {
return {
title: this.isSecretBoard ? "기밀실" : "회의실",
@@ -150,48 +187,5 @@ export default defineComponent({
await this.reloadArticles();
},
},
setup(props) {
const newArticleTextForm = ref<HTMLInputElement>();
const articles = reactive<BoardArticleItem[]>([]);
const reloadArticles = async () => {
let boardResponse: BoardResponse;
try {
const response = await axios({
url: "j_board_get_articles.php",
responseType: "json",
method: "post",
data: convertFormData({
isSecret: props.isSecretBoard,
}),
});
const result: InvalidResponse | BoardResponse = response.data;
if (!result.result) {
throw result.reason;
}
boardResponse = result;
} catch (e) {
console.error(e);
alert(`에러: ${e}`);
return;
}
articles.length = 0;
articles.push(...Object.values(boardResponse.articles));
articles.reverse();
};
onMounted(async () => {
await reloadArticles();
});
return {
newArticleTextForm,
articles,
reloadArticles,
};
},
});
</script>
</script>
+36 -57
View File
@@ -2,26 +2,14 @@
<div id="container" class="pageChiefCenter">
<TopBackBar title="사령부" reloadable @reload="reloadTable" />
<div
id="mainTable"
v-if="chiefList !== undefined"
:class="`${targetIsMe ? 'targetIsMe' : 'targetIsNotMe'}`"
>
<div v-if="chiefList !== undefined" id="mainTable" :class="`${targetIsMe ? 'targetIsMe' : 'targetIsNotMe'}`">
<template v-for="(chiefLevel, vidx) in [12, 10, 8, 6, 11, 9, 7, 5]" :key="chiefLevel">
<div
v-if="vidx % 4 == 0"
:class="[
'turnIdx',
vidx == 0 && !targetIsMe ? undefined : 'only1000px',
]"
>
<div v-if="vidx % 4 == 0" :class="['turnIdx', vidx == 0 && !targetIsMe ? undefined : 'only1000px']">
<div :class="['subRows', 'bg0']" :style="mainTableGridRows">
<div class="bg1">&nbsp;</div>
<div
v-for="idx in maxChiefTurn"
:class="[`turnIdxLeft`, 'align-self-center', 'center']"
:key="idx"
>{{ idx }}</div>
<div v-for="idx in maxChiefTurn" :key="idx" :class="[`turnIdxLeft`, 'align-self-center', 'center']">
{{ idx }}
</div>
</div>
</div>
<div
@@ -49,35 +37,29 @@
:maxTurn="maxChiefTurn"
:maxPushTurn="Math.floor(maxChiefTurn / 2)"
:date="date"
@raiseReload="reloadTable()"
:officer="officer"
@raiseReload="reloadTable()"
/>
</div>
<div
v-if="vidx % 4 == 3"
:class="[
'turnIdx',
vidx == 7 && !targetIsMe ? undefined : 'only1000px',
]"
>
<div v-if="vidx % 4 == 3" :class="['turnIdx', vidx == 7 && !targetIsMe ? undefined : 'only1000px']">
<div :class="['subRows', 'bg0']" :style="mainTableGridRows">
<div class="bg1">&nbsp;</div>
<div
v-for="idx in maxChiefTurn"
:class="[`turnIdxRight`, 'align-self-center', 'center']"
:key="idx"
>{{ idx }}</div>
<div v-for="idx in maxChiefTurn" :key="idx" :class="[`turnIdxRight`, 'align-self-center', 'center']">
{{ idx }}
</div>
</div>
</div>
</template>
</div>
</div>
<div id="bottomChiefBox" v-if="chiefList">
<div v-if="chiefList" id="bottomChiefBox">
<div id="bottomChiefList" class="c-bg2">
<template v-for="(chiefLevel, vidx) in [12, 10, 8, 6, 11, 9, 7, 5]" :key="chiefLevel">
<div class="turnIdx subRows bg0" :style="subTableGridRows" v-if="vidx % 4 == 0">
<div class="bg1" style="grid-row: 1/3"></div>
<div v-for="idx in maxChiefTurn" :class="[`turnIdxLeft`]" :key="idx">{{ idx }}</div>
<div v-if="vidx % 4 == 0" class="turnIdx subRows bg0" :style="subTableGridRows">
<div class="bg1" style="grid-row: 1/3" />
<div v-for="idx in maxChiefTurn" :key="idx" :class="[`turnIdxLeft`]">
{{ idx }}
</div>
</div>
<BottomItem
:chiefLevel="chiefLevel"
@@ -86,9 +68,11 @@
:isMe="chiefLevel == officerLevel"
@click="viewTarget = chiefLevel"
/>
<div class="turnIdx subRows bg0" :style="subTableGridRows" v-if="vidx % 4 == 3">
<div class="bg1" style="grid-row: 1/3"></div>
<div v-for="idx in maxChiefTurn" :class="`turnIdxRight`" :key="idx">{{ idx }}</div>
<div v-if="vidx % 4 == 3" class="turnIdx subRows bg0" :style="subTableGridRows">
<div class="bg1" style="grid-row: 1/3" />
<div v-for="idx in maxChiefTurn" :key="idx" :class="`turnIdxRight`">
{{ idx }}
</div>
</div>
</template>
</div>
@@ -99,10 +83,10 @@
</template>
<script lang="ts">
declare const staticValues: {
serverNick: string,
mapName: string,
unitSet: string,
}
serverNick: string;
mapName: string;
unitSet: string;
};
</script>
<script lang="ts" setup>
import "@scss/common/bootstrap5.scss";
@@ -125,7 +109,7 @@ import { StoredActionsHelper } from "./util/StoredActionsHelper";
const props = defineProps({
maxChiefTurn: VueTypes.number.isRequired,
})
});
const tableObj = reactive<Omit<OptionalFull<ChiefResponse>, "result">>({
lastExecute: undefined,
@@ -142,15 +126,7 @@ const tableObj = reactive<Omit<OptionalFull<ChiefResponse>, "result">>({
unitSet: undefined,
});
const {
year,
month,
turnTerm,
date,
chiefList,
officerLevel,
commandList,
} = toRefs(tableObj);
const { year, month, turnTerm, date, chiefList, officerLevel, commandList } = toRefs(tableObj);
const viewTarget = ref<number | undefined>();
@@ -169,8 +145,7 @@ watch(viewTarget, (val) => {
async function reloadTable(): Promise<void> {
try {
const response =
await SammoAPI.NationCommand.GetReservedCommand<ChiefResponse>();
const response = await SammoAPI.NationCommand.GetReservedCommand<ChiefResponse>();
console.log(response);
for (const [key, value] of entriesWithType(response)) {
if (key === "result") {
@@ -220,10 +195,14 @@ const subTableGridRows = computed(() => {
void reloadTable();
const storedActionsHelper = new StoredActionsHelper(staticValues.serverNick, 'nation', staticValues.mapName, staticValues.unitSet);
provide('storedNationActionsHelper', storedActionsHelper);
const storedActionsHelper = new StoredActionsHelper(
staticValues.serverNick,
"nation",
staticValues.mapName,
staticValues.unitSet
);
provide("storedNationActionsHelper", storedActionsHelper);
</script>
<style lang="scss">
@import "@scss/chiefCenter.scss";
</style>
</style>
+53 -131
View File
@@ -1,40 +1,31 @@
<template>
<top-back-bar :title="title" />
<TopBackBar :title="title" />
<div
id="container"
class="bg0 px-2"
style="
max-width: 1000px;
margin: auto;
border: solid 1px #888888;
overflow: hidden;
"
style="max-width: 1000px; margin: auto; border: solid 1px #888888; overflow: hidden"
>
<div id="inheritance_list" class="row">
<template v-for="(text, key) in inheritanceViewText" :key="key">
<div
:id="`inherit_${key}`"
class="col col-sm-4 col-12 inherit_item inherit_template_item"
>
<div :id="`inherit_${key}`" class="col col-sm-4 col-12 inherit_item inherit_template_item">
<div class="row">
<label
:id="`inherit_${key}_head`"
class="inherit_head col col-md-6 col-sm-7 col-6 col-form-label"
>{{ text.title }}</label
>
<label :id="`inherit_${key}_head`" class="inherit_head col col-md-6 col-sm-7 col-6 col-form-label">{{
text.title
}}</label>
<div class="col col-md-6 col-sm-5 col-6">
<input
:id="`inherit_${key}_value`"
type="text"
class="form-control inherit_value f_tnum"
readonly
:id="`inherit_${key}_value`"
:value="Math.floor(items[key]).toLocaleString()"
/>
</div>
</div>
<div style="text-align: right">
<small class="form-text text-muted" v-html="text.info"></small>
<!-- eslint-disable-next-line vue/no-v-html -->
<small class="form-text text-muted" v-html="text.info" />
</div>
</div>
<div v-if="key == 'new'" style="width: 100%; padding: 0 10px">
@@ -44,22 +35,18 @@
</div>
<div id="inheritance_store">
<div class="row">
<div class="col"><div class="bg1 a-center">유산 포인트 상점</div></div>
<div class="col">
<div class="bg1 a-center">유산 포인트 상점</div>
</div>
</div>
<div class="row">
<div class="col offset-md-4 col-md-4 col-sm-6 col-12 py-2">
<div class="row px-4">
<div class="a-right col-6 align-self-center">
다음 전투 특기 선택
</div>
<div class="a-right col-6 align-self-center">다음 전투 특기 선택</div>
<div class="col-6">
<select class="form-select col-6" v-model="nextSpecialWar">
<option
v-for="(info, key) in availableSpecialWar"
:key="key"
:value="key"
>
<select v-model="nextSpecialWar" class="form-select col-6">
<option v-for="(info, key) in availableSpecialWar" :key="key" :value="key">
{{ info.title }}
</option>
</select>
@@ -67,34 +54,23 @@
</div>
<div class="a-right">
<small class="form-text text-muted"
><span
style="color: white"
v-html="availableSpecialWar[nextSpecialWar].info"
/><br />다음에 얻을 전투 특기를 정합니다.<br /><span
style="color: white"
><!-- eslint-disable-next-line vue/no-v-html -->
<span style="color: white" v-html="availableSpecialWar[nextSpecialWar].info" /><br />다음에 얻을 전투
특기를 정합니다.<br /><span style="color: white"
>필요 포인트: {{ inheritActionCost.nextSpecial }}</span
></small
>
</div>
<div class="row px-4">
<b-button
class="col-6 offset-6"
variant="primary"
@click="setNextSpecialWar"
>구입</b-button
>
<b-button class="col-6 offset-6" variant="primary" @click="setNextSpecialWar"> 구입 </b-button>
</div>
</div>
<div class="col col-md-4 col-sm-6 col-12 py-2">
<div class="row px-4">
<div class="a-right col-6 align-self-center">유니크 입찰</div>
<div class="col-6">
<select class="form-select col-6" v-model="specificUnique">
<option
v-for="(info, key) in availableUnique"
:key="key"
:value="key"
>
<select v-model="specificUnique" class="form-select col-6">
<option v-for="(info, key) in availableUnique" :key="key" :value="key">
{{ info.title }}
</option>
</select>
@@ -103,29 +79,23 @@
<div class="row px-4">
<div class="col f_tnum">
<NumberInputWithInfo
v-model="specificUniqueAmount"
title="입찰 포인트"
:min="inheritActionCost.minSpecificUnique"
:max="items.previous"
v-model="specificUniqueAmount"
/>
</div>
</div>
<div class="a-right">
<small class="form-text text-muted"
>얻고자 하는 유니크 아이템을 포인트를 걸어 입찰합니다. 최고
포인트인 경우 다음 턴에 유니크를 얻습니다.<br /><span
style="color: white"
v-html="availableUnique[specificUnique].info"
/></small>
>얻고자 하는 유니크 아이템을 포인트를 걸어 입찰합니다. 최고 포인트인 경우 다음 턴에 유니크를 얻습니다.<br />
<!-- eslint-disable-next-line vue/no-v-html -->
<span style="color: white" v-html="availableUnique[specificUnique].info" />
</small>
</div>
<div class="row px-4">
<b-button
class="col-6 offset-6"
variant="primary"
@click="buySpecificUnique"
>구입</b-button
>
<b-button class="col-6 offset-6" variant="primary" @click="buySpecificUnique"> 구입 </b-button>
</div>
</div>
</div>
@@ -136,12 +106,7 @@
<div class="col col-md-4 col-sm-6 col-12 py-2">
<div class="row px-4">
<div class="a-right col-6 align-self-center">랜덤 초기화</div>
<b-button
class="col-6"
variant="primary"
@click="buySimple('ResetTurnTime')"
>구입</b-button
>
<b-button class="col-6" variant="primary" @click="buySimple('ResetTurnTime')"> 구입 </b-button>
</div>
<div class="a-right">
<small class="form-text text-muted"
@@ -154,12 +119,7 @@
<div class="col col-md-4 col-sm-6 col-12 py-2">
<div class="row px-4">
<div class="a-right col-6 align-self-center">랜덤 유니크 획득</div>
<b-button
class="col-6"
variant="primary"
@click="buySimple('BuyRandomUnique')"
>구입</b-button
>
<b-button class="col-6" variant="primary" @click="buySimple('BuyRandomUnique')"> 구입 </b-button>
</div>
<div class="a-right">
<small class="form-text text-muted"
@@ -171,15 +131,8 @@
</div>
<div class="col col-md-4 col-sm-6 col-12 py-2">
<div class="row px-4">
<div class="a-right col-6 align-self-center">
즉시 전투 특기 초기화
</div>
<b-button
class="col-6"
variant="primary"
@click="buySimple('ResetSpecialWar')"
>구입</b-button
>
<div class="a-right col-6 align-self-center">즉시 전투 특기 초기화</div>
<b-button class="col-6" variant="primary" @click="buySimple('ResetSpecialWar')"> 구입 </b-button>
</div>
<div class="a-right">
<small class="form-text text-muted"
@@ -195,23 +148,17 @@
<hr :style="{ opacity: 0.5 }" />
</div>
<div class="row">
<div
class="col col-md-4 col-sm-6 col-12"
v-for="(info, buffKey) in inheritBuffHelpText"
:key="buffKey"
>
<div v-for="(info, buffKey) in inheritBuffHelpText" :key="buffKey" class="col col-md-4 col-sm-6 col-12">
<div class="row">
<label class="col col-sm-6 col-form-label" :for="`buff-${buffKey}`">{{
info.title
}}</label>
<label class="col col-sm-6 col-form-label" :for="`buff-${buffKey}`">{{ info.title }}</label>
<div class="col col-sm-6 f_tnum">
<b-form-input
:id="`buff-${buffKey}`"
type="number"
v-model="inheritBuff[buffKey]"
type="number"
:min="prevInheritBuff[buffKey] ?? 0"
:max="maxInheritBuff"
></b-form-input>
/>
</div>
</div>
<div style="text-align: right">
@@ -219,8 +166,7 @@
>{{ info.info }}<br /><span style="color: white"
>필요 포인트:
{{
inheritActionCost.buff[inheritBuff[buffKey]] -
inheritActionCost.buff[prevInheritBuff[buffKey] ?? 0]
inheritActionCost.buff[inheritBuff[buffKey]] - inheritActionCost.buff[prevInheritBuff[buffKey] ?? 0]
}}</span
></small
>
@@ -228,15 +174,11 @@
<div class="row px-4" style="margin-bottom: 1em">
<b-button
variant="secondary"
@click="inheritBuff[buffKey] = prevInheritBuff[buffKey] ?? 0"
class="col col-md-6 col-4 offset-md-0 offset-4"
>리셋</b-button
><b-button
variant="primary"
class="col col-md-6 col-4"
@click="buyInheritBuff(buffKey)"
>구입</b-button
@click="inheritBuff[buffKey] = prevInheritBuff[buffKey] ?? 0"
>
리셋 </b-button
><b-button variant="primary" class="col col-md-6 col-4" @click="buyInheritBuff(buffKey)"> 구입 </b-button>
</div>
</div>
</div>
@@ -245,7 +187,7 @@
<div class="bg1 a-center">유산 포인트 변경 내역(최근 30)</div>
</div>
</div>
<div class="row" v-for="(log, idx) in lastInheritPointLogs" :key="idx">
<div v-for="(log, idx) in lastInheritPointLogs" :key="idx" class="row">
<div class="col a-right" style="max-width: 20ch">
<small class="text-muted tnum">[{{ log.date }}]</small>
</div>
@@ -291,10 +233,7 @@ declare const lastInheritPointLogs: {
declare const items: Record<InheritanceType, number>;
const inheritanceViewText: Record<
InheritanceViewType,
{ title: string; info: string }
> = {
const inheritanceViewText: Record<InheritanceViewType, { title: string; info: string }> = {
sum: {
title: "총 포인트",
info: "다음 플레이에서 사용할 수 있는 총 포인트입니다.",
@@ -439,11 +378,13 @@ declare const availableUnique: Record<
export default defineComponent({
name: "PageInheritPoint",
components: {
TopBackBar,
NumberInputWithInfo,
},
data() {
const inheritBuff = {} as Record<inheritBuffType, number>;
for (const buffKey of Object.keys(
inheritBuffHelpText
) as inheritBuffType[]) {
for (const buffKey of Object.keys(inheritBuffHelpText) as inheritBuffType[]) {
inheritBuff[buffKey] = currentInheritBuff[buffKey] ?? 0;
}
return {
@@ -486,9 +427,7 @@ export default defineComponent({
alert("낮출 수 없습니다.");
return;
}
const cost =
this.inheritActionCost.buff[level] -
this.inheritActionCost.buff[prevLevel];
const cost = this.inheritActionCost.buff[level] - this.inheritActionCost.buff[prevLevel];
if (this.items.previous < cost) {
alert("유산 포인트가 부족합니다.");
return;
@@ -496,11 +435,7 @@ export default defineComponent({
const name = inheritBuffHelpText[buffKey].title;
if (
!confirm(
`${name}${level}등급으로 올릴까요? ${cost} 포인트가 소모됩니다.`
)
) {
if (!confirm(`${name}${level}등급으로 올릴까요? ${cost} 포인트가 소모됩니다.`)) {
return;
}
@@ -519,9 +454,7 @@ export default defineComponent({
//TODO: 페이지 새로고침 필요없이 하도록
location.reload();
},
async buySimple(
type: "ResetTurnTime" | "BuyRandomUnique" | "ResetSpecialWar"
) {
async buySimple(type: "ResetTurnTime" | "BuyRandomUnique" | "ResetSpecialWar") {
const costMap: Record<typeof type, number> = {
ResetTurnTime: inheritActionCost.resetTurnTime,
ResetSpecialWar: inheritActionCost.resetSpecialWar,
@@ -560,8 +493,7 @@ export default defineComponent({
location.reload();
},
async setNextSpecialWar() {
const specialWarName =
this.availableSpecialWar[this.nextSpecialWar].title ?? undefined;
const specialWarName = this.availableSpecialWar[this.nextSpecialWar].title ?? undefined;
if (specialWarName === undefined) {
alert(`잘못된 타입: ${this.nextSpecialWar}`);
return;
@@ -573,11 +505,7 @@ export default defineComponent({
return;
}
//TODO: JosaUtil
if (
!confirm(
`${cost} 포인트로 다음 전특을 ${specialWarName}(으)로 고정하겠습니까?`
)
) {
if (!confirm(`${cost} 포인트로 다음 전특을 ${specialWarName}(으)로 고정하겠습니까?`)) {
return;
}
@@ -596,8 +524,7 @@ export default defineComponent({
location.reload();
},
async buySpecificUnique() {
const uniqueName =
this.availableUnique[this.specificUnique].title ?? undefined;
const uniqueName = this.availableUnique[this.specificUnique].title ?? undefined;
if (uniqueName === undefined) {
alert(`잘못된 타입: ${this.specificUnique}`);
return;
@@ -629,14 +556,9 @@ export default defineComponent({
location.reload();
},
},
components: {
TopBackBar,
NumberInputWithInfo,
},
});
</script>
<style>
.col-form-label {
text-align: right;
@@ -650,4 +572,4 @@ export default defineComponent({
.tnum {
font-feature-settings: "tnum";
}
</style>
</style>
+93 -143
View File
@@ -1,5 +1,5 @@
<template>
<top-back-bar title="장수 생성" />
<TopBackBar title="장수 생성" />
<div id="container" class="bg0">
<div class="nation-list">
@@ -8,20 +8,24 @@
<div>임관권유문</div>
<div class="display-toggle d-grid">
<b-button
v-model="displayTable"
:pressed="displayTable"
:variant="displayTable ? 'info' : 'secondary'"
v-model="displayTable"
@click="displayTable = !displayTable"
>{{ displayTable ? "숨기기" : "보이기" }}</b-button>
>
{{ displayTable ? "숨기기" : "보이기" }}
</b-button>
</div>
<div class="zoom-toggle d-grid">
<b-button
v-model="toggleZoom"
:pressed="toggleZoom"
:variant="toggleZoom ? 'info' : 'secondary'"
v-model="toggleZoom"
@click="toggleZoom = !toggleZoom"
:disabled="!displayTable"
>{{ toggleZoom ? "작게 보기" : "크게 보기" }}</b-button>
@click="toggleZoom = !toggleZoom"
>
{{ toggleZoom ? "작게 보기" : "크게 보기" }}
</b-button>
</div>
</div>
<template v-if="displayTable">
@@ -38,9 +42,12 @@
}"
class="d-grid"
>
<div class="align-self-center center">{{ nation.name }}</div>
<div class="align-self-center center">
{{ nation.name }}
</div>
</div>
<div class="nation-scout-plate align-self-center">
<!-- eslint-disable-next-line vue/no-v-html -->
<div class="nation-scout-msg" v-html="nation.scoutmsg ?? '-'" />
</div>
</div>
@@ -60,32 +67,22 @@
<div class="col col-md-1 col-3 a-right align-self-center">전콘 사용</div>
<div class="col col-md-4 col-9 align-self-center">
<img style="height: 64px; width: 64px" :src="iconPath" />
<label>
<input type="checkbox" v-model="args.pic" /> 사용
</label>
<label> <input v-model="args.pic" type="checkbox" /> 사용 </label>
</div>
<div class="col col-md-4 col-3 align-self-center a-right">성격</div>
<div class="col col-md-8 col-9 align-self-center">
<div class="row">
<div class="col col-md-3 col-4 align-self-center">
<select
class="form-select form-inline"
style="max-width: 20ch"
v-model="args.character"
>
<option
v-for="(personalityObj, key) in availablePersonality"
:key="key"
:value="key"
>{{ personalityObj.name }}</option>
<select v-model="args.character" class="form-select form-inline" style="max-width: 20ch">
<option v-for="(personalityObj, key) in availablePersonality" :key="key" :value="key">
{{ personalityObj.name }}
</option>
</select>
</div>
<div class="col col-md-9 col-8 align-self-center">
<small class="text-muted">
{{
availablePersonality[args.character].info
}}
{{ availablePersonality[args.character].info }}
</small>
</div>
</div>
@@ -103,37 +100,35 @@
<small class="text-muted">//</small>
</div>
<div class="col col-md-2 col-3 align-self-center">
<input type="number" class="form-control" v-model="args.leadership" />
<input v-model="args.leadership" type="number" class="form-control" />
</div>
<div class="col col-md-2 col-3 align-self-center">
<input type="number" class="form-control" v-model="args.strength" />
<input v-model="args.strength" type="number" class="form-control" />
</div>
<div class="col col-md-2 col-3 align-self-center">
<input type="number" class="form-control" v-model="args.intel" />
<input v-model="args.intel" type="number" class="form-control" />
</div>
</div>
<div class="row" style="margin-top: 1em">
<div class="col col-md-4 col-3 a-right align-self-center">능력치 조절</div>
<div class="col col-md-8 col-9">
<b-button variant="secondary" class="stat-btn" @click="randStatRandom">랜덤형</b-button>
<b-button variant="secondary" class="stat-btn" @click="randStatLeadPow">통솔무력형</b-button>
<b-button variant="secondary" class="stat-btn" @click="randStatLeadInt">통솔지력형</b-button>
<b-button variant="secondary" class="stat-btn" @click="randStatPowInt">무력지력형</b-button>
<b-button variant="secondary" class="stat-btn" @click="randStatRandom"> 랜덤형 </b-button>
<b-button variant="secondary" class="stat-btn" @click="randStatLeadPow"> 통솔무력형 </b-button>
<b-button variant="secondary" class="stat-btn" @click="randStatLeadInt"> 통솔지력형 </b-button>
<b-button variant="secondary" class="stat-btn" @click="randStatPowInt"> 무력지력형 </b-button>
</div>
</div>
</div>
<div class="row" style="border-top: solid 1px #aaa; margin-top: 0.5em">
<div class="col a-center" style="color: orange">
모든 능력치는 ( {{ stats.min }} &lt;= 능력치 &lt;= {{ stats.max }} )
사이로 잡으셔야 니다.
<br /> 외의 능력치는 가입되지 않습니다.
모든 능력치는 ( {{ stats.min }} &lt;= 능력치 &lt;= {{ stats.max }} ) 사이로 잡으셔야 합니다. <br /> 외의
능력치는 가입되지 않습니다.
</div>
</div>
<div class="row">
<div class="col a-center">
능력치의 총합은 {{ stats.total }} 입니다. 가입후 {{ stats.bonusMin }} ~
{{ stats.bonusMax }} 능력치 보너스를 니다.
<br />임의의 도시에서 재야로 시작하며 건국과 임관은 게임 내에서 실행합니다.
능력치의 총합은 {{ stats.total }} 입니다. 가입후 {{ stats.bonusMin }} ~ {{ stats.bonusMax }} 능력치 보너스를
받게 됩니다. <br />임의의 도시에서 재야로 시작하며 건국과 임관은 내에서 실행합니다.
</div>
</div>
@@ -141,20 +136,18 @@
<div class="col col-md-11 col-9 center align-self-center">유산 포인트 사용</div>
<div class="col col-md-1 col-3">
<label>
<input type="checkbox" v-model="displayInherit" />
{{
displayInherit ? "숨기기" : "보이기"
}}
<input v-model="displayInherit" type="checkbox" />
{{ displayInherit ? "숨기기" : "보이기" }}
</label>
</div>
</div>
<div class="inherit-block" v-if="displayInherit">
<div v-if="displayInherit" class="inherit-block">
<div class="row">
<div class="col">
<NumberInputWithInfo title="보유한 유산 포인트" v-model="inheritTotalPoint" :readonly="true" />
<NumberInputWithInfo v-model="inheritTotalPoint" title="보유한 유산 포인트" :readonly="true" />
</div>
<div class="col">
<NumberInputWithInfo title="필요 유산 포인트" v-model="inheritRequiredPoint" :readonly="true" />
<NumberInputWithInfo v-model="inheritRequiredPoint" title="필요 유산 포인트" :readonly="true" />
</div>
</div>
<hr />
@@ -163,28 +156,17 @@
<div class="row">
<div class="col col-6 a-right align-self-center">천재로 생성</div>
<div class="col col-6 align-self-center">
<select
class="form-select form-inline"
style="max-width: 20ch"
v-model="args.inheritSpecial"
>
<select v-model="args.inheritSpecial" class="form-select form-inline" style="max-width: 20ch">
<option :value="undefined">사용안함</option>
<option
v-for="(inheritSpecial, key) in availableInheritSpecial"
:key="key"
:value="key"
>{{ inheritSpecial.name }}</option>
<option v-for="(inheritSpecial, key) in availableInheritSpecial" :key="key" :value="key">
{{ inheritSpecial.name }}
</option>
</select>
</div>
</div>
<div class="col align-self-center">
<small
class="text-muted"
v-html="
(availableInheritSpecial[args.inheritSpecial ?? ''] ?? { info: '' })
.info
"
/>
<!-- eslint-disable-next-line vue/no-v-html -->
<small class="text-muted" v-html="availableInheritSpecial[args.inheritSpecial ?? '']?.info" />
</div>
</div>
@@ -192,17 +174,11 @@
<div class="row">
<div class="col col-6 a-right align-self-center">도시</div>
<div class="col col-6 align-self-center">
<select
class="form-select form-inline"
style="max-width: 20ch"
v-model="args.inheritCity"
>
<select v-model="args.inheritCity" class="form-select form-inline" style="max-width: 20ch">
<option :value="undefined">사용안함</option>
<option
v-for="city in availableInheritCity"
:key="city[0]"
:value="city[0]"
>{{ `[${city[1]}] ${city[2]}` }}</option>
<option v-for="city in availableInheritCity" :key="city[0]" :value="city[0]">
{{ `[${city[1]}] ${city[2]}` }}
</option>
</select>
</div>
</div>
@@ -210,28 +186,25 @@
<div class="col col-md-6 col-sm-6 col-12 p-2 align-self-center">
<div class="a-center">
<label>
<input type="checkbox" v-model="inheritTurnTimeSet" />턴 시간
고정
</label>
<label> <input v-model="inheritTurnTimeSet" type="checkbox" /> 시간 고정 </label>
</div>
<div class="row turn_time_pad">
<div class="col col-md-4 offset-md-3 col-4 offset-3">
<NumberInputWithInfo
v-model="inheritTurnTimeMinute"
:readonly="!inheritTurnTimeSet"
:min="0"
:max="1 - turnterm"
v-model="inheritTurnTimeMinute"
:right="true"
title="분"
/>
</div>
<div class="col col-md-4 col-4">
<NumberInputWithInfo
v-model="inheritTurnTimeSecond"
:readonly="!inheritTurnTimeSet"
:min="0"
:max="60"
v-model="inheritTurnTimeSecond"
:right="true"
title="초"
/>
@@ -243,13 +216,13 @@
<div class="a-center">추가 능력치 고정</div>
<div class="row">
<div class="col">
<NumberInputWithInfo title="통솔" v-model="(args.inheritBonusStat ?? [0, 0, 0])[0]" />
<NumberInputWithInfo v-model="(args.inheritBonusStat ?? [0, 0, 0])[0]" title="통솔" />
</div>
<div class="col">
<NumberInputWithInfo title="무력" v-model="(args.inheritBonusStat ?? [0, 0, 0])[1]" />
<NumberInputWithInfo v-model="(args.inheritBonusStat ?? [0, 0, 0])[1]" title="무력" />
</div>
<div class="col">
<NumberInputWithInfo title="지력" v-model="(args.inheritBonusStat ?? [0, 0, 0])[2]" />
<NumberInputWithInfo v-model="(args.inheritBonusStat ?? [0, 0, 0])[2]" title="지력" />
</div>
</div>
</div>
@@ -258,8 +231,8 @@
<div class="row" style="border-top: solid 1px #aaa">
<div class="col a-center" style="margin: 0.5em">
<b-button color="primary" @click="submitForm">장수 생성</b-button>&nbsp;
<b-button color="secondary" @click="resetArgs">다시 입력</b-button>
<b-button color="primary" @click="submitForm"> 장수 생성 </b-button>&nbsp;
<b-button color="secondary" @click="resetArgs"> 다시 입력 </b-button>
</div>
</div>
</div>
@@ -272,12 +245,7 @@ import { defineComponent } from "vue";
import TopBackBar from "@/components/TopBackBar.vue";
import { getIconPath } from "@util/getIconPath";
import { isBrightColor } from "@util/isBrightColor";
import {
abilityLeadint,
abilityLeadpow,
abilityPowint,
abilityRand,
} from "@util/generalStats";
import { abilityLeadint, abilityLeadpow, abilityPowint, abilityRand } from "@util/generalStats";
import { clone, shuffle, sum } from "lodash";
import NumberInputWithInfo from "@/components/NumberInputWithInfo.vue";
import { SammoAPI } from "./SammoAPI";
@@ -358,12 +326,8 @@ export default defineComponent({
NumberInputWithInfo,
},
data() {
const displayTable = JSON.parse(
localStorage.getItem(`conf.${serverID}.join.displayTable`) ?? "true"
);
const displayInherit = JSON.parse(
localStorage.getItem(`conf.${serverID}.join.displayInherit`) ?? "true"
);
const displayTable = JSON.parse(localStorage.getItem(`conf.${serverID}.join.displayTable`) ?? "true");
const displayInherit = JSON.parse(localStorage.getItem(`conf.${serverID}.join.displayInherit`) ?? "true");
const nationListShuffled = shuffle(nationList);
const args: APIArgs = {
name: member.name,
@@ -402,18 +366,36 @@ export default defineComponent({
toggleZoom: true,
};
},
computed: {
iconPath() {
if (this.args.pic) {
return getIconPath(this.member.imgsvr, this.member.picture);
}
return getIconPath(0, "default.jpg");
},
inheritRequiredPoint() {
let inheritRequiredPoint = 0;
if (this.args.inheritCity !== undefined) {
inheritRequiredPoint += inheritPoints.city;
}
if (this.args.inheritSpecial !== undefined) {
inheritRequiredPoint += inheritPoints.special;
}
if (this.args.inheritTurntime !== undefined) {
inheritRequiredPoint += inheritPoints.turnTime;
}
if (this.args.inheritBonusStat !== undefined && sum(this.args.inheritBonusStat) != 0) {
inheritRequiredPoint += inheritPoints.stat;
}
return inheritRequiredPoint;
},
},
watch: {
displayTable(newValue: boolean) {
localStorage.setItem(
`conf.${serverID}.join.displayTable`,
JSON.stringify(newValue)
);
localStorage.setItem(`conf.${serverID}.join.displayTable`, JSON.stringify(newValue));
},
displayInherit(newValue: boolean) {
localStorage.setItem(
`conf.${serverID}.join.displayInherit`,
JSON.stringify(newValue)
);
localStorage.setItem(`conf.${serverID}.join.displayInherit`, JSON.stringify(newValue));
},
inheritTurnTimeMinute(newValue: number) {
if (!this.inheritTurnTimeSet) {
@@ -434,53 +416,21 @@ export default defineComponent({
this.args.inheritTurntime = undefined;
return;
}
this.args.inheritTurntime =
this.inheritTurnTimeMinute * 60 + this.inheritTurnTimeSecond;
},
},
computed: {
iconPath() {
if (this.args.pic) {
return getIconPath(this.member.imgsvr, this.member.picture);
}
return getIconPath(0, "default.jpg");
},
inheritRequiredPoint() {
let inheritRequiredPoint = 0;
if (this.args.inheritCity !== undefined) {
inheritRequiredPoint += inheritPoints.city;
}
if (this.args.inheritSpecial !== undefined) {
inheritRequiredPoint += inheritPoints.special;
}
if (this.args.inheritTurntime !== undefined) {
inheritRequiredPoint += inheritPoints.turnTime;
}
if (
this.args.inheritBonusStat !== undefined &&
sum(this.args.inheritBonusStat) != 0
) {
inheritRequiredPoint += inheritPoints.stat;
}
return inheritRequiredPoint;
this.args.inheritTurntime = this.inheritTurnTimeMinute * 60 + this.inheritTurnTimeSecond;
},
},
methods: {
randStatRandom() {
[this.args.leadership, this.args.strength, this.args.intel] =
abilityRand();
[this.args.leadership, this.args.strength, this.args.intel] = abilityRand();
},
randStatLeadPow() {
[this.args.leadership, this.args.strength, this.args.intel] =
abilityLeadpow();
[this.args.leadership, this.args.strength, this.args.intel] = abilityLeadpow();
},
randStatLeadInt() {
[this.args.leadership, this.args.strength, this.args.intel] =
abilityLeadint();
[this.args.leadership, this.args.strength, this.args.intel] = abilityLeadint();
},
randStatPowInt() {
[this.args.leadership, this.args.strength, this.args.intel] =
abilityPowint();
[this.args.leadership, this.args.strength, this.args.intel] = abilityPowint();
},
resetArgs() {
this.args.name = member.name;
@@ -496,7 +446,9 @@ export default defineComponent({
const totalStat = args.leadership + args.strength + args.intel;
if (totalStat < stats.total) {
if (!confirm(`설정한 능력치가 ${totalStat}으로, 실제 최대치인 ${stats.total}보다 적습니다.\r\n그래도 진행할까요?`)) {
if (
!confirm(`설정한 능력치가 ${totalStat}으로, 실제 최대치인 ${stats.total}보다 적습니다.\r\n그래도 진행할까요?`)
) {
return false;
}
}
@@ -508,9 +460,7 @@ export default defineComponent({
return;
}
alert(
"정상적으로 생성되었습니다. \n위키와 팁/강좌 게시판을 꼭 읽어보세요!"
);
alert("정상적으로 생성되었습니다. \n위키와 팁/강좌 게시판을 꼭 읽어보세요!");
location.href = "./";
},
},
@@ -612,4 +562,4 @@ export default defineComponent({
}
}
}
</style>
</style>
+83 -197
View File
@@ -3,7 +3,7 @@
<BContainer
id="container"
class="tb_layout bg0"
:toast="{root: true}"
:toast="{ root: true }"
:style="{ maxWidth: '1000px', margin: 'auto', border: 'solid 1px #888888', padding: '0' }"
>
<div class="bg1 section_bar">국가 정책</div>
@@ -17,94 +17,54 @@
<div class="form_list row row-cols-md-2 row-cols-1">
<div class="col">
<NumberInputWithInfo v-model="nationPolicy.reqNationGold" :step="100" title="국가 권장 금">
이보다 많으면 포상, 적으면 몰수/헌납합니다.(긴급포상
제외)
이보다 많으면 포상, 적으면 몰수/헌납합니다.(긴급포상 제외)
</NumberInputWithInfo>
</div>
<div class="col">
<NumberInputWithInfo v-model="nationPolicy.reqNationRice" :step="100" title="국가 권장 쌀">
이보다 많으면 포상, 적으면 몰수/헌납합니다.(긴급포상
제외)
이보다 많으면 포상, 적으면 몰수/헌납합니다.(긴급포상 제외)
</NumberInputWithInfo>
</div>
<div class="col">
<NumberInputWithInfo
v-model="nationPolicy.reqHumanWarUrgentGold"
:step="100"
title="유저전투장 긴급포상 금"
>
<NumberInputWithInfo v-model="nationPolicy.reqHumanWarUrgentGold" :step="100" title="유저전투장 긴급포상 금">
유저장긴급포상시 이보다 금이 적은 장수에게 포상합니다.
<br />
0이면
보병 6 징병({{ (defaultStatMax * 100).toLocaleString() }} * 6)
가능한 금을 기준으로 하며, 수치는 현재
{{
zeroPolicy.reqHumanWarUrgentGold.toLocaleString()
}}입니다.
0이면 보병 6 징병({{ (defaultStatMax * 100).toLocaleString() }} * 6) 가능한 금을 기준으로 하며, 수치는
현재 {{ zeroPolicy.reqHumanWarUrgentGold.toLocaleString() }}입니다.
</NumberInputWithInfo>
</div>
<div class="col">
<NumberInputWithInfo
v-model="nationPolicy.reqHumanWarUrgentRice"
:step="100"
title="유저전투장 긴급포상 쌀"
>
<NumberInputWithInfo v-model="nationPolicy.reqHumanWarUrgentRice" :step="100" title="유저전투장 긴급포상 쌀">
유저장긴급포상시 이보다 쌀이 적은 장수에게 포상합니다.
<br />
0이면
기본 병종으로 {{ (defaultStatMax * 100).toLocaleString() }} * 6 사살
가능한 쌀을 기준으로 하며, 수치는 현재
{{
zeroPolicy.reqHumanWarUrgentRice.toLocaleString()
}}입니다.
0이면 기본 병종으로 {{ (defaultStatMax * 100).toLocaleString() }} * 6 사살 가능한 쌀을 기준으로 하며,
수치는 현재 {{ zeroPolicy.reqHumanWarUrgentRice.toLocaleString() }}입니다.
</NumberInputWithInfo>
</div>
<div class="col">
<NumberInputWithInfo
v-model="nationPolicy.reqHumanWarRecommandGold"
:step="100"
title="유저전투장 권장 금"
>
<NumberInputWithInfo v-model="nationPolicy.reqHumanWarRecommandGold" :step="100" title="유저전투장 권장 금">
유저전투장에게 주는 금입니다. 이보다 적으면 포상합니다.
<br />
0이면 유저전투장 긴급포상 금의 2배를 기준으로 하며, 수치는 현재
{{
(calcPolicyValue("reqHumanWarUrgentGold") * 2).toLocaleString()
}}입니다.
{{ (calcPolicyValue("reqHumanWarUrgentGold") * 2).toLocaleString() }}입니다.
</NumberInputWithInfo>
</div>
<div class="col">
<NumberInputWithInfo
v-model="nationPolicy.reqHumanWarRecommandRice"
:step="100"
title="유저전투장 권장 쌀"
>
<NumberInputWithInfo v-model="nationPolicy.reqHumanWarRecommandRice" :step="100" title="유저전투장 권장 쌀">
유저전투장에게 주는 쌀입니다. 이보다 적으면 포상합니다.
<br />
0이면 유저전투장 긴급포상 쌀의 2배를 기준으로 하며, 수치는 현재
{{
(calcPolicyValue("reqHumanWarUrgentRice") * 2).toLocaleString()
}}입니다.
{{ (calcPolicyValue("reqHumanWarUrgentRice") * 2).toLocaleString() }}입니다.
</NumberInputWithInfo>
</div>
<div class="col">
<NumberInputWithInfo
v-model="nationPolicy.reqHumanDevelGold"
:step="100"
title="유저내정장 권장 금"
>
유저내정장에게 주는 금입니다. 이보다 적으면
포상합니다.
<NumberInputWithInfo v-model="nationPolicy.reqHumanDevelGold" :step="100" title="유저내정장 권장 금">
유저내정장에게 주는 금입니다. 이보다 적으면 포상합니다.
</NumberInputWithInfo>
</div>
<div class="col">
<NumberInputWithInfo
v-model="nationPolicy.reqHumanDevelRice"
:step="100"
title="유저내정장 권장 쌀"
>
유저내정장에게 주는 쌀입니다. 이보다 적으면
포상합니다.
<NumberInputWithInfo v-model="nationPolicy.reqHumanDevelRice" :step="100" title="유저내정장 권장 쌀">
유저내정장에게 주는 쌀입니다. 이보다 적으면 포상합니다.
</NumberInputWithInfo>
</div>
<div class="col">
@@ -113,9 +73,7 @@
<br />
0이면 기본 병종 4({{ (defaultStatNPCMax * 100).toLocaleString() }}
* 4) 징병비를 기준으로 하며, 수치는 현재
{{
zeroPolicy.reqNPCWarGold.toLocaleString()
}}입니다.
{{ zeroPolicy.reqNPCWarGold.toLocaleString() }}입니다.
</NumberInputWithInfo>
</div>
<div class="col">
@@ -123,26 +81,21 @@
NPC전투장에게 주는 쌀입니다. 이보다 적으면 포상합니다.
<br />
0이면 기본 병종으로
{{ (defaultStatNPCMax * 100).toLocaleString() }} * 4 사살 가능한
쌀을 기준으로 하며, 수치는 현재
{{
zeroPolicy.reqNPCWarRice.toLocaleString()
}}입니다.
{{ (defaultStatNPCMax * 100).toLocaleString() }} * 4 사살 가능한 쌀을 기준으로 하며, 수치는 현재
{{ zeroPolicy.reqNPCWarRice.toLocaleString() }}입니다.
</NumberInputWithInfo>
</div>
<div class="col">
<NumberInputWithInfo v-model="nationPolicy.reqNPCDevelGold" :step="100" title="NPC내정장 권장 금">
NPC내정장에게 주는 금입니다. 이보다 5배 많다면 헌납합니다.
<br />
0이면
30 내정 가능한 금을 기준으로 하며, 수치는 현재
0이면 30 내정 가능한 금을 기준으로 하며, 수치는 현재
{{ zeroPolicy.reqNPCDevelGold }}입니다.
</NumberInputWithInfo>
</div>
<div class="col">
<NumberInputWithInfo v-model="nationPolicy.reqNPCDevelRice" :step="100" title="NPC내정장 권장 쌀">
NPC내정장에게 주는 쌀입니다. 이보다 5배 많다면
헌납합니다.
NPC내정장에게 주는 쌀입니다. 이보다 5배 많다면 헌납합니다.
</NumberInputWithInfo>
</div>
<div class="col">
@@ -150,21 +103,23 @@
v-model="nationPolicy.minimumResourceActionAmount"
:step="100"
title="포상/몰수/헌납/삼/팜 최소 단위"
>연산결과가 단위보다 적다면 수행하지 않습니다.</NumberInputWithInfo>
>
연산결과가 단위보다 적다면 수행하지 않습니다.
</NumberInputWithInfo>
</div>
<div class="col">
<NumberInputWithInfo
v-model="nationPolicy.maximumResourceActionAmount"
:step="100"
title="포상/몰수/헌납/삼/팜 최대 단위"
>연산결과가 단위보다 크다면, 값에 맞춥니다.</NumberInputWithInfo>
>
연산결과가 단위보다 크다면, 값에 맞춥니다.
</NumberInputWithInfo>
</div>
<div class="col">
<NumberInputWithInfo
v-model="nationPolicy.minWarCrew"
:step="50"
title="최소 전투 가능 병력 수"
>이보다 적을 때에는 징병을 시도합니다.</NumberInputWithInfo>
<NumberInputWithInfo v-model="nationPolicy.minWarCrew" :step="50" title="최소 전투 가능 병력 수">
이보다 적을 때에는 징병을 시도합니다.
</NumberInputWithInfo>
</div>
<div class="col">
<NumberInputWithInfo
@@ -172,38 +127,27 @@
:step="100"
title="NPC 최소 징병 가능 인구 수"
>
도시의 인구가 이보다 낮으면 NPC는 도시에서 징병하지 않고 후방
워프합니다.
<br />NPC의 최대 병력수보다 낮게 설정하면 제자리에서
정착장려를 합니다.
도시의 인구가 이보다 낮으면 NPC는 도시에서 징병하지 않고 후방 워프합니다.
<br />NPC의 최대 병력수보다 낮게 설정하면 제자리에서 정착장려를 합니다.
</NumberInputWithInfo>
</div>
<div class="col">
<NumberInputWithInfo
:modelValue="nationPolicy.safeRecruitCityPopulationRatio * 100"
@update:modelValue="
nationPolicy.safeRecruitCityPopulationRatio = $event / 100
"
:step="0.5"
:int="false"
:min="0"
:max="100"
title="제자리 징병 허용 인구율(%)"
@update:modelValue="nationPolicy.safeRecruitCityPopulationRatio = $event / 100"
>
전쟁 후방 발령, 후방 워프의 기준 인구입니다. 이보다 많다면
'충분하다' 판단합니다.
<br />NPC의 최대 병력수보다 낮게 설정하면
제자리에서 정착장려를 합니다.
전쟁 후방 발령, 후방 워프의 기준 인구입니다. 이보다 많다면 '충분하다' 판단합니다.
<br />NPC의 최대 병력수보다 낮게 설정하면 제자리에서 정착장려를 합니다.
</NumberInputWithInfo>
</div>
<div class="col">
<NumberInputWithInfo
v-model="nationPolicy.minNPCWarLeadership"
:step="5"
title="NPC 전투 참여 통솔 기준"
>
수치보다 같거나 높으면 NPC전투장으로
분류됩니다.
<NumberInputWithInfo v-model="nationPolicy.minNPCWarLeadership" :step="5" title="NPC 전투 참여 통솔 기준">
수치보다 같거나 높으면 NPC전투장으로 분류됩니다.
</NumberInputWithInfo>
</div>
<div class="col">
@@ -214,44 +158,27 @@
:max="100"
title="훈련/사기진작 목표치"
>
훈련/사기진작 기준치입니다. 이보다 같거나 높으면
출병합니다.
훈련/사기진작 기준치입니다. 이보다 같거나 높으면 출병합니다.
</NumberInputWithInfo>
</div>
<!--allowNpcAttackCity는 현재 게임 비활성-->
</div>
<div style="padding: 0 8pt">
<div class="alert alert-secondary">
전투 부대는 작업중입니다(json양식:
{부대번호:[시작도시번호(아국),도착도시번호(적군)],...})
<br />후방 징병 부대는 작업중입니다(json양식: [부대번호,...])
<br />내정 부대는 작업중입니다(json양식: [부대번호,...])
<input
type="hidden"
:value="JSON.stringify(nationPolicy.CombatForce)"
data-type="json"
id="CombatForce"
/>
<input
type="hidden"
:value="JSON.stringify(nationPolicy.SupportForce)"
data-type="json"
id="SupportForce"
/>
<input
type="hidden"
:value="JSON.stringify(nationPolicy.DevelopForce)"
data-type="json"
id="DevelopForce"
/>
전투 부대는 작업중입니다(json양식: {부대번호:[시작도시번호(아국),도착도시번호(적군)],...})
<br />후방 징병 부대는 작업중입니다(json양식: [부대번호,...]) <br />내정 부대는 작업중입니다(json양식:
[부대번호,...])
<input id="CombatForce" type="hidden" :value="JSON.stringify(nationPolicy.CombatForce)" data-type="json" />
<input id="SupportForce" type="hidden" :value="JSON.stringify(nationPolicy.SupportForce)" data-type="json" />
<input id="DevelopForce" type="hidden" :value="JSON.stringify(nationPolicy.DevelopForce)" data-type="json" />
</div>
</div>
<div class="control_bar" data-type="nationPolicy">
<div class="btn-group" role="group">
<button type="button" @click="resetPolicy" class="btn btn-dark reset_btn">초기값으로</button>
<button type="button" @click="rollbackPolicy" class="btn btn-secondary revert_btn">이전값으로</button>
<button type="button" class="btn btn-dark reset_btn" @click="resetPolicy">초기값으로</button>
<button type="button" class="btn btn-secondary revert_btn" @click="rollbackPolicy">이전값으로</button>
</div>
<button type="button" @click="submitPolicy" class="btn btn-primary submit_btn">설정</button>
<button type="button" class="btn btn-primary submit_btn" @click="submitPolicy">설정</button>
</div>
<div class="row row-cols-md-2 row-cols-1 g-0">
<div class="col half_section_left">
@@ -266,8 +193,7 @@
<div class="text-left px-2">
<small class="text-muted">
예턴이 없거나, 지정되어 있더라도 실패하면
<br />아래 순위에 따라
사령턴을 시도합니다.
<br />아래 순위에 따라 사령턴을 시도합니다.
</small>
</div>
<div>
@@ -285,16 +211,14 @@
</template>
<template #item="{ element }">
<div class="list-group-item">
<i class="bi bi-list"></i>
&nbsp;&nbsp;{{
element.id
}}
<i class="bi bi-list" />
&nbsp;&nbsp;{{ element.id }}
<button
class="btn btn-sm float-right btn-secondary py-0 px-1"
v-b-tooltip.hover
class="btn btn-sm float-right btn-secondary py-0 px-1"
:title="actionHelpText[element.id]"
>
<i class="bi bi-question-lg"></i>
<i class="bi bi-question-lg" />
</button>
</div>
</template>
@@ -310,16 +234,14 @@
>
<template #item="{ element }">
<div class="list-group-item">
<i class="bi bi-list"></i>
&nbsp;&nbsp;{{
element.id
}}
<i class="bi bi-list" />
&nbsp;&nbsp;{{ element.id }}
<button
class="btn btn-sm float-right btn-secondary py-0 px-1"
v-b-tooltip.hover
class="btn btn-sm float-right btn-secondary py-0 px-1"
:title="actionHelpText[element.id]"
>
<i class="bi bi-question-lg"></i>
<i class="bi bi-question-lg" />
</button>
</div>
</template>
@@ -328,22 +250,12 @@
</div>
<div class="control_bar" data-type="nationPriority">
<div class="btn-group" role="group">
<button
type="button"
class="btn btn-dark reset_btn"
@click="resetNationPriority"
>초기값으로</button>
<button
type="button"
class="btn btn-secondary revert_btn"
@click="rollbackNationPriority"
>이전값으로</button>
<button type="button" class="btn btn-dark reset_btn" @click="resetNationPriority">초기값으로</button>
<button type="button" class="btn btn-secondary revert_btn" @click="rollbackNationPriority">
이전값으로
</button>
</div>
<button
type="button"
class="btn btn-primary submit_btn"
@click="submitNationPriority"
>설정</button>
<button type="button" class="btn btn-primary submit_btn" @click="submitNationPriority">설정</button>
</div>
</div>
</div>
@@ -359,8 +271,7 @@
<div class="text-left px-2">
<small class="text-muted">
순위가 높은 것부터 시도합니다.
<br />아무것도 실행할 없으면
물자조달이나 인재탐색을 합니다.
<br />아무것도 실행할 없으면 물자조달이나 인재탐색을 합니다.
</small>
</div>
<div>
@@ -378,16 +289,14 @@
</template>
<template #item="{ element }">
<div class="list-group-item">
<i class="bi bi-list"></i>
&nbsp;&nbsp;{{
element.id
}}
<i class="bi bi-list" />
&nbsp;&nbsp;{{ element.id }}
<button
class="btn btn-sm float-right btn-secondary py-0 px-1"
v-b-tooltip.hover
class="btn btn-sm float-right btn-secondary py-0 px-1"
:title="actionHelpText[element.id]"
>
<i class="bi bi-question-lg"></i>
<i class="bi bi-question-lg" />
</button>
</div>
</template>
@@ -403,16 +312,14 @@
>
<template #item="{ element }">
<div class="list-group-item">
<i class="bi bi-list"></i>
&nbsp;&nbsp;{{
element.id
}}
<i class="bi bi-list" />
&nbsp;&nbsp;{{ element.id }}
<button
class="btn btn-sm float-right btn-secondary py-0 px-1"
v-b-tooltip.hover
class="btn btn-sm float-right btn-secondary py-0 px-1"
:title="actionHelpText[element.id]"
>
<i class="bi bi-question-lg"></i>
<i class="bi bi-question-lg" />
</button>
</div>
</template>
@@ -421,22 +328,12 @@
</div>
<div class="control_bar" data-type="generalPriority">
<div class="btn-group" role="group">
<button
type="button"
class="btn btn-dark reset_btn"
@click="resetGeneralPriority"
>초기값으로</button>
<button
type="button"
class="btn btn-secondary revert_btn"
@click="rollbackGeneralPriority"
>이전값으로</button>
<button type="button" class="btn btn-dark reset_btn" @click="resetGeneralPriority">초기값으로</button>
<button type="button" class="btn btn-secondary revert_btn" @click="rollbackGeneralPriority">
이전값으로
</button>
</div>
<button
type="button"
class="btn btn-primary submit_btn"
@click="submitGeneralPriority"
>설정</button>
<button type="button" class="btn btn-primary submit_btn" @click="submitGeneralPriority">설정</button>
</div>
</div>
</div>
@@ -444,7 +341,6 @@
</BContainer>
</template>
<script lang="ts">
type SetterInfo = {
setter: string | null;
date: string | null;
@@ -480,13 +376,7 @@ import "@scss/common/bootstrap5.scss";
import "@scss/game_bg.scss";
import { ref } from "vue";
import type {
IDItem,
InvalidResponse,
NationPolicy,
NPCChiefActions,
NPCGeneralActions,
} from "@/defs";
import type { IDItem, InvalidResponse, NationPolicy, NPCChiefActions, NPCGeneralActions } from "@/defs";
import NumberInputWithInfo from "@/components/NumberInputWithInfo.vue";
import { cloneDeep, isEqual, isNumber, last } from "lodash";
import { unwrap } from "@util/unwrap";
@@ -496,7 +386,7 @@ import { NPCPriorityBtnHelpMessage } from "@/helpTexts";
import draggable from "vuedraggable";
import TopBackBar from "@/components/TopBackBar.vue";
import { convertIDArray } from "@util/convertIDArray";
import { useToast, BContainer } from 'bootstrap-vue-3';
import { useToast, BContainer } from "bootstrap-vue-3";
const chiefActionPriority = ref<IDItem<NPCChiefActions>[]>([]);
const chiefActionKeys = ref(new Set(staticValues.availableNationPriorityItems));
@@ -533,8 +423,8 @@ function resetPolicy() {
}
nationPolicy.value = cloneDeep(staticValues.defaultNationPolicy);
toasts.info({
title: '초기화 완료',
body: "서버 초기값을 적용했습니다.설정 버튼을 누르면 반영됩니다."
title: "초기화 완료",
body: "서버 초기값을 적용했습니다.설정 버튼을 누르면 반영됩니다.",
});
}
@@ -596,9 +486,7 @@ async function submitPolicy() {
}
}
function calcPolicyValue(
title: keyof NationPolicy
): number {
function calcPolicyValue(title: keyof NationPolicy): number {
if (!(title in nationPolicy.value)) {
throw `${title}이 NationPolicy key가 아님`;
}
@@ -745,9 +633,7 @@ async function submitGeneralPriority() {
method: "post",
data: convertFormData({
type: "generalPriority",
data: JSON.stringify(
generalActionPriority.value.map(({ id }) => id)
),
data: JSON.stringify(generalActionPriority.value.map(({ id }) => id)),
}),
});
const result: InvalidResponse = response.data;
@@ -828,4 +714,4 @@ async function submitGeneralPriority() {
padding-right: 0;
padding-left: 0;
}
</style>
</style>
+14 -26
View File
@@ -1,27 +1,21 @@
<template>
<BContainer id="container" :toast="{ root: true }" class="pageNationBetting bg0">
<TopBackBar :title="title" />
<BettingDetail
v-if="targetBettingID !== undefined"
:bettingID="targetBettingID"
@reqToast="addToast"
/>
<BettingDetail v-if="targetBettingID !== undefined" :bettingID="targetBettingID" @reqToast="addToast" />
<div v-if="bettingList === undefined">로딩 중...</div>
<div class="bettingList" v-else>
<div v-else class="bettingList">
<div class="bg2">베팅 목록</div>
<div
class="bettingItem"
v-for="info of Object.values(bettingList).reverse()"
:key="info.id"
class="bettingItem"
@click="targetBettingID = info.id"
>
[{{ parseYearMonth(info.openYearMonth)[0] }} {{ parseYearMonth(info.openYearMonth)[1] }}] {{ info.name }}
<span
v-if="info.finished"
>(종료)</span>
<span
v-else-if="(yearMonth ?? 0) <= info.closeYearMonth"
>({{ parseYearMonth(info.closeYearMonth)[0] }} {{ parseYearMonth(info.closeYearMonth)[1] }}월까지)</span>
<span v-if="info.finished">(종료)</span>
<span v-else-if="(yearMonth ?? 0) <= info.closeYearMonth"
>({{ parseYearMonth(info.closeYearMonth)[0] }} {{ parseYearMonth(info.closeYearMonth)[1] }}월까지)</span
>
<span v-else>(베팅 마감)</span>
</div>
</div>
@@ -44,27 +38,24 @@ import { BContainer, useToast } from "bootstrap-vue-3";
import { unwrap } from "./util/unwrap";
type BettingListResponse = ValidResponse & {
bettingList: Record<number, Omit<BettingInfo & { totalAmount: number }, 'candidates'>>,
year: number,
month: number,
bettingList: Record<number, Omit<BettingInfo & { totalAmount: number }, "candidates">>;
year: number;
month: number;
};
const toasts = unwrap(useToast());
const year = ref<number>();
const month = ref<number>();
const yearMonth = ref<number>();
const bettingList = ref<BettingListResponse['bettingList']>();
const bettingList = ref<BettingListResponse["bettingList"]>();
const targetBettingID = ref<number>();
function addToast(msg: ToastType) {
toasts.show(msg.content, msg.options);
}
console.log('시작!');
console.log("시작!");
onMounted(async () => {
try {
const result = await SammoAPI.Betting.GetBettingList<BettingListResponse>();
@@ -84,8 +75,5 @@ onMounted(async () => {
}
});
const title = '국가 베팅장';
</script>
const title = "국가 베팅장";
</script>
+47 -114
View File
@@ -1,5 +1,5 @@
<template>
<BContainer :toast="{root: true}" id="container" class="pageNationStratFinan bg0">
<BContainer id="container" :toast="{ root: true }" class="pageNationStratFinan bg0">
<TopBackBar title="내무부" />
<div class="diplomacyTitle">외교관계</div>
<div class="diplomacyTable">
@@ -12,11 +12,7 @@
<div>기간</div>
<div>종료 시점</div>
</div>
<div
:class="['diplomacyItem', 'tRow', 's-border-b']"
v-for="nation in nationsList"
:key="nation.nation"
>
<div v-for="nation in nationsList" :key="nation.nation" :class="['diplomacyItem', 'tRow', 's-border-b']">
<div :class="`sam-nation-bg-${nation.color.slice(1)}`">
{{ nation.name }}
</div>
@@ -29,28 +25,21 @@
<template v-else>
<div
:style="{
color:
diplomacyStateInfo[nation.diplomacy.state].color ?? undefined,
color: diplomacyStateInfo[nation.diplomacy.state].color ?? undefined,
}"
>
{{ diplomacyStateInfo[nation.diplomacy.state].name }}
</div>
<div>
{{
nation.diplomacy.term == 0 ? "-" : `${nation.diplomacy.term}개월`
}}
{{ nation.diplomacy.term == 0 ? "-" : `${nation.diplomacy.term}개월` }}
</div>
<div
v-for="([endYear, endMonth], _idx) in [
parseYearMonth(
joinYearMonth(year, month) + (nation.diplomacy.term??0)
),
parseYearMonth(joinYearMonth(year, month) + (nation.diplomacy.term ?? 0)),
]"
:key="_idx"
>
{{
nation.diplomacy.term == 0 ? "-" : `${endYear}년 ${endMonth}월`
}}
{{ nation.diplomacy.term == 0 ? "-" : `${endYear}년 ${endMonth}월` }}
</div>
</template>
</div>
@@ -60,19 +49,9 @@
<div class="bg1" style="display: flex; justify-content: space-around">
<div style="flex: 1 1 auto">국가 방침</div>
<div>
<b-button
@click="enableEditNationMsg"
v-if="editable && !inEditNationMsg"
>국가방침 수정</b-button
>
<b-button @click="saveNationMsg" v-if="editable && inEditNationMsg"
>저장</b-button
>
<b-button
v-if="editable && inEditNationMsg"
@click="rollbackNationMsg"
>취소</b-button
>
<b-button v-if="editable && !inEditNationMsg" @click="enableEditNationMsg"> 국가방침 수정 </b-button>
<b-button v-if="editable && inEditNationMsg" @click="saveNationMsg"> 저장 </b-button>
<b-button v-if="editable && inEditNationMsg" @click="rollbackNationMsg"> 취소 </b-button>
</div>
</div>
<TipTap
@@ -87,22 +66,12 @@
<div class="bg1" style="display: flex; justify-content: space-around">
<div style="flex: 1 1 auto">임관 권유</div>
<div>
<b-button
@click="enableEditScoutMsg"
v-if="editable && !inEditScoutMsg"
>임관 권유문 수정</b-button
>
<b-button @click="saveScoutMsg" v-if="editable && inEditScoutMsg"
>저장</b-button
>
<b-button v-if="editable && inEditScoutMsg" @click="rollbackScoutMsg"
>취소</b-button
>
<b-button v-if="editable && !inEditScoutMsg" @click="enableEditScoutMsg"> 임관 권유문 수정 </b-button>
<b-button v-if="editable && inEditScoutMsg" @click="saveScoutMsg"> 저장 </b-button>
<b-button v-if="editable && inEditScoutMsg" @click="rollbackScoutMsg"> 취소 </b-button>
</div>
</div>
<div style="border-bottom: solid gray 0.5px">
870px x 200px를 넘어서는 내용은 표시되지 않습니다.
</div>
<div style="border-bottom: solid gray 0.5px">870px x 200px를 넘어서는 내용은 표시되지 않습니다.</div>
<div style="width: 870px; margin-left: auto">
<TipTap
v-model="scoutMsg"
@@ -119,9 +88,13 @@
<div class="row gx-0">
<div class="col-12 bg2">자금 예산</div>
<div class="col-4 bg1">현 재</div>
<div class="col-8">{{ gold.toLocaleString() }}</div>
<div class="col-8">
{{ gold.toLocaleString() }}
</div>
<div class="col-4 bg1">단기수입</div>
<div class="col-8">{{ income.gold.war.toLocaleString() }}</div>
<div class="col-8">
{{ income.gold.war.toLocaleString() }}
</div>
<div class="col-4 bg1">세 금</div>
<div class="col-8">
{{ Math.floor(incomeGoldCity).toLocaleString() }}
@@ -134,8 +107,7 @@
<div class="col-4 bg1">국고 예산</div>
<div class="col-8">
{{ Math.floor(gold + incomeGold - outcomeByBill).toLocaleString() }}
({{ incomeGold >= outcomeByBill ? "+" : ""
}}{{ Math.floor(incomeGold - outcomeByBill).toLocaleString() }})
({{ incomeGold >= outcomeByBill ? "+" : "" }}{{ Math.floor(incomeGold - outcomeByBill).toLocaleString() }})
</div>
</div>
</div>
@@ -143,7 +115,9 @@
<div class="row gx-0">
<div class="col-12 bg2">군량 예산</div>
<div class="col-4 bg1">현 재</div>
<div class="col-8">{{ rice.toLocaleString() }}</div>
<div class="col-8">
{{ rice.toLocaleString() }}
</div>
<div class="col-4 bg1">둔전수입</div>
<div class="col-8">
{{ Math.floor(incomeRiceWall).toLocaleString() }}
@@ -160,37 +134,27 @@
<div class="col-4 bg1">국고 예산</div>
<div class="col-8">
{{ Math.floor(rice + incomeRice - outcomeByBill).toLocaleString() }}
({{ incomeRice >= outcomeByBill ? "+" : ""
}}{{ Math.floor(incomeRice - outcomeByBill).toLocaleString() }})
({{ incomeRice >= outcomeByBill ? "+" : "" }}{{ Math.floor(incomeRice - outcomeByBill).toLocaleString() }})
</div>
</div>
</div>
<div class="col-6">
<div class="row gx-0">
<div class="col-4 bg1 d-grid">
<div class="align-self-center">
세율 <span class="avoid-wrap">(5 ~ 30%)</span>
</div>
<div class="align-self-center">세율 <span class="avoid-wrap">(5 ~ 30%)</span></div>
</div>
<div class="col-8 row gx-0">
<div class="col-md-6 offset-md-3 align-self-center">
<div class="input-group my-0">
<input
v-model="policy.rate"
type="number"
class="form-control py-1 f_tnum px-0 text-end"
v-model="policy.rate"
min="5"
max="30"
/><span class="input-group-text py-1 f_tnum">%</span
><b-button
variant="primary"
size="sm"
@click="setRate"
v-if="editable"
>변경</b-button
><b-button size="sm" @click="rollbackRate" v-if="editable"
>취소</b-button
>
><b-button v-if="editable" variant="primary" size="sm" @click="setRate"> 변경 </b-button
><b-button v-if="editable" size="sm" @click="rollbackRate"> 취소 </b-button>
</div>
</div>
</div>
@@ -199,29 +163,20 @@
<div class="col-6">
<div class="row gx-0">
<div class="col-4 bg1 d-grid">
<div class="align-self-center">
지급률 <span class="avoid-wrap">(20 ~ 200%)</span>
</div>
<div class="align-self-center">지급률 <span class="avoid-wrap">(20 ~ 200%)</span></div>
</div>
<div class="col-8 row gx-0">
<div class="col-md-6 offset-md-3 align-self-center">
<div class="input-group my-0">
<input
v-model="policy.bill"
type="number"
class="form-control py-1 f_tnum px-0 text-end"
v-model="policy.bill"
min="20"
max="200"
/><span class="input-group-text py-1 f_tnum">%</span
><b-button
variant="primary"
size="sm"
@click="setBill"
v-if="editable"
>변경</b-button
><b-button size="sm" @click="rollbackBill" v-if="editable"
>취소</b-button
>
><b-button v-if="editable" variant="primary" size="sm" @click="setBill"> 변경 </b-button
><b-button v-if="editable" size="sm" @click="rollbackBill"> 취소 </b-button>
</div>
</div>
</div>
@@ -230,32 +185,20 @@
<div class="col-6">
<div class="row gx-0">
<div class="col-4 bg1 d-grid">
<div class="align-self-center">
기밀 권한 <span class="avoid-wrap">(1 ~ 99년)</span>
</div>
<div class="align-self-center">기밀 권한 <span class="avoid-wrap">(1 ~ 99년)</span></div>
</div>
<div class="col-8 row gx-0">
<div class="col-md-6 offset-md-3 align-self-center">
<div class="input-group my-0">
<input
v-model="policy.secretLimit"
type="number"
class="form-control py-1 f_tnum px-0 text-end"
v-model="policy.secretLimit"
min="1"
max="99"
/><span class="input-group-text py-1 f_tnum">년</span
><b-button
variant="primary"
size="sm"
@click="setSecretLimit"
v-if="editable"
>변경</b-button
><b-button
size="sm"
@click="rollbackSecretLimit"
v-if="editable"
>취소</b-button
>
><b-button v-if="editable" variant="primary" size="sm" @click="setSecretLimit"> 변경 </b-button
><b-button v-if="editable" size="sm" @click="rollbackSecretLimit"> 취소 </b-button>
</div>
</div>
</div>
@@ -267,29 +210,23 @@
<div class="align-self-center">전쟁 금지 설정</div>
</div>
<div class="col-8 d-grid">
<div class="align-self-center">{{warSettingCnt.remain}} 회(월 +{{warSettingCnt.inc}}회, 최대{{warSettingCnt.max}}회)</div>
<div class="align-self-center">
{{ warSettingCnt.remain }} 회(월 +{{ warSettingCnt.inc }}회, 최대{{ warSettingCnt.max }}회)
</div>
</div>
</div>
</div>
<div class="col-3 col-md-4"></div>
<div class="col-3 col-md-4" />
<div class="col-3 col-md-2 row gx-0">
<div class="col-9 col-md-8 text-end p-2">전쟁 금지</div>
<div class="col-3 col-md-4 py-2">
<b-form-checkbox
v-model="policy.blockWar"
@change="setBlockWar"
switch
/>
<b-form-checkbox v-model="policy.blockWar" switch @change="setBlockWar" />
</div>
</div>
<div class="col-3 col-md-2 row gx-0">
<div class="col-9 col-md-8 text-end p-2">임관 금지</div>
<div class="col-3 col-md-4 py-2">
<b-form-checkbox
v-model="policy.blockScout"
@change="setBlockScout"
switch
/>
<b-form-checkbox v-model="policy.blockScout" switch @change="setBlockScout" />
</div>
</div>
</div>
@@ -303,11 +240,7 @@ import TopBackBar from "@/components/TopBackBar.vue";
import BottomBar from "@/components/BottomBar.vue";
import { computed, defineComponent, reactive, ref, toRefs } from "vue";
import { isString } from "lodash";
import {
type diplomacyState,
diplomacyStateInfo,
type NationStaticItem,
} from "./defs";
import { type diplomacyState, diplomacyStateInfo, type NationStaticItem } from "./defs";
import { SammoAPI } from "./SammoAPI";
import { joinYearMonth } from "@/util/joinYearMonth";
import { parseYearMonth } from "@/util/parseYearMonth";
@@ -325,7 +258,7 @@ type NationItem = NationStaticItem & {
type SetBlockWarResponse = ValidResponse & {
availableCnt: number;
}
};
declare const staticValues: {
editable: boolean;
nationMsg: string;
@@ -369,8 +302,8 @@ export default defineComponent({
TopBackBar,
BottomBar,
TipTap,
BContainer
},
BContainer,
},
setup() {
const toasts = unwrap(useToast());
const self = reactive(staticValues);
@@ -644,4 +577,4 @@ export default defineComponent({
},
methods: {},
});
</script>
</script>
+138 -183
View File
@@ -5,27 +5,21 @@
</div>
<div class="row gx-1">
<div class="col d-grid">
<BButton
variant="secondary"
@click="isEditMode = !isEditMode"
>{{ isEditMode ? '일반 모드로' : '고급 모드로' }}</BButton>
<BButton variant="secondary" @click="isEditMode = !isEditMode">
{{ isEditMode ? "일반 모드로" : "고급 모드로" }}
</BButton>
</div>
<div
class="col alert alert-primary m-0 p-0"
style="
text-align: center;
display: flex;
justify-content: center;
align-items: center;
"
><SimpleClock :serverTime="serverNow" /></div>
style="text-align: center; display: flex; justify-content: center; align-items: center"
>
<SimpleClock :serverTime="serverNow" />
</div>
<div class="col d-grid">
<BDropdown right text="반복">
<BDropdownItem
v-for="turnIdx in maxPushTurn"
:key="turnIdx"
@click="repeatGeneralCommand(turnIdx)"
>{{ turnIdx }}</BDropdownItem>
<BDropdownItem v-for="turnIdx in maxPushTurn" :key="turnIdx" @click="repeatGeneralCommand(turnIdx)">
{{ turnIdx }}
</BDropdownItem>
</BDropdown>
</div>
</div>
@@ -33,22 +27,24 @@
<div v-if="isEditMode" class="row gx-1">
<div class="col-4 d-grid">
<BDropdown left text="범위">
<BDropdownItem @click="queryActionHelper.selectTurn()">해제</BDropdownItem>
<BDropdownItem @click="queryActionHelper.selectAll()">모든턴</BDropdownItem>
<BDropdownItem @click="queryActionHelper.selectStep(0, 2)">홀수턴</BDropdownItem>
<BDropdownItem @click="queryActionHelper.selectStep(1, 2)">짝수턴</BDropdownItem>
<BDropdownDivider></BDropdownDivider>
<BDropdownItem @click="queryActionHelper.selectTurn()"> 해제 </BDropdownItem>
<BDropdownItem @click="queryActionHelper.selectAll()"> 모든턴 </BDropdownItem>
<BDropdownItem @click="queryActionHelper.selectStep(0, 2)"> 홀수턴 </BDropdownItem>
<BDropdownItem @click="queryActionHelper.selectStep(1, 2)"> 짝수턴 </BDropdownItem>
<BDropdownDivider />
<BDropdownText v-for="spanIdx in [3, 4, 5, 6, 7]" :key="spanIdx">
{{ spanIdx }} 간격
<br />
<BButtonGroup>
<BButton
class="ignoreMe"
v-for="beginIdx in spanIdx"
:key="beginIdx"
class="ignoreMe"
@click="queryActionHelper.selectStep(beginIdx - 1, spanIdx)"
>{{ beginIdx }}</BButton>
>
{{ beginIdx }}
</BButton>
</BButtonGroup>
</BDropdownText>
</BDropdown>
@@ -62,7 +58,7 @@
@click.self="useStoredAction(actions)"
>
{{ actionKey }}
<BButton @click.prevent="deleteStoredActions(actionKey)" size="sm">삭제</BButton>
<BButton size="sm" @click.prevent="deleteStoredActions(actionKey)"> 삭제 </BButton>
</BDropdownItem>
</BDropdown>
</div>
@@ -73,51 +69,41 @@
v-for="(action, idx) of Array.from(recentActions.values()).reverse()"
:key="idx"
@click="void reserveCommandDirect([[Array.from(selectedTurnList.values()), action]])"
>{{ action.brief }}</BDropdownItem>
>
{{ action.brief }}
</BDropdownItem>
</BDropdown>
</div>
<div class="col-5 d-grid">
<BDropdown left variant="info" text="선택한 턴을">
<BDropdownItem @click="clipboardCut">
<i class="bi bi-scissors"></i>&nbsp;잘라내기
</BDropdownItem>
<BDropdownItem @click="clipboardCopy">
<i class="bi bi-files"></i>&nbsp;복사하기
</BDropdownItem>
<BDropdownItem @click="clipboardPaste">
<i class="bi bi-clipboard-fill"></i>&nbsp;붙여넣기
</BDropdownItem>
<BDropdownItem @click="clipboardCut"> <i class="bi bi-scissors" />&nbsp;잘라내기 </BDropdownItem>
<BDropdownItem @click="clipboardCopy"> <i class="bi bi-files" />&nbsp;복사하기 </BDropdownItem>
<BDropdownItem @click="clipboardPaste"> <i class="bi bi-clipboard-fill" />&nbsp;붙여넣기 </BDropdownItem>
<BDropdownDivider />
<BDropdownItem @click="setStoredActions">
<i class="bi bi-bookmark-plus-fill"></i>&nbsp;보관하기
</BDropdownItem>
<BDropdownItem @click="subRepeatCommand">
<i class="bi bi-arrow-repeat"></i>&nbsp;반복하기
<i class="bi bi-bookmark-plus-fill" />&nbsp;보관하기
</BDropdownItem>
<BDropdownItem @click="subRepeatCommand"> <i class="bi bi-arrow-repeat" />&nbsp;반복하기 </BDropdownItem>
<BDropdownDivider />
<BDropdownItem @click="eraseSelectedTurnList">
<i class="bi bi-eraser"></i>&nbsp;비우기
</BDropdownItem>
<BDropdownItem @click="eraseSelectedTurnList"> <i class="bi bi-eraser" />&nbsp;비우기 </BDropdownItem>
<BDropdownItem @click="eraseAndPullCommand">
<i class="bi bi-arrow-bar-up"></i>&nbsp;지우고 당기기
</BDropdownItem>
<BDropdownItem @click="pushEmptyCommand">
<i class="bi bi-arrow-bar-down"></i>&nbsp;뒤로 밀기
<i class="bi bi-arrow-bar-up" />&nbsp;지우고 당기기
</BDropdownItem>
<BDropdownItem @click="pushEmptyCommand"> <i class="bi bi-arrow-bar-down" />&nbsp;뒤로 밀기 </BDropdownItem>
<!-- 최근에 실행한 10 -->
</BDropdown>
</div>
<div class="col-7 d-grid">
<BButton variant="light" @click="toggleForm($event)" :style="{ color: 'black' }">명령 선택 </BButton>
<BButton variant="light" :style="{ color: 'black' }" @click="toggleForm($event)"> 명령 선택 </BButton>
</div>
</div>
<CommandSelectForm
:commandList="commandList"
ref="commandSelectForm"
@on-close="chooseCommand($event)"
v-model:activatedCategory="activatedCategory"
:commandList="commandList"
@onClose="chooseCommand($event)"
/>
<div :style="{ position: 'relative' }">
@@ -131,82 +117,79 @@
}"
>
<CommandSelectForm
:commandList="commandList"
ref="commandQuickReserveForm"
@on-close="chooseQuickReserveCommand($event)"
:hideClose="false"
v-model:activatedCategory="activatedCategory"
:commandList="commandList"
:hideClose="false"
@onClose="chooseQuickReserveCommand($event)"
/>
</div>
</div>
<div :class="{
commandTable: true,
isEditMode,
}">
<div
:class="{
commandTable: true,
isEditMode,
}"
>
<DragSelect
v-slot="{ selected }"
:style="rowGridStyle"
:disabled="!isEditMode"
attribute="turnIdx"
@dragStart="isDragToggle = true"
@dragDone="
isDragToggle = false;
queryActionHelper.toggleTurn(...$event);
isDragToggle = false;
queryActionHelper.toggleTurn(...$event);
"
v-slot="{ selected }"
>
<div
v-for="(turnObj, turnIdx) in reservedCommandList.slice(
0,
viewMaxTurn
)"
:turnIdx="turnIdx"
v-for="(turnObj, turnIdx) in reservedCommandList.slice(0, viewMaxTurn)"
:key="turnIdx"
:turnIdx="turnIdx"
class="idx_pad center d-grid"
>
<BButton
v-if="isEditMode"
size="sm"
:variant="
(isDragToggle && selected.has(`${turnIdx}`)) ? 'light' :
selectedTurnList.has(turnIdx)
? 'info'
: selectedTurnList.size == 0 && prevSelectedTurnList.has(turnIdx)
? 'success'
: 'primary'
isDragToggle && selected.has(`${turnIdx}`)
? 'light'
: selectedTurnList.has(turnIdx)
? 'info'
: selectedTurnList.size == 0 && prevSelectedTurnList.has(turnIdx)
? 'success'
: 'primary'
"
>{{ turnIdx + 1 }}</BButton>
<div v-else class="plain-center">{{ turnIdx + 1 }}</div>
>
{{ turnIdx + 1 }}
</BButton>
<div v-else class="plain-center">
{{ turnIdx + 1 }}
</div>
</div>
</DragSelect>
<DragSelect
v-slot="{ selected }"
:style="rowGridStyle"
attribute="turnIdx"
:disabled="!isEditMode"
@dragStart="isDragSingle = true"
@dragDone="
isDragSingle = false;
queryActionHelper.selectTurn(...$event);
isDragSingle = false;
queryActionHelper.selectTurn(...$event);
"
v-slot="{ selected }"
>
<div
v-for="(turnObj, turnIdx) in reservedCommandList.slice(
0,
viewMaxTurn
)"
v-for="(turnObj, turnIdx) in reservedCommandList.slice(0, viewMaxTurn)"
:key="turnIdx"
height="24"
class="month_pad center"
:turnIdx="turnIdx"
:style="{
'white-space': 'nowrap',
'font-size': `${Math.min(
14,
(75 / (`${turnObj.year ?? 1}`.length + 8)) * 1.8
)}px`,
'font-size': `${Math.min(14, (75 / (`${turnObj.year ?? 1}`.length + 8)) * 1.8)}px`,
overflow: 'hidden',
color:
isDragSingle && selected.has(`${turnIdx}`) ? 'cyan' : undefined,
color: isDragSingle && selected.has(`${turnIdx}`) ? 'cyan' : undefined,
}"
>
{{ turnObj.year ? `${turnObj.year}` : "" }}
@@ -215,10 +198,7 @@ queryActionHelper.selectTurn(...$event);
</DragSelect>
<div :style="rowGridStyle">
<div
v-for="(turnObj, turnIdx) in reservedCommandList.slice(
0,
viewMaxTurn
)"
v-for="(turnObj, turnIdx) in reservedCommandList.slice(0, viewMaxTurn)"
:key="turnIdx"
class="time_pad center"
:style="{
@@ -226,65 +206,56 @@ queryActionHelper.selectTurn(...$event);
whiteSpace: 'nowrap',
overflow: 'hidden',
}"
>{{ turnObj.time }}</div>
>
{{ turnObj.time }}
</div>
</div>
<div :style="rowGridStyle">
<div
v-for="(turnObj, turnIdx) in reservedCommandList.slice(
0,
viewMaxTurn
)"
v-for="(turnObj, turnIdx) in reservedCommandList.slice(0, viewMaxTurn)"
:key="turnIdx"
class="turn_pad center"
>
<span
class="turn_text"
:style="turnObj.style"
v-b-tooltip.hover
:title="turnObj.tooltip"
v-html="turnObj.brief"
></span>
<span v-b-tooltip.hover class="turn_text" :style="turnObj.style" :title="turnObj.tooltip">
<!-- eslint-disable-next-line vue/no-v-html -->
<span v-html="turnObj.brief" />
</span>
</div>
</div>
<div v-if="!isEditMode" :style="rowGridStyle">
<div
v-for="(turnObj, turnIdx) in reservedCommandList.slice(
0,
viewMaxTurn
)"
v-for="(turnObj, turnIdx) in reservedCommandList.slice(0, viewMaxTurn)"
:key="turnIdx"
class="action_pad d-grid"
>
<BButton
:variant="(turnIdx % 2 == 0) ? 'secondary' : 'dark'"
:variant="turnIdx % 2 == 0 ? 'secondary' : 'dark'"
size="sm"
class="simple_action_btn bi bi-pencil"
@click="toggleQuickReserveForm(turnIdx)"
></BButton>
/>
</div>
</div>
</div>
<div class="row gx-1">
<div class="col d-grid">
<BDropdown :split="isEditMode" text="당기기" @click="pullGeneralCommandSingle">
<BDropdownItem
v-for="turnIdx in maxPushTurn"
:key="turnIdx"
@click="pushGeneralCommand(-turnIdx)"
>{{ turnIdx }}턴</BDropdownItem>
<BDropdownItem v-for="turnIdx in maxPushTurn" :key="turnIdx" @click="pushGeneralCommand(-turnIdx)">
{{ turnIdx }}
</BDropdownItem>
</BDropdown>
</div>
<div class="col d-grid">
<BDropdown :split="isEditMode" text="미루기" @click="pushGeneralCommandSingle">
<BDropdownItem
v-for="turnIdx in maxPushTurn"
:key="turnIdx"
@click="pushGeneralCommand(turnIdx)"
>{{ turnIdx }}턴</BDropdownItem>
<BDropdownItem v-for="turnIdx in maxPushTurn" :key="turnIdx" @click="pushGeneralCommand(turnIdx)">
{{ turnIdx }}
</BDropdownItem>
</BDropdown>
</div>
<div class="col d-grid">
<BButton @click="toggleViewMaxTurn">{{ flippedMaxTurn == viewMaxTurn ? "펼치기" : "접기" }}</BButton>
<BButton @click="toggleViewMaxTurn">
{{ flippedMaxTurn == viewMaxTurn ? "펼치기" : "접기" }}
</BButton>
</div>
</div>
</div>
@@ -292,17 +263,17 @@ queryActionHelper.selectTurn(...$event);
<script lang="ts">
declare const staticValues: {
maxTurn: number,
maxPushTurn: number,
maxTurn: number;
maxPushTurn: number;
commandList: {
category: string;
values: CommandItem[];
}[],
serverNow: string,
serverNick: string,
mapName: string,
unitSet: string,
}
}[];
serverNow: string;
serverNick: string;
mapName: string;
unitSet: string;
};
</script>
<script lang="ts" setup>
@@ -321,13 +292,11 @@ import type { CommandItem, ReserveCommandResponse } from "@/defs";
import CommandSelectForm from "@/components/CommandSelectForm.vue";
import { BButton, BButtonGroup, BDropdownItem, BDropdown, BDropdownText, BDropdownDivider } from "bootstrap-vue-3";
import { StoredActionsHelper } from "./util/StoredActionsHelper";
import type { TurnObj } from '@/defs';
import type { TurnObj } from "@/defs";
import type { Args } from "./processing/args";
import { QueryActionHelper } from "./util/QueryActionHelper";
import SimpleClock from "./components/SimpleClock.vue";
type ReservedCommandResponse = {
result: true;
turnTime: string;
@@ -339,13 +308,7 @@ type ReservedCommandResponse = {
autorun_limit: null | number;
};
const {
maxTurn,
maxPushTurn,
commandList,
} = staticValues;
const { maxTurn, maxPushTurn, commandList } = staticValues;
const listReqArgCommand = new Set<string>();
const selectedCommand = ref(staticValues.commandList[0].values[0]);
@@ -391,7 +354,12 @@ function isDropdownChildren(e?: Event): boolean {
}
const queryActionHelper = new QueryActionHelper(staticValues.maxTurn);
const storedActionsHelper = new StoredActionsHelper(staticValues.serverNick, 'general', staticValues.mapName, staticValues.unitSet);
const storedActionsHelper = new StoredActionsHelper(
staticValues.serverNick,
"general",
staticValues.mapName,
staticValues.unitSet
);
const reservedCommandList = queryActionHelper.reservedCommandList;
const prevSelectedTurnList = queryActionHelper.prevSelectedTurnList;
@@ -477,7 +445,6 @@ function pullGeneralCommandSingle(e: Event) {
void pushGeneralCommand(-1);
}
async function reloadCommandList() {
let result: ReservedCommandResponse;
try {
@@ -494,9 +461,7 @@ async function reloadCommandList() {
let nextTurnTime = new Date(turnTime);
const autorunLimitYearMonth = result.autorun_limit ?? yearMonth - 1;
const [autorunLimitYear, autorunLimitMonth] = parseYearMonth(
autorunLimitYearMonth
);
const [autorunLimitYear, autorunLimitMonth] = parseYearMonth(autorunLimitYearMonth);
reservedCommandList.value = [];
for (const obj of result.turn) {
@@ -512,9 +477,7 @@ async function reloadCommandList() {
}
style.color = "#aaffff";
tooltip.push(
`자율 행동 기간: ${autorunLimitYear}년 ${autorunLimitMonth}월까지`
);
tooltip.push(`자율 행동 기간: ${autorunLimitYear}${autorunLimitMonth}월까지`);
}
if (mb_strwidth(brief) > 22) {
@@ -534,22 +497,20 @@ async function reloadCommandList() {
nextTurnTime = addMinutes(nextTurnTime, result.turnTerm);
}
serverNow.value = parseTime(result.date);
}
async function reserveCommandDirect(args: [number[], TurnObj][], reload = true): Promise<boolean> {
const query: {
turnList: number[],
action: string,
arg: Args
turnList: number[];
action: string;
arg: Args;
}[] = [];
for (const [turnList, { action, arg }] of args) {
query.push({
turnList,
action,
arg
arg,
});
}
@@ -593,12 +554,10 @@ async function reserveCommand() {
storedActionsHelper.pushRecentActions({
action: commandName,
brief: result.brief,
arg: {}
arg: {},
});
queryActionHelper.releaseSelectedTurnList();
} catch (e) {
console.error(e);
alert(`실패했습니다: ${e}`);
@@ -636,29 +595,23 @@ function toggleQuickReserveForm(turnIdx: number) {
commandQuickReserveForm.value?.show();
}
watch(isEditMode, newEditMode => {
watch(isEditMode, (newEditMode) => {
if (newEditMode) {
commandQuickReserveForm.value?.close();
currentQuickReserveTarget.value = -1;
}
else {
} else {
commandSelectForm.value?.close();
}
});
const emptyTurnObj: TurnObj = { action: '휴식', brief: '휴식', arg: {} };
const emptyTurnObj: TurnObj = { action: "휴식", brief: "휴식", arg: {} };
const recentActions = storedActionsHelper.recentActions;
const storedActions = storedActionsHelper.storedActions;
const activatedCategory = storedActionsHelper.activatedCategory;
async function eraseSelectedTurnList(releaseSelect = true): Promise<boolean> {
const result = await reserveCommandDirect([[
queryActionHelper.getSelectedTurnList(),
emptyTurnObj
]]);
const result = await reserveCommandDirect([[queryActionHelper.getSelectedTurnList(), emptyTurnObj]]);
if (releaseSelect) {
queryActionHelper.releaseSelectedTurnList();
}
@@ -713,7 +666,6 @@ async function subRepeatCommand(releaseSelect = true): Promise<boolean> {
return result;
}
async function eraseAndPullCommand(releaseSelect = true): Promise<boolean> {
const reqTurnList = queryActionHelper.getSelectedTurnList();
const selectedMinTurnIdx = reqTurnList[0];
@@ -731,7 +683,6 @@ async function eraseAndPullCommand(releaseSelect = true): Promise<boolean> {
const actions: [number[], TurnObj][] = [];
const emptyTurnList: number[] = [];
for (const srcTurnIdx of range(selectedMinTurnIdx + queryLength, maxTurn)) {
@@ -740,11 +691,14 @@ async function eraseAndPullCommand(releaseSelect = true): Promise<boolean> {
emptyTurnList.push(srcTurnIdx - queryLength);
continue;
}
actions.push([[srcTurnIdx - queryLength], {
action: rawAction.action,
arg: rawAction.arg,
brief: rawAction.brief
}]);
actions.push([
[srcTurnIdx - queryLength],
{
action: rawAction.action,
arg: rawAction.arg,
brief: rawAction.brief,
},
]);
}
emptyTurnList.push(...range(maxTurn - queryLength, maxTurn));
@@ -774,7 +728,6 @@ async function pushEmptyCommand(releaseSelect = true): Promise<boolean> {
const actions: [number[], TurnObj][] = [];
const emptyTurnList: number[] = [];
for (const srcTurnIdx of range(selectedMinTurnIdx, maxTurn - queryLength)) {
@@ -783,11 +736,14 @@ async function pushEmptyCommand(releaseSelect = true): Promise<boolean> {
emptyTurnList.push(srcTurnIdx + queryLength);
continue;
}
actions.push([[srcTurnIdx + queryLength], {
action: rawAction.action,
arg: rawAction.arg,
brief: rawAction.brief
}]);
actions.push([
[srcTurnIdx + queryLength],
{
action: rawAction.action,
arg: rawAction.arg,
brief: rawAction.brief,
},
]);
}
emptyTurnList.push(...range(selectedMinTurnIdx, selectedMinTurnIdx + queryLength));
@@ -804,7 +760,7 @@ function setStoredActions() {
const actions = queryActionHelper.extractQueryActions();
const turnBrief = new Map<number, string>();
for (const [subTurnList, action] of actions) {
const actionName = action.action.split('_');
const actionName = action.action.split("_");
const actionShortName = actionName.length == 1 ? actionName[0] : actionName[1];
for (const turnIdx of subTurnList) {
turnBrief.set(turnIdx, actionShortName[0]);
@@ -814,10 +770,10 @@ function setStoredActions() {
const turnBriefStr = Array.from(turnBrief.entries())
.sort(([turnA], [turnB]) => turnA - turnB)
.map(([, action]) => action)
.join('');
.join("");
const nickName = trim(prompt('선택한 턴들의 별명을 지어주세요', turnBriefStr) ?? '');
if (nickName == '') {
const nickName = trim(prompt("선택한 턴들의 별명을 지어주세요", turnBriefStr) ?? "");
if (nickName == "") {
return;
}
@@ -826,12 +782,12 @@ function setStoredActions() {
}
function deleteStoredActions(actionKey: string) {
storedActionsHelper.deleteStoredActions(actionKey)
storedActionsHelper.deleteStoredActions(actionKey);
}
async function useStoredAction(rawActions: [number[], TurnObj][]) {
const reqTurnList = queryActionHelper.getSelectedTurnList();
const actions = queryActionHelper.amplifyQueryActions(rawActions, reqTurnList)
const actions = queryActionHelper.amplifyQueryActions(rawActions, reqTurnList);
const result = await reserveCommandDirect(actions);
queryActionHelper.releaseSelectedTurnList();
return result;
@@ -840,7 +796,6 @@ async function useStoredAction(rawActions: [number[], TurnObj][]) {
onMounted(() => {
void reloadCommandList();
});
</script>
<style lang="scss">
@use "sass:color";
@@ -947,4 +902,4 @@ onMounted(() => {
text-overflow: ellipsis;
overflow: hidden;
}
</style>
</style>
+383 -382
View File
@@ -1,150 +1,159 @@
<template>
<div v-if="bettingDetailInfo !== undefined && info !== undefined">
<div class="bg2">
{{ info.name }}
<span v-if="info.finished">(종료)</span>
<span
v-else-if="(yearMonth ?? 0) <= info.closeYearMonth"
>({{ parseYearMonth(info.closeYearMonth)[0] }} {{ parseYearMonth(info.closeYearMonth)[1] }}월까지)</span>
<span v-else>(베팅 마감)</span>
(총액: {{ bettingAmount.toLocaleString() }})
</div>
<div class="row bettingCandidates gx-1 gy-1">
<div
class="col-4 col-md-2"
v-for="(candidate, idx) in info.candidates"
:key="idx"
@click="toggleCandidate(idx)"
>
<div
:class="[
'bettingCandidate',
pickedBetType.has(idx) ? 'picked' : undefined,
(info.finished && winner.has(idx)) ? 'picked' : undefined,
]"
>
<div class="title bg1">{{ candidate.title }}</div>
<div class="info" v-if="candidate.isHtml" v-html="candidate.info"></div>
<div
class="pickRate"
>선택율: {{ ((partialBet.get(idx) ?? 0) / pureBettingAmount * 100).toFixed(1) }}%</div>
</div>
</div>
</div>
<div v-if="!info.finished && (yearMonth ?? 0) <= info.closeYearMonth" class="row gx-0">
<div
class="col-6 col-md-3 align-self-center"
>잔여 {{ info.reqInheritancePoint ? '포인트' : '금' }} : {{ bettingDetailInfo.remainPoint.toLocaleString() }}</div>
<div
class="col-6 col-md-3 align-self-center"
>사용 포인트: {{ sum(Array.from(myBettings.values())).toLocaleString() }}</div>
<div class="col-6 col-md-3 align-self-center">대상: {{ getTypeStr(pickedBetTypeKey) }}</div>
<div class="col-4 col-md-2 d-grid">
<b-form-input
class="d-grid"
type="number"
v-model="betPoint"
:min="10"
:max="1000"
:step="10"
></b-form-input>
</div>
<div class="col-2 col-md-1 d-grid">
<b-button class="d-grid" @click="submitBet">베팅</b-button>
</div>
</div>
<div>
<div class="bg2">배당 순위</div>
<div
class="row"
:style="{
borderBottom: 'gray solid 1px'
}"
>
<div class="col-5 text-center">대상</div>
<div class="col-2 text-center">베팅액</div>
<div class="col-3 text-center"> 베팅</div>
<div class="col-2 text-center">{{ info.finished ? '배율' : '기대 배율' }}</div>
</div>
<template v-if="info.finished">
<div class="row" v-for="[betType, amount] of detailBet" :key="betType">
<template
v-for="[matchPoint, color] of [calcMatchPointWithColor(betType)]"
:key="matchPoint"
>
<div
class="col-5"
:style="{
fontWeight: myBettings.has(betType) ? 'bold' : undefined,
color: color
}"
>{{ getTypeStr(betType) }}</div>
<div class="col-2 text-end">{{ amount.toLocaleString() }}</div>
<div class="col-3 text-center" v-if="myBettings.has(betType)">
<template
v-for="subPoint of [myBettings.get(betType) ?? 0]"
>({{ subPoint.toLocaleString() }} -> {{ calculatedReward[matchPoint] == 0 ? 0 : (subPoint * calculatedReward[matchPoint] / (calculatedSubAmount.get(matchPoint) ?? 1)).toFixed(1).toLocaleString() }})</template>
</div>
<div class="col-3 text-center" v-else></div>
<div
class="col-2 text-end"
>{{ (calculatedReward[matchPoint] == 0 ? 0 : (calculatedReward[matchPoint] / (calculatedSubAmount.get(matchPoint) ?? 1))).toFixed(1).toLocaleString() }}</div>
</template>
</div>
</template>
<template v-else>
<div class="row" v-for="[betType, amount] of detailBet" :key="betType">
<div
class="col-5"
:style="{
fontWeight: myBettings.has(betType) ? 'bold' : undefined,
}"
>{{ getTypeStr(betType) }}</div>
<div class="col-2 text-end">{{ amount.toLocaleString() }}</div>
<div class="col-3 text-center" v-if="myBettings.has(betType)">
<template
v-for="subPoint of [myBettings.get(betType) ?? 0]"
>({{ subPoint.toLocaleString() }} -> {{ (subPoint * maxBettingReward / amount).toFixed(1).toLocaleString() }})</template>
</div>
<div class="col-3 text-center" v-else></div>
<div
class="col-2 text-end"
>{{ (maxBettingReward / amount).toFixed(1).toLocaleString() }}</div>
</div>
</template>
</div>
<div v-if="bettingDetailInfo !== undefined && info !== undefined">
<div class="bg2">
{{ info.name }}
<span v-if="info.finished">(종료)</span>
<span v-else-if="(yearMonth ?? 0) <= info.closeYearMonth"
>({{ parseYearMonth(info.closeYearMonth)[0] }} {{ parseYearMonth(info.closeYearMonth)[1] }}월까지)</span
>
<span v-else>(베팅 마감)</span>
(총액: {{ bettingAmount.toLocaleString() }})
</div>
<div class="row bettingCandidates gx-1 gy-1">
<div v-for="(candidate, idx) in info.candidates" :key="idx" class="col-4 col-md-2" @click="toggleCandidate(idx)">
<div
:class="[
'bettingCandidate',
pickedBetType.has(idx) ? 'picked' : undefined,
info.finished && winner.has(idx) ? 'picked' : undefined,
]"
>
<div class="title bg1">
{{ candidate.title }}
</div>
<!-- eslint-disable-next-line vue/no-v-html -->
<div v-if="candidate.isHtml" class="info" v-html="candidate.info" />
<div class="pickRate">선택율: {{ (((partialBet.get(idx) ?? 0) / pureBettingAmount) * 100).toFixed(1) }}%</div>
</div>
</div>
</div>
<div v-if="!info.finished && (yearMonth ?? 0) <= info.closeYearMonth" class="row gx-0">
<div class="col-6 col-md-3 align-self-center">
잔여 {{ info.reqInheritancePoint ? "포인트" : "금" }} : {{ bettingDetailInfo.remainPoint.toLocaleString() }}
</div>
<div class="col-6 col-md-3 align-self-center">
사용 포인트: {{ sum(Array.from(myBettings.values())).toLocaleString() }}
</div>
<div class="col-6 col-md-3 align-self-center">대상: {{ getTypeStr(pickedBetTypeKey) }}</div>
<div class="col-4 col-md-2 d-grid">
<!-- eslint-disable-next-line vue/max-attributes-per-line -->
<b-form-input v-model="betPoint" class="d-grid" type="number" :min="10" :max="1000" :step="10" />
</div>
<div class="col-2 col-md-1 d-grid">
<b-button class="d-grid" @click="submitBet"> 베팅 </b-button>
</div>
</div>
<div>
<div class="bg2">배당 순위</div>
<div
class="row"
:style="{
borderBottom: 'gray solid 1px',
}"
>
<div class="col-5 text-center">대상</div>
<div class="col-2 text-center">베팅액</div>
<div class="col-3 text-center"> 베팅</div>
<div class="col-2 text-center">
{{ info.finished ? "배율" : "기대 배율" }}
</div>
</div>
<template v-if="info.finished">
<div v-for="[betType, amount] of detailBet" :key="betType" class="row">
<template v-for="[matchPoint, color] of [calcMatchPointWithColor(betType)]" :key="matchPoint">
<div
class="col-5"
:style="{
fontWeight: myBettings.has(betType) ? 'bold' : undefined,
color: color,
}"
>
{{ getTypeStr(betType) }}
</div>
<div class="col-2 text-end">
{{ amount.toLocaleString() }}
</div>
<div v-if="myBettings.has(betType)" class="col-3 text-center">
<template v-for="subPoint of [myBettings.get(betType) ?? 0]">
({{ subPoint.toLocaleString() }} ->
{{
calculatedReward[matchPoint] == 0
? 0
: ((subPoint * calculatedReward[matchPoint]) / (calculatedSubAmount.get(matchPoint) ?? 1))
.toFixed(1)
.toLocaleString()
}})
</template>
</div>
<div v-else class="col-3 text-center" />
<div class="col-2 text-end">
{{
(calculatedReward[matchPoint] == 0
? 0
: calculatedReward[matchPoint] / (calculatedSubAmount.get(matchPoint) ?? 1)
)
.toFixed(1)
.toLocaleString()
}}
</div>
</template>
</div>
</template>
<template v-else>
<div v-for="[betType, amount] of detailBet" :key="betType" class="row">
<div
class="col-5"
:style="{
fontWeight: myBettings.has(betType) ? 'bold' : undefined,
}"
>
{{ getTypeStr(betType) }}
</div>
<div class="col-2 text-end">
{{ amount.toLocaleString() }}
</div>
<div v-if="myBettings.has(betType)" class="col-3 text-center">
<template v-for="subPoint of [myBettings.get(betType) ?? 0]">
({{ subPoint.toLocaleString() }} ->
{{ ((subPoint * maxBettingReward) / amount).toFixed(1).toLocaleString() }})
</template>
</div>
<div v-else class="col-3 text-center" />
<div class="col-2 text-end">{{ (maxBettingReward / amount).toFixed(1).toLocaleString() }}</div>
</div>
</template>
</div>
</div>
</template>
<script setup lang="ts">
import type { BettingInfo, ToastType } from '@/defs';
import type { BettingInfo, ToastType } from "@/defs";
import { SammoAPI, type ValidResponse } from "@/SammoAPI";
import { joinYearMonth } from '@/util/joinYearMonth';
import { parseYearMonth } from '@/util/parseYearMonth';
import { isString, range, sum } from 'lodash';
import { joinYearMonth } from "@/util/joinYearMonth";
import { parseYearMonth } from "@/util/parseYearMonth";
import { isString, range, sum } from "lodash";
import { ref, type PropType, watch } from "vue";
type BettingDetailResponse = ValidResponse & {
bettingInfo: BettingInfo;
bettingDetail: [string, number][];
myBetting: [string, number][];
remainPoint: number;
year: number;
month: number;
}
bettingInfo: BettingInfo;
bettingDetail: [string, number][];
myBetting: [string, number][];
remainPoint: number;
year: number;
month: number;
};
const props = defineProps({
bettingID: {
type: Number as PropType<number>,
required: true,
}
bettingID: {
type: Number as PropType<number>,
required: true,
},
});
const emit = defineEmits<{
(event: 'reqToast', content: ToastType): void,
(event: "reqToast", content: ToastType): void;
}>();
const year = ref<number>(0);
@@ -163,24 +172,26 @@ const detailBet = ref<[string, number][]>([]);
const typeMap = ref(new Map<string, string>());
function getTypeStr(type: string): string {
const typeResult = typeMap.value.get(type);
if (typeResult !== undefined) {
return typeResult;
}
const bettingSubTypes = JSON.parse(type) as number[];
if (bettingSubTypes[0] < -1) {
return 'Invalid';
}
const typeResult = typeMap.value.get(type);
if (typeResult !== undefined) {
return typeResult;
}
const bettingSubTypes = JSON.parse(type) as number[];
if (bettingSubTypes[0] < -1) {
return "Invalid";
}
const textBettingType = bettingSubTypes.map((idx) => {
return bettingDetailInfo.value?.bettingInfo.candidates[idx].title;
}).join(', ');
typeMap.value.set(type, textBettingType);
return textBettingType;
const textBettingType = bettingSubTypes
.map((idx) => {
return bettingDetailInfo.value?.bettingInfo.candidates[idx].title;
})
.join(", ");
typeMap.value.set(type, textBettingType);
return textBettingType;
}
const pickedBetType = ref(new Set<number>());
const pickedBetTypeKey = ref('[]');
const pickedBetTypeKey = ref("[]");
const betPoint = ref(0);
const myBettings = ref(new Map<string, number>());
@@ -189,274 +200,264 @@ const winner = ref(new Set<number>());
const calculatedReward = ref<number[]>([]);
const calculatedSubAmount = ref(new Map<number, number>());
function calcMatchPointWithColor(type: string): [number, "green" | "yellow" | "red" | undefined] {
if (!info.value?.finished) {
return [0, undefined];
}
const bettingSubTypes = JSON.parse(type) as number[];
if (bettingSubTypes[0] < -1) {
return [0, undefined];
}
function calcMatchPointWithColor(type: string): [number, 'green' | 'yellow' | 'red' | undefined] {
if (!info.value?.finished) {
return [0, undefined];
}
const bettingSubTypes = JSON.parse(type) as number[];
if (bettingSubTypes[0] < -1) {
return [0, undefined];
let matchPoint = 0;
for (const subType of bettingSubTypes) {
if (winner.value.has(subType)) {
matchPoint += 1;
}
}
let matchPoint = 0;
for (const subType of bettingSubTypes) {
if (winner.value.has(subType)) {
matchPoint += 1;
}
if (info.value.isExclusive) {
if (matchPoint == info.value.selectCnt) {
return [matchPoint, "green"];
} else {
return [matchPoint, "red"];
}
}
if (info.value.isExclusive) {
if (matchPoint == info.value.selectCnt) {
return [matchPoint, 'green'];
}
else {
return [matchPoint, 'red'];
}
}
let color: 'green' | 'red' | 'yellow' = 'green';
if (matchPoint == 0) {
color = 'red';
}
else if (matchPoint < info.value.selectCnt) {
color = 'yellow';
}
return [matchPoint, color];
let color: "green" | "red" | "yellow" = "green";
if (matchPoint == 0) {
color = "red";
} else if (matchPoint < info.value.selectCnt) {
color = "yellow";
}
return [matchPoint, color];
}
function calcReward() {
if (info.value === undefined || bettingDetailInfo.value === undefined) {
throw 'no info';
}
const selectCnt = info.value.selectCnt;
const rewardAmount = new Array<number>(selectCnt).fill(0);
const subAmount = new Map<number, number>();
for (const [bettingTypeStr, amount] of bettingDetailInfo.value.bettingDetail) {
if (amount == 0) {
continue;
}
const [matchPoint,] = calcMatchPointWithColor(bettingTypeStr);
subAmount.set(matchPoint, (subAmount.get(matchPoint) ?? 0) + amount);
}
calculatedSubAmount.value = subAmount;
if (selectCnt == 1){
rewardAmount[selectCnt - 1] = bettingAmount.value;
calculatedReward.value = rewardAmount;
return;
}
if (info.value.isExclusive) {
rewardAmount[selectCnt - 1] = bettingAmount.value;
calculatedReward.value = rewardAmount;
return;
}
let remainRewardAmount = bettingAmount.value;
for (const matchPoint of range(selectCnt, 0, -1)) {
if (!subAmount.has(matchPoint)) {
continue;
}
const givenRewardAmount = remainRewardAmount / 2;
rewardAmount[matchPoint] = givenRewardAmount;
remainRewardAmount -= givenRewardAmount; // /2가 아니라 다른 값이 될 경우를 대비..
}
for (const matchPoint of range(1, selectCnt + 1)) {
if (!subAmount.has(matchPoint)) {
continue;
}
rewardAmount[matchPoint] += remainRewardAmount;
break;
if (info.value === undefined || bettingDetailInfo.value === undefined) {
throw "no info";
}
const selectCnt = info.value.selectCnt;
const rewardAmount = new Array<number>(selectCnt).fill(0);
const subAmount = new Map<number, number>();
for (const [bettingTypeStr, amount] of bettingDetailInfo.value.bettingDetail) {
if (amount == 0) {
continue;
}
const [matchPoint] = calcMatchPointWithColor(bettingTypeStr);
subAmount.set(matchPoint, (subAmount.get(matchPoint) ?? 0) + amount);
}
calculatedSubAmount.value = subAmount;
if (selectCnt == 1) {
rewardAmount[selectCnt - 1] = bettingAmount.value;
calculatedReward.value = rewardAmount;
return;
}
if (info.value.isExclusive) {
rewardAmount[selectCnt - 1] = bettingAmount.value;
calculatedReward.value = rewardAmount;
return;
}
let remainRewardAmount = bettingAmount.value;
for (const matchPoint of range(selectCnt, 0, -1)) {
if (!subAmount.has(matchPoint)) {
continue;
}
const givenRewardAmount = remainRewardAmount / 2;
rewardAmount[matchPoint] = givenRewardAmount;
remainRewardAmount -= givenRewardAmount; // /2가 아니라 다른 값이 될 경우를 대비..
}
for (const matchPoint of range(1, selectCnt + 1)) {
if (!subAmount.has(matchPoint)) {
continue;
}
rewardAmount[matchPoint] += remainRewardAmount;
break;
}
calculatedReward.value = rewardAmount;
}
async function loadBetting(bettingID: number) {
try {
const result = await SammoAPI.Betting.GetBettingDetail<BettingDetailResponse>({
betting_id: bettingID
});
year.value = result.year;
month.value = result.month;
yearMonth.value = joinYearMonth(result.year, result.month);
bettingDetailInfo.value = result;
info.value = result.bettingInfo;
try {
const result = await SammoAPI.Betting.GetBettingDetail<BettingDetailResponse>({
betting_id: bettingID,
});
year.value = result.year;
month.value = result.month;
yearMonth.value = joinYearMonth(result.year, result.month);
bettingDetailInfo.value = result;
info.value = result.bettingInfo;
partialBet.value.clear();
partialBet.value.clear();
const betSort = new Map<string, number>();
const betSort = new Map<string, number>();
let _bettingAmount = 0;
let adminBettingAmount = 0;
for (const [bettingType, amount] of result.bettingDetail) {
console.log(amount, typeof (amount));
let userBet = true;
const bettingSubTypes = JSON.parse(bettingType) as number[];
for (const bettingSubType of bettingSubTypes) {
if (bettingSubType < 0) {
userBet = false;
continue;
}
const oldValue = partialBet.value.get(bettingSubType) ?? 0;
partialBet.value.set(bettingSubType, oldValue + amount);
}
if (userBet) {
const oldValue = betSort.get(bettingType) ?? 0;
betSort.set(bettingType, oldValue + amount);
}
_bettingAmount += amount;
if (!userBet) {
adminBettingAmount += amount;
}
}
console.log(_bettingAmount);
bettingAmount.value = _bettingAmount;
pureBettingAmount.value = _bettingAmount - adminBettingAmount;
if (info.value.isExclusive || info.value.selectCnt == 1) {
maxBettingReward.value = _bettingAmount;
} else {
maxBettingReward.value = _bettingAmount / 2;
let _bettingAmount = 0;
let adminBettingAmount = 0;
for (const [bettingType, amount] of result.bettingDetail) {
console.log(amount, typeof amount);
let userBet = true;
const bettingSubTypes = JSON.parse(bettingType) as number[];
for (const bettingSubType of bettingSubTypes) {
if (bettingSubType < 0) {
userBet = false;
continue;
}
const oldValue = partialBet.value.get(bettingSubType) ?? 0;
partialBet.value.set(bettingSubType, oldValue + amount);
}
detailBet.value = Array.from(betSort.entries());
detailBet.value.sort(([, lhsVal], [, rhsVal]) => {
return rhsVal - lhsVal;
})
if (userBet) {
const oldValue = betSort.get(bettingType) ?? 0;
betSort.set(bettingType, oldValue + amount);
}
pickedBetType.value.clear();
pickedBetTypeKey.value = '[]';
myBettings.value.clear();
if (result.bettingInfo.winner) {
winner.value = new Set(result.bettingInfo.winner);
}
else {
winner.value.clear();
}
for (const [betType, amount] of result.myBetting) {
myBettings.value.set(betType, amount);
}
calcReward();
} catch (e) {
if (isString(e)) {
emit('reqToast', {
content: {
title: "에러",
body: e
},
options: {
variant: 'danger',
}
});
}
console.error(e);
_bettingAmount += amount;
if (!userBet) {
adminBettingAmount += amount;
}
}
console.log(_bettingAmount);
bettingAmount.value = _bettingAmount;
pureBettingAmount.value = _bettingAmount - adminBettingAmount;
if (info.value.isExclusive || info.value.selectCnt == 1) {
maxBettingReward.value = _bettingAmount;
} else {
maxBettingReward.value = _bettingAmount / 2;
}
detailBet.value = Array.from(betSort.entries());
detailBet.value.sort(([, lhsVal], [, rhsVal]) => {
return rhsVal - lhsVal;
});
pickedBetType.value.clear();
pickedBetTypeKey.value = "[]";
myBettings.value.clear();
if (result.bettingInfo.winner) {
winner.value = new Set(result.bettingInfo.winner);
} else {
winner.value.clear();
}
for (const [betType, amount] of result.myBetting) {
myBettings.value.set(betType, amount);
}
calcReward();
} catch (e) {
if (isString(e)) {
emit("reqToast", {
content: {
title: "에러",
body: e,
},
options: {
variant: "danger",
},
});
}
console.error(e);
}
}
void loadBetting(props.bettingID);
watch(() => props.bettingID, (newBettingID) => {
watch(
() => props.bettingID,
(newBettingID) => {
void loadBetting(newBettingID);
});
}
);
function toggleCandidate(idx: number) {
if (info.value === undefined) {
return;
}
if (bettingDetailInfo.value === undefined) {
return;
}
if (info.value.closeYearMonth < yearMonth.value) {
return;
}
if (info.value.finished) {
return;
}
const selectCnt = bettingDetailInfo.value.bettingInfo.selectCnt;
if (info.value === undefined) {
return;
}
if (bettingDetailInfo.value === undefined) {
return;
}
if (info.value.closeYearMonth < yearMonth.value) {
return;
}
if (info.value.finished) {
return;
}
const selectCnt = bettingDetailInfo.value.bettingInfo.selectCnt;
if (selectCnt == 1) {
pickedBetType.value.clear();
pickedBetType.value.add(idx);
pickedBetTypeKey.value = JSON.stringify([idx]);
return;
}
if (selectCnt == 1) {
pickedBetType.value.clear();
pickedBetType.value.add(idx);
pickedBetTypeKey.value = JSON.stringify([idx]);
return;
}
if (pickedBetType.value.has(idx)) {
pickedBetType.value.delete(idx);
}
else if (pickedBetType.value.size < selectCnt) {
pickedBetType.value.add(idx);
}
else {
emit('reqToast', {
content: {
title: '오류',
body: `이미 ${selectCnt}개를 선택했습니다.`,
},
options: {
variant: 'warning',
}
});
return;
}
if (pickedBetType.value.has(idx)) {
pickedBetType.value.delete(idx);
} else if (pickedBetType.value.size < selectCnt) {
pickedBetType.value.add(idx);
} else {
emit("reqToast", {
content: {
title: "오류",
body: `이미 ${selectCnt}개를 선택했습니다.`,
},
options: {
variant: "warning",
},
});
return;
}
const typeArr = Array.from(pickedBetType.value.values());
pickedBetTypeKey.value = JSON.stringify(typeArr.sort((lhs, rhs) => lhs - rhs));
const typeArr = Array.from(pickedBetType.value.values());
pickedBetTypeKey.value = JSON.stringify(typeArr.sort((lhs, rhs) => lhs - rhs));
}
async function submitBet(): Promise<void> {
const bettingInfo = info.value;
if (bettingInfo === undefined) {
return;
}
const bettingInfo = info.value;
if (bettingInfo === undefined) {
return;
const bettingID = bettingInfo.id;
const bettingType = JSON.parse(pickedBetTypeKey.value);
const amount = betPoint.value;
try {
await SammoAPI.Betting.Bet({
bettingID,
bettingType,
amount,
});
emit("reqToast", {
content: {
title: "완료",
body: "베팅했습니다",
},
options: {
variant: "success",
},
});
await loadBetting(bettingInfo.id);
} catch (e) {
if (isString(e)) {
emit("reqToast", {
content: {
title: "에러",
body: e,
},
options: {
variant: "danger",
},
});
}
const bettingID = bettingInfo.id;
const bettingType = JSON.parse(pickedBetTypeKey.value);
const amount = betPoint.value;
try {
await SammoAPI.Betting.Bet({
bettingID,
bettingType,
amount,
});
emit('reqToast', {
content: {
title: '완료',
body: '베팅했습니다',
},
options: {
variant: 'success'
}
});
await loadBetting(bettingInfo.id);
} catch (e) {
if (isString(e)) {
emit('reqToast', {
content: {
title: "에러",
body: e,
},
options: {
variant: "danger",
}
});
}
console.error(e);
}
console.error(e);
}
}
</script>
</script>
+62 -71
View File
@@ -1,27 +1,26 @@
<template>
<div class="articleFrame bg0">
<div class="bg1 row gx-0">
<div class="authorName center">{{ article.author }}</div>
<div class="col articleTitle center">{{ article.title }}</div>
<div class="col-2 col-md-1 date center">{{ article.date.slice(5, 16) }}</div>
<div class="authorName center">
{{ article.author }}
</div>
<div class="col articleTitle center">
{{ article.title }}
</div>
<div class="col-2 col-md-1 date center">
{{ article.date.slice(5, 16) }}
</div>
</div>
<div class="row gx-0 s-border-b">
<div class="col-2 col-md-1 authorIcon center">
<img
class="generalIcon"
width="64"
height="64"
:src="article.author_icon"
/>
<img class="generalIcon" width="64" height="64" :src="article.author_icon" />
</div>
<div class="col text">
{{ article.text }}
</div>
<div class="col text">{{ article.text }}</div>
</div>
<div class="commentList">
<board-comment
v-for="comment in article.comment"
:key="comment.no"
:comment="comment"
/>
<board-comment v-for="comment in article.comment" :key="comment.no" :comment="comment" />
</div>
<div class="row gx-0">
<div class="bg2 inputCommentHeader center d-grid">
@@ -29,86 +28,78 @@
</div>
<div class="col d-grid">
<input
v-model.trim="newCommentText"
class="commentText"
type="text"
maxlength="250"
placeholder="새 댓글 내용"
v-model.trim="newCommentText"
@keyup.enter="submitComment"
/>
</div>
<div class="col-2 col-md-1 d-grid">
<b-button class="submitComment" @click="submitComment" size="sm"
>등록</b-button
>
<b-button class="submitComment" size="sm" @click="submitComment"> 등록 </b-button>
</div>
</div>
</div>
</template>
<script lang="ts">
<script lang="ts" setup>
import type { BoardArticleItem } from "@/PageBoard.vue";
import BoardComment from "@/components/BoardComment.vue";
import { defineComponent, type PropType } from "vue";
import { ref, type PropType } from "vue";
import axios from "axios";
import { convertFormData } from "@util/convertFormData";
import type { InvalidResponse } from "@/defs";
export default defineComponent({
name: "BoardArticle",
components: {
BoardComment,
},
data() {
return {
newCommentText: "",
};
},
props: {
article: {
type: Object as PropType<BoardArticleItem>,
required: true,
},
},
emits: ["submit-comment"],
methods: {
async submitComment() {
const comment = this.newCommentText;
if (!comment) {
return;
}
const articleNo = this.article.no;
let result: InvalidResponse;
const newCommentText = ref("");
try {
const response = await axios({
url: "j_board_comment_add.php",
method: "post",
responseType: "json",
data: convertFormData({
articleNo: articleNo,
text: comment,
}),
});
result = response.data;
if (!result.result) {
throw result.reason;
}
} catch (e) {
console.error(e);
alert(`실패했습니다: ${e}`);
return;
}
this.newCommentText = "";
this.$emit("submit-comment");
},
const props = defineProps({
article: {
type: Object as PropType<BoardArticleItem>,
required: true,
},
});
const emit = defineEmits<{
(event: "submit-comment"): void;
}>();
async function submitComment() {
const comment = newCommentText.value;
if (!comment) {
return;
}
const articleNo = props.article.no;
let result: InvalidResponse;
try {
const response = await axios({
url: "j_board_comment_add.php",
method: "post",
responseType: "json",
data: convertFormData({
articleNo: articleNo,
text: comment,
}),
});
result = response.data;
if (!result.result) {
throw result.reason;
}
} catch (e) {
console.error(e);
alert(`실패했습니다: ${e}`);
return;
}
newCommentText.value = "";
emit("submit-comment");
}
</script>
<style>
td.text {
white-space: pre;
}
</style>
</style>
+13 -3
View File
@@ -1,8 +1,18 @@
<template>
<div class="row gx-0 comment s-border-b">
<div class="authorName center d-grid"><div class="align-self-center">{{ comment.author }}</div></div>
<div class="col text">{{ comment.text }}</div>
<div class="col-2 col-md-1 date center d-grid"><div class="align-self-center">{{ comment.date.slice(5, 16) }}</div></div>
<div class="authorName center d-grid">
<div class="align-self-center">
{{ comment.author }}
</div>
</div>
<div class="col text">
{{ comment.text }}
</div>
<div class="col-2 col-md-1 date center d-grid">
<div class="align-self-center">
{{ comment.date.slice(5, 16) }}
</div>
</div>
</div>
</template>
<script lang="ts">
+21 -32
View File
@@ -1,41 +1,30 @@
<template>
<div class="bg0" style="padding-top:20px;">
<button type="button" class="btn btn-sammo-base2 back_btn" @click="back">
돌아가기
</button>
<div></div>
<div class="bg0" style="padding-top: 20px">
<button type="button" class="btn btn-sammo-base2 back_btn" @click="back">돌아가기</button>
<div />
</div>
</template>
<script lang="ts">
import { defineComponent, type PropType } from "vue";
<script lang="ts" setup>
import type { PropType } from "vue";
import "@scss/game_bg.scss";
export default defineComponent({
methods: {
back(){
if(this.type === 'normal'){
location.href = './';
}
else if(this.type == 'chief'){
location.href = 'v_chiefCenter.php';
}
else{
//TODO: window.close하려면 부모창이 있어야함!
window.close();
}
}
},
props: {
type: {
type: String as PropType<"normal"|"chief"|"close">,
default: "normal",
required: false,
},
const props = defineProps({
type: {
type: String as PropType<"normal" | "chief" | "close">,
default: "normal",
required: false,
},
});
function back() {
if (props.type === "normal") {
location.href = "./";
} else if (props.type == "chief") {
location.href = "v_chiefCenter.php";
} else {
//TODO: window.close하려면 부모창이 있어야함!
window.close();
}
}
</script>
<style>
</style>
+166 -191
View File
@@ -7,15 +7,11 @@
:style="{
color: getNpcColor(officer.npcType ?? 0),
}"
>{{ officer.name }}</div>
>
{{ officer.name }}
</div>
</div>
<div
:class="[
'row',
'controlPad',
props.targetIsMe ? 'targetIsMe' : 'targetIsNotMe',
]"
>
<div :class="['row', 'controlPad', props.targetIsMe ? 'targetIsMe' : 'targetIsNotMe']">
<div class="col-3 col-md-12 order-md-last">
<div class="d-grid mb-1 py-1 only500px bg1 center">
<div
@@ -23,51 +19,50 @@
color: getNpcColor(officer.npcType ?? 0),
fontSize: '1.2em',
}"
>{{ officer.name }}</div>
>
{{ officer.name }}
</div>
<div>{{ officer.officerLevelText }}</div>
</div>
<div class="row gx-1 gy-1 py-1">
<div class="col-md-4 mx-0 mb-0 mt-1 d-grid">
<div class="alert alert-primary mb-0 center" style="padding: 0.5rem 0"><SimpleClock :serverTime="parseTime(props.date)" /></div>
<div class="alert alert-primary mb-0 center" style="padding: 0.5rem 0">
<SimpleClock :serverTime="parseTime(props.date)" />
</div>
</div>
<div class="col-md-4 d-grid">
<BButton
variant="secondary"
@click="isEditMode = !isEditMode"
>{{ isEditMode ? '일반 모드' : '고급 모드' }}</BButton>
<BButton variant="secondary" @click="isEditMode = !isEditMode">
{{ isEditMode ? "일반 모드" : "고급 모드" }}
</BButton>
</div>
<BDropdown class="col-md-4" text="반복">
<BDropdownItem
v-for="turnIdx in maxPushTurn"
:key="turnIdx"
@click="repeatNationCommand(turnIdx)"
>
{{
turnIdx
}}
<BDropdownItem v-for="turnIdx in maxPushTurn" :key="turnIdx" @click="repeatNationCommand(turnIdx)">
{{ turnIdx }}
</BDropdownItem>
</BDropdown>
<template v-if="isEditMode">
<BDropdown class="col-md-4" left text="범위">
<BDropdownItem @click="queryActionHelper.selectTurn()">해제</BDropdownItem>
<BDropdownItem @click="queryActionHelper.selectAll()">모든턴</BDropdownItem>
<BDropdownItem @click="queryActionHelper.selectStep(0, 2)">홀수턴</BDropdownItem>
<BDropdownItem @click="queryActionHelper.selectStep(1, 2)">짝수턴</BDropdownItem>
<BDropdownDivider></BDropdownDivider>
<BDropdownItem @click="queryActionHelper.selectTurn()"> 해제 </BDropdownItem>
<BDropdownItem @click="queryActionHelper.selectAll()"> 모든턴 </BDropdownItem>
<BDropdownItem @click="queryActionHelper.selectStep(0, 2)"> 홀수턴 </BDropdownItem>
<BDropdownItem @click="queryActionHelper.selectStep(1, 2)"> 짝수턴 </BDropdownItem>
<BDropdownDivider />
<BDropdownText v-for="spanIdx in [3, 4, 5, 6, 7]" :key="spanIdx">
{{ spanIdx }} 간격
<br />
<BButtonGroup>
<BButton
class="ignoreMe"
v-for="beginIdx in spanIdx"
:key="beginIdx"
class="ignoreMe"
@click="queryActionHelper.selectStep(beginIdx - 1, spanIdx)"
>{{ beginIdx }}</BButton>
>
{{ beginIdx }}
</BButton>
</BButtonGroup>
</BDropdownText>
</BDropdown>
@@ -79,7 +74,7 @@
@click.self="useStoredAction(actions)"
>
{{ actionKey }}
<BButton @click.prevent="deleteStoredActions(actionKey)" size="sm">삭제</BButton>
<BButton size="sm" @click.prevent="deleteStoredActions(actionKey)"> 삭제 </BButton>
</BDropdownItem>
</BDropdown>
@@ -90,34 +85,20 @@
:key="idx"
@click="void reserveCommandDirect([[Array.from(selectedTurnList.values()), action]])"
>
{{
action.brief
}}
{{ action.brief }}
</BDropdownItem>
</BDropdown>
</div>
</template>
<BDropdown class="col-md-6" split text="당기기" @click="pullNationCommandSingle">
<BDropdownItem
v-for="turnIdx in maxPushTurn"
:key="turnIdx"
@click="pushNationCommand(-turnIdx)"
>
{{
turnIdx
}}
<BDropdownItem v-for="turnIdx in maxPushTurn" :key="turnIdx" @click="pushNationCommand(-turnIdx)">
{{ turnIdx }}
</BDropdownItem>
</BDropdown>
<BDropdown class="col-md-6" split text="미루기" @click="pushNationCommandSingle">
<BDropdownItem
v-for="turnIdx in maxPushTurn"
:key="turnIdx"
@click="pushNationCommand(turnIdx)"
>
{{
turnIdx
}}
<BDropdownItem v-for="turnIdx in maxPushTurn" :key="turnIdx" @click="pushNationCommand(turnIdx)">
{{ turnIdx }}
</BDropdownItem>
</BDropdown>
</div>
@@ -134,28 +115,28 @@
}"
>
<CommandSelectForm
:commandList="commandList"
ref="commandQuickReserveForm"
@on-close="chooseQuickReserveCommand($event)"
:hideClose="false"
v-model:activatedCategory="activatedCategory"
:commandList="commandList"
:hideClose="false"
class="bg-dark"
style="position:absolute"
style="position: absolute"
@onClose="chooseQuickReserveCommand($event)"
/>
</div>
</div>
<div class="commandPad chiefReservedCommand">
<div :class="['commandTable', isEditMode ? 'editMode' : 'singleMode']">
<DragSelect
v-slot="{ selected }"
:style="rowGridStyle"
attribute="turnIdx"
:disabled="!isEditMode"
@dragStart="isDragSingle = true"
@dragDone="
isDragSingle = false;
queryActionHelper.selectTurn(...$event);
isDragSingle = false;
queryActionHelper.selectTurn(...$event);
"
v-slot="{ selected }"
>
<div
v-for="(turnObj, turnIdx) in reservedCommandList"
@@ -166,21 +147,22 @@ queryActionHelper.selectTurn(...$event);
backgroundColor: 'black',
whiteSpace: 'nowrap',
overflow: 'hidden',
color:
isDragSingle && selected.has(`${turnIdx}`) ? 'cyan' : undefined,
color: isDragSingle && selected.has(`${turnIdx}`) ? 'cyan' : undefined,
}"
>{{ turnObj.time }}</div>
>
{{ turnObj.time }}
</div>
</DragSelect>
<DragSelect
v-slot="{ selected }"
:style="{ ...rowGridStyle, display: isEditMode ? 'grid' : 'none' }"
attribute="turnIdx"
:disabled="!isEditMode"
@dragStart="isDragToggle = true"
@dragDone="
isDragToggle = false;
toggleTurn(...$event);
isDragToggle = false;
toggleTurn(...$event);
"
v-slot="{ selected }"
>
<div
v-for="(turnObj, turnIdx) in reservedCommandList"
@@ -194,90 +176,75 @@ toggleTurn(...$event);
isDragToggle && selected.has(`${turnIdx}`)
? 'light'
: selectedTurnList.has(turnIdx)
? 'info'
: selectedTurnList.size == 0 && prevSelectedTurnList.has(turnIdx)
? 'success'
: 'primary'
? 'info'
: selectedTurnList.size == 0 && prevSelectedTurnList.has(turnIdx)
? 'success'
: 'primary'
"
>{{ turnIdx + 1 }}</BButton>
>
{{ turnIdx + 1 }}
</BButton>
</div>
</DragSelect>
<div :style="rowGridStyle">
<div
v-for="(turnObj, turnIdx) in reservedCommandList"
:key="turnIdx"
class="turn_pad center"
>
<span
class="turn_text"
:style="turnObj.style"
v-b-tooltip.hover
:title="turnObj.tooltip"
v-html="turnObj.brief"
></span>
<div v-for="(turnObj, turnIdx) in reservedCommandList" :key="turnIdx" class="turn_pad center">
<span v-b-tooltip.hover class="turn_text" :style="turnObj.style" :title="turnObj.tooltip">
<!-- eslint-disable-next-line vue/no-v-html -->
<span v-html="turnObj.brief" />
</span>
</div>
</div>
<div :style="{ ...rowGridStyle, display: isEditMode ? 'none' : 'grid' }">
<div v-for="turnIdx in range(props.maxTurn)" :key="turnIdx" class="action_pad d-grid">
<BButton
:variant="(turnIdx % 2 == 0) ? 'secondary' : 'dark'"
:variant="turnIdx % 2 == 0 ? 'secondary' : 'dark'"
size="sm"
class="simple_action_btn bi bi-pencil"
@click="toggleQuickReserveForm(turnIdx)"
></BButton>
/>
</div>
</div>
</div>
<div style="position:relative">
<div style="position: relative">
<CommandSelectForm
:commandList="commandList"
ref="commandSelectForm"
@on-close="chooseCommand($event)"
v-model:activatedCategory="activatedCategory"
:commandList="commandList"
class="bg-dark"
:style="{ position: 'absolute', bottom: '0' }"
@onClose="chooseCommand($event)"
/>
</div>
<div class="row gx-0" v-if="isEditMode">
<div v-if="isEditMode" class="row gx-0">
<div class="col-5 col-md-6 d-grid">
<BDropdown left variant="info" text="선택한 턴을">
<BDropdownItem @click="clipboardCut">
<i class="bi bi-scissors"></i>&nbsp;잘라내기
</BDropdownItem>
<BDropdownItem @click="clipboardCopy">
<i class="bi bi-files"></i>&nbsp;복사하기
</BDropdownItem>
<BDropdownItem @click="clipboardCut"> <i class="bi bi-scissors" />&nbsp;잘라내기 </BDropdownItem>
<BDropdownItem @click="clipboardCopy"> <i class="bi bi-files" />&nbsp;복사하기 </BDropdownItem>
<BDropdownItem @click="clipboardPaste">
<i class="bi bi-clipboard-fill"></i>&nbsp;붙여넣기
<i class="bi bi-clipboard-fill" />&nbsp;붙여넣기
</BDropdownItem>
<BDropdownDivider />
<BDropdownItem @click="setStoredActions">
<i class="bi bi-bookmark-plus-fill"></i>&nbsp;보관하기
<i class="bi bi-bookmark-plus-fill" />&nbsp;보관하기
</BDropdownItem>
<BDropdownItem @click="subRepeatCommand">
<i class="bi bi-arrow-repeat"></i>&nbsp;반복하기
<i class="bi bi-arrow-repeat" />&nbsp;반복하기
</BDropdownItem>
<BDropdownDivider />
<BDropdownItem @click="eraseSelectedTurnList">
<i class="bi bi-eraser"></i>&nbsp;비우기
</BDropdownItem>
<BDropdownItem @click="eraseSelectedTurnList"> <i class="bi bi-eraser" />&nbsp;비우기 </BDropdownItem>
<BDropdownItem @click="eraseAndPullCommand">
<i class="bi bi-arrow-bar-up"></i>&nbsp;지우고 당기기
<i class="bi bi-arrow-bar-up" />&nbsp;지우고 당기기
</BDropdownItem>
<BDropdownItem @click="pushEmptyCommand">
<i class="bi bi-arrow-bar-down"></i>&nbsp;뒤로 밀기
<i class="bi bi-arrow-bar-down" />&nbsp;뒤로 밀기
</BDropdownItem>
<!-- 최근에 실행한 10 -->
</BDropdown>
</div>
<div class="col-7 col-md-6 d-grid">
<BButton
variant="light"
@click="toggleForm($event)"
:style="{ color: 'black' }"
>명령 선택 </BButton>
<BButton variant="light" :style="{ color: 'black' }" @click="toggleForm($event)"> 명령 선택 </BButton>
</div>
</div>
</div>
@@ -327,7 +294,6 @@ const props = defineProps({
turnTime: VueTypes.string.isRequired,
targetIsMe: VueTypes.bool.isRequired,
selectedTurn: {
type: Object as PropType<Set<number>>,
required: false,
@@ -338,15 +304,15 @@ const props = defineProps({
required: true,
},
commandList: {
type: Object as PropType<ChiefResponse['commandList']>,
type: Object as PropType<ChiefResponse["commandList"]>,
required: true,
},
officer: {
type: Object as PropType<ChiefResponse['chiefList'][0]>,
type: Object as PropType<ChiefResponse["chiefList"][0]>,
required: true,
}
})
},
});
const basicModeRowHeight = 30;
@@ -391,8 +357,8 @@ const isDragToggle = ref(false);
const autorun_limit = ref<number | null>(null);
const emit = defineEmits<{
(event: 'raiseReload'): void,
(event: 'update:selectedTurn', value: Set<number>): void,
(event: "raise-reload"): void;
(event: "update:selectedTurn", value: Set<number>): void;
}>();
function triggerUpdateCommandList(type?: string) {
@@ -442,10 +408,9 @@ async function repeatNationCommand(amount: number) {
alert(`실패했습니다: ${e}`);
return;
}
emit('raiseReload');
emit("raise-reload");
}
function pushNationCommandSingle(e: Event) {
//NOTE: split 구현에 버그가 있어서, 수동으로 구분해야함
if (isDropdownChildren(e)) {
@@ -470,10 +435,9 @@ async function pushNationCommand(amount: number) {
alert(`실패했습니다: ${e}`);
return;
}
emit('raiseReload');
emit("raise-reload");
}
const queryActionHelper = new QueryActionHelper(props.maxTurn);
const reservedCommandList = queryActionHelper.reservedCommandList;
const prevSelectedTurnList = queryActionHelper.prevSelectedTurnList;
@@ -481,15 +445,15 @@ const selectedTurnList = queryActionHelper.selectedTurnList;
async function reserveCommandDirect(args: [number[], TurnObj][], reload = true): Promise<boolean> {
const query: {
turnList: number[],
action: string,
arg: Args
turnList: number[];
action: string;
arg: Args;
}[] = [];
for (const [turnList, { action, arg }] of args) {
query.push({
turnList,
action,
arg
arg,
});
}
@@ -503,7 +467,7 @@ async function reserveCommandDirect(args: [number[], TurnObj][], reload = true):
}
if (reload) {
emit('raiseReload');
emit("raise-reload");
}
return true;
}
@@ -520,9 +484,7 @@ function updateCommandList() {
let nextTurnTime = new Date(turnTime);
const autorunLimitYearMonth = autorun_limit.value ?? yearMonth - 1;
const [autorunLimitYear, autorunLimitMonth] = parseYearMonth(
autorunLimitYearMonth
);
const [autorunLimitYear, autorunLimitMonth] = parseYearMonth(autorunLimitYearMonth);
for (const obj of props.turn) {
const [year, month] = parseYearMonth(yearMonth);
@@ -537,9 +499,7 @@ function updateCommandList() {
}
style.color = "#aaffff";
tooltip.push(
`자율 행동 기간: ${autorunLimitYear}${autorunLimitMonth}월까지`
);
tooltip.push(`자율 행동 기간: ${autorunLimitYear}${autorunLimitMonth}월까지`);
}
if (mb_strwidth(brief) > 22) {
@@ -550,10 +510,7 @@ function updateCommandList() {
...obj,
year,
month,
time: formatTime(
nextTurnTime,
props.turnTerm >= 5 ? "HH:mm" : "mm:ss"
),
time: formatTime(nextTurnTime, props.turnTerm >= 5 ? "HH:mm" : "mm:ss"),
tooltip: tooltip.length == 0 ? undefined : tooltip.join("\n"),
style,
});
@@ -590,7 +547,7 @@ async function reserveCommand() {
storedActionsHelper.pushRecentActions({
action: commandName,
brief: result.brief,
arg: {}
arg: {},
});
queryActionHelper.releaseSelectedTurnList();
@@ -599,7 +556,7 @@ async function reserveCommand() {
alert(`실패했습니다: ${e}`);
return;
}
emit("raiseReload");
emit("raise-reload");
}
function chooseCommand(val?: string) {
@@ -610,20 +567,16 @@ function chooseCommand(val?: string) {
void reserveCommand();
}
const emptyTurnObj: TurnObj = { action: '휴식', brief: '휴식', arg: {} };
const emptyTurnObj: TurnObj = { action: "휴식", brief: "휴식", arg: {} };
const storedActionsHelper = inject('storedNationActionsHelper') as StoredActionsHelper;
const storedActionsHelper = inject("storedNationActionsHelper") as StoredActionsHelper;
const recentActions = storedActionsHelper.recentActions;
const storedActions = storedActionsHelper.storedActions;
const activatedCategory = storedActionsHelper.activatedCategory;
async function eraseSelectedTurnList(releaseSelect = true): Promise<boolean> {
const result = await reserveCommandDirect([[
queryActionHelper.getSelectedTurnList(),
emptyTurnObj
]]);
const result = await reserveCommandDirect([[queryActionHelper.getSelectedTurnList(), emptyTurnObj]]);
if (releaseSelect) {
queryActionHelper.releaseSelectedTurnList();
}
@@ -669,7 +622,10 @@ async function subRepeatCommand(releaseSelect = true): Promise<boolean> {
const queryLength = selectedMaxTurnIdx - selectedMinTurnIdx + 1;
const rawActions = queryActionHelper.extractQueryActions();
const actions = queryActionHelper.amplifyQueryActions(rawActions, range(selectedMinTurnIdx, props.maxTurn, queryLength));
const actions = queryActionHelper.amplifyQueryActions(
rawActions,
range(selectedMinTurnIdx, props.maxTurn, queryLength)
);
const result = await reserveCommandDirect(actions);
if (releaseSelect) {
@@ -678,7 +634,6 @@ async function subRepeatCommand(releaseSelect = true): Promise<boolean> {
return result;
}
async function eraseAndPullCommand(releaseSelect = true): Promise<boolean> {
const reqTurnList = queryActionHelper.getSelectedTurnList();
const selectedMinTurnIdx = reqTurnList[0];
@@ -696,7 +651,6 @@ async function eraseAndPullCommand(releaseSelect = true): Promise<boolean> {
const actions: [number[], TurnObj][] = [];
const emptyTurnList: number[] = [];
for (const srcTurnIdx of range(selectedMinTurnIdx + queryLength, props.maxTurn)) {
@@ -705,11 +659,14 @@ async function eraseAndPullCommand(releaseSelect = true): Promise<boolean> {
emptyTurnList.push(srcTurnIdx - queryLength);
continue;
}
actions.push([[srcTurnIdx - queryLength], {
action: rawAction.action,
arg: rawAction.arg,
brief: rawAction.brief
}]);
actions.push([
[srcTurnIdx - queryLength],
{
action: rawAction.action,
arg: rawAction.arg,
brief: rawAction.brief,
},
]);
}
emptyTurnList.push(...range(props.maxTurn - queryLength, props.maxTurn));
@@ -739,7 +696,6 @@ async function pushEmptyCommand(releaseSelect = true): Promise<boolean> {
const actions: [number[], TurnObj][] = [];
const emptyTurnList: number[] = [];
for (const srcTurnIdx of range(selectedMinTurnIdx, props.maxTurn - queryLength)) {
@@ -748,11 +704,14 @@ async function pushEmptyCommand(releaseSelect = true): Promise<boolean> {
emptyTurnList.push(srcTurnIdx + queryLength);
continue;
}
actions.push([[srcTurnIdx + queryLength], {
action: rawAction.action,
arg: rawAction.arg,
brief: rawAction.brief
}]);
actions.push([
[srcTurnIdx + queryLength],
{
action: rawAction.action,
arg: rawAction.arg,
brief: rawAction.brief,
},
]);
}
emptyTurnList.push(...range(selectedMinTurnIdx, selectedMinTurnIdx + queryLength));
@@ -769,7 +728,7 @@ function setStoredActions() {
const actions = queryActionHelper.extractQueryActions();
const turnBrief = new Map<number, string>();
for (const [subTurnList, action] of actions) {
const actionName = action.action.split('_');
const actionName = action.action.split("_");
const actionShortName = actionName.length == 1 ? actionName[0] : actionName[1];
for (const turnIdx of subTurnList) {
turnBrief.set(turnIdx, actionShortName[0]);
@@ -779,10 +738,10 @@ function setStoredActions() {
const turnBriefStr = Array.from(turnBrief.entries())
.sort(([turnA], [turnB]) => turnA - turnB)
.map(([, action]) => action)
.join('');
.join("");
const nickName = trim(prompt('선택한 턴들의 별명을 지어주세요', turnBriefStr) ?? '');
if (nickName == '') {
const nickName = trim(prompt("선택한 턴들의 별명을 지어주세요", turnBriefStr) ?? "");
if (nickName == "") {
return;
}
@@ -791,12 +750,12 @@ function setStoredActions() {
}
function deleteStoredActions(actionKey: string) {
storedActionsHelper.deleteStoredActions(actionKey)
storedActionsHelper.deleteStoredActions(actionKey);
}
async function useStoredAction(rawActions: [number[], TurnObj][]) {
const reqTurnList = queryActionHelper.getSelectedTurnList();
const actions = queryActionHelper.amplifyQueryActions(rawActions, reqTurnList)
const actions = queryActionHelper.amplifyQueryActions(rawActions, reqTurnList);
const result = await reserveCommandDirect(actions);
queryActionHelper.releaseSelectedTurnList();
return result;
@@ -818,39 +777,57 @@ defineExpose({
clipboardPaste,
getQueryActionHelper,
getStoredActionHeler,
})
});
watch(() => props.date, () => {
triggerUpdateCommandList("date");
})
watch(() => props.year, () => {
triggerUpdateCommandList("year");
})
watch(() => props.month, () => {
triggerUpdateCommandList("month");
})
watch(() => props.turnTime, () => {
triggerUpdateCommandList("turnTime");
})
watch(() => props.commandList, () => {
triggerUpdateCommandList("commandList");
})
watch(() => props.selectedTurn, (val: Set<number>) => {
console.log(val);
if (val === selectedTurnList.value) {
console.log("pass!");
return;
watch(
() => props.date,
() => {
triggerUpdateCommandList("date");
}
selectedTurnList.value.clear();
for (const t of val.values()) {
selectedTurnList.value.add(t);
);
watch(
() => props.year,
() => {
triggerUpdateCommandList("year");
}
})
);
watch(
() => props.month,
() => {
triggerUpdateCommandList("month");
}
);
watch(
() => props.turnTime,
() => {
triggerUpdateCommandList("turnTime");
}
);
watch(
() => props.commandList,
() => {
triggerUpdateCommandList("commandList");
}
);
watch(
() => props.selectedTurn,
(val: Set<number>) => {
console.log(val);
if (val === selectedTurnList.value) {
console.log("pass!");
return;
}
selectedTurnList.value.clear();
for (const t of val.values()) {
selectedTurnList.value.add(t);
}
}
);
watch(selectedTurnList, () => {
console.log(selectedTurnList.value);
emit("update:selectedTurn", selectedTurnList.value);
})
});
const commandQuickReserveForm = ref<InstanceType<typeof CommandSelectForm> | null>(null);
const commandSelectForm = ref<InstanceType<typeof CommandSelectForm> | null>(null);
@@ -876,12 +853,11 @@ function toggleQuickReserveForm(turnIdx: number) {
}
const isEditMode = storedActionsHelper.isEditMode;
watch(isEditMode, newEditMode => {
watch(isEditMode, (newEditMode) => {
if (newEditMode) {
commandQuickReserveForm.value?.close();
currentQuickReserveTarget.value = -1;
}
else {
} else {
commandSelectForm.value?.close();
}
});
@@ -897,8 +873,7 @@ function toggleForm($event: Event): void {
onMounted(() => {
updateCommandList();
})
});
</script>
<style lang="scss">
@import "@scss/common/break_500px.scss";
@@ -990,4 +965,4 @@ onMounted(() => {
overflow: hidden;
}
}
</style>
</style>
+153 -155
View File
@@ -1,68 +1,58 @@
<template>
<div v-if="showForm" class="my-1">
<div class="commandCategory row gx-0 gy-1">
<div
class="categoryItem col-4 d-grid"
v-for="[categoryKey, { deco: categoryDeco }] of commandList"
:key="categoryKey"
>
<BButton
variant="success"
@click="chosenCategory = categoryKey"
:active="chosenCategory == categoryKey"
>{{ categoryDeco.altName ?? categoryDeco.name }}</BButton>
</div>
</div>
<div
class="commandList my-1"
:style="{
display: 'grid',
alignItems: 'self-start',
}"
>
<div
class="row gx-1 gy-1"
v-for="[category, { values }] of commandList"
:key="category"
:style="{ visibility: category == chosenCategory ? 'visible' : 'hidden', gridRow: '1', gridColumn: '1' }"
>
<div
class="col-6 d-grid"
v-for="commandItem of values"
:key="commandItem.value"
@click="close(commandItem.value)"
>
<div class="commandItem">
<p
:class="['center', 'my-0', commandItem.possible ? '' : 'commandImpossible']"
>
{{ commandItem.simpleName }}
<span
class="compensatePositive"
v-if="commandItem.compensation > 0"
></span>
<span
class="compensateNegative"
v-else-if="commandItem.compensation < 0"
></span>
</p>
<small class="center" :style="{ display: 'block' }">
{{
commandItem.title.startsWith(commandItem.simpleName)
? commandItem.title.substring(commandItem.simpleName.length)
: commandItem.title
}}
</small>
</div>
</div>
</div>
</div>
<div v-if="!hideClose" class="commandBottom row mt-1 mb-1">
<div class="offset-8 col-4 d-grid">
<BButton @click="close()">닫기</BButton>
</div>
</div>
<div v-if="showForm" class="my-1">
<div class="commandCategory row gx-0 gy-1">
<div
v-for="[categoryKey, { deco: categoryDeco }] of commandList"
:key="categoryKey"
class="categoryItem col-4 d-grid"
>
<BButton variant="success" :active="chosenCategory == categoryKey" @click="chosenCategory = categoryKey">
{{ categoryDeco.altName ?? categoryDeco.name }}
</BButton>
</div>
</div>
<div
class="commandList my-1"
:style="{
display: 'grid',
alignItems: 'self-start',
}"
>
<div
v-for="[category, { values }] of commandList"
:key="category"
class="row gx-1 gy-1"
:style="{ visibility: category == chosenCategory ? 'visible' : 'hidden', gridRow: '1', gridColumn: '1' }"
>
<div
v-for="commandItem of values"
:key="commandItem.value"
class="col-6 d-grid"
@click="close(commandItem.value)"
>
<div class="commandItem">
<p :class="['center', 'my-0', commandItem.possible ? '' : 'commandImpossible']">
{{ commandItem.simpleName }}
<span v-if="commandItem.compensation > 0" class="compensatePositive"></span>
<span v-else-if="commandItem.compensation < 0" class="compensateNegative"></span>
</p>
<small class="center" :style="{ display: 'block' }">
{{
commandItem.title.startsWith(commandItem.simpleName)
? commandItem.title.substring(commandItem.simpleName.length)
: commandItem.title
}}
</small>
</div>
</div>
</div>
</div>
<div v-if="!hideClose" class="commandBottom row mt-1 mb-1">
<div class="offset-8 col-4 d-grid">
<BButton @click="close()"> 닫기 </BButton>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import type { CommandItem } from "@/defs";
@@ -70,144 +60,152 @@ import { BButton } from "bootstrap-vue-3";
import { ref, type PropType, watch, onMounted } from "vue";
interface CategoryDecoration {
name: string,
altName?: string,
//icon?: string,
//color?: string,
//backgroundColor?: string,
name: string;
altName?: string;
//icon?: string,
//color?: string,
//backgroundColor?: string,
}
const props = defineProps({
categoryInfo: {
type: Object as PropType<Record<string, Omit<CategoryDecoration, 'name'>>>,
required: false,
categoryInfo: {
type: Object as PropType<Record<string, Omit<CategoryDecoration, "name">>>,
default: () => {
return {};
},
commandList: {
type: Object as PropType<{
category: string;
values: CommandItem[];
}[]>,
required: true,
},
anchor: {
type: String,
required: false,
default: '.commandSelectFormAnchor',
},
hideClose: {
type: Boolean,
required: false,
default: true,
},
activatedCategory: {
type: String,
required: false,
default: "",
}
})
required: false,
},
commandList: {
type: Object as PropType<
{
category: string;
values: CommandItem[];
}[]
>,
required: true,
},
anchor: {
type: String,
required: false,
default: ".commandSelectFormAnchor",
},
hideClose: {
type: Boolean,
required: false,
default: true,
},
activatedCategory: {
type: String,
required: false,
default: "",
},
});
const chosenCategory = ref<string>('-');
const chosenCategory = ref<string>("-");
const chosenSubList = ref<CommandItem[]>([]);
const categories = new Set(props.commandList.map(({ category }) => category));
watch(() => props.activatedCategory, (newValue) => {
watch(
() => props.activatedCategory,
(newValue) => {
chosenCategory.value = newValue;
})
}
);
const showForm = ref(false);
function convCategoryDeco(category: string): CategoryDecoration {
const itemInfo = props.categoryInfo?.[category];
if (!itemInfo) {
return {
name: category,
}
}
const itemInfo = props.categoryInfo?.[category];
if (!itemInfo) {
return {
name: category,
...itemInfo
name: category,
};
}
return {
name: category,
...itemInfo,
};
}
const commandList = ref(new Map<string, {
deco: CategoryDecoration,
values: CommandItem[],
}>());
const commandList = ref(
new Map<
string,
{
deco: CategoryDecoration;
values: CommandItem[];
}
>()
);
function updateCommandList(rawCommandList: typeof props.commandList) {
commandList.value.clear();
for (const { category, values } of rawCommandList) {
commandList.value.set(category, {
deco: convCategoryDeco(category),
values
});
}
commandList.value.clear();
for (const { category, values } of rawCommandList) {
commandList.value.set(category, {
deco: convCategoryDeco(category),
values,
});
}
}
watch(() => props.commandList, updateCommandList);
updateCommandList(props.commandList);
watch(chosenCategory, (category) => {
const itemInfo = commandList.value?.get(category);
if (itemInfo === undefined) {
console.error(`category 없음: ${category}`);
return;
}
chosenSubList.value = itemInfo.values;
if (props.activatedCategory !== category) {
emits('update:activatedCategory', category);
}
const itemInfo = commandList.value?.get(category);
if (itemInfo === undefined) {
console.error(`category 없음: ${category}`);
return;
}
chosenSubList.value = itemInfo.values;
if (props.activatedCategory !== category) {
emits("update:activatedCategory", category);
}
});
onMounted(() => {
if (!categories.has(props.activatedCategory)) {
chosenCategory.value = props.commandList[0].category;
}
else {
chosenCategory.value = props.activatedCategory;
}
if (!categories.has(props.activatedCategory)) {
chosenCategory.value = props.commandList[0].category;
} else {
chosenCategory.value = props.activatedCategory;
}
});
function show(): void {
showForm.value = true;
showForm.value = true;
}
function toggle(): void {
showForm.value = !showForm.value;
if (showForm.value === false) {
emits('onClose');
}
showForm.value = !showForm.value;
if (showForm.value === false) {
emits("onClose");
}
}
function close(category?: string): void {
showForm.value = false;
emits('onClose', category);
showForm.value = false;
emits("onClose", category);
}
const emits = defineEmits<{
(event: 'onClose', command?: string): void,
(event: 'update:activatedCategory', category: string): void,
(event: "onClose", command?: string): void;
(event: "update:activatedCategory", category: string): void;
}>();
defineExpose({
show,
close,
toggle
})
show,
close,
toggle,
});
</script>
<style scoped>
.commandItem {
border: gray 1px solid;
border-radius: 0.5em;
overflow: hidden;
cursor: pointer;
padding: 0.1em;
margin: 0;
border: gray 1px solid;
border-radius: 0.5em;
overflow: hidden;
cursor: pointer;
padding: 0.1em;
margin: 0;
}
</style>
</style>
+21 -34
View File
@@ -6,8 +6,7 @@
userSelect: disabled ? undefined : 'none',
overflow: 'hidden',
touchAction: disabled ? undefined : 'none',
}
"
}"
:class="{ disabledDrag: disabled }"
>
<slot :selected="intersected" />
@@ -16,14 +15,7 @@
<script lang="ts">
/// https://github.com/andi23rosca/drag-select-vue/blob/master/src/DragSelect.vue
import {
defineComponent,
ref,
watch,
onMounted,
onBeforeUnmount,
type PropType,
} from "vue";
import { defineComponent, ref, watch, onMounted, onBeforeUnmount, type PropType } from "vue";
import VueTypes from "vue-types";
function getDimensions(p1: coord, p2: coord): rect {
@@ -47,14 +39,8 @@ type rect = { width: number; height: number };
export default defineComponent({
props: {
attribute: VueTypes.string.isRequired,
color: {
...VueTypes.string.def("#4299E1"),
required: false,
},
opacity: {
...VueTypes.number.def(0.7),
required: false,
},
color: VueTypes.string.def("#4299E1"),
opacity: VueTypes.number.def(0.7),
modelValue: {
type: Object as PropType<Set<string>>,
required: false,
@@ -64,7 +50,7 @@ export default defineComponent({
type: Boolean,
required: false,
default: false,
}
},
},
emits: ["update:modelValue", "dragDone", "dragStart"],
setup(props, { emit }) {
@@ -194,20 +180,22 @@ export default defineComponent({
isMine = false;
}
watch(() => props.disabled, disabledNext => {
if (disabledNext) {
uContainer.removeEventListener("mousedown", startDrag);
uContainer.removeEventListener("touchstart", touchStart);
document.removeEventListener("mouseup", endDrag);
document.removeEventListener("touchend", endDrag);
watch(
() => props.disabled,
(disabledNext) => {
if (disabledNext) {
uContainer.removeEventListener("mousedown", startDrag);
uContainer.removeEventListener("touchstart", touchStart);
document.removeEventListener("mouseup", endDrag);
document.removeEventListener("touchend", endDrag);
} else {
uContainer.addEventListener("mousedown", startDrag);
uContainer.addEventListener("touchstart", touchStart);
document.addEventListener("mouseup", endDrag);
document.addEventListener("touchend", endDrag);
}
}
else {
uContainer.addEventListener("mousedown", startDrag);
uContainer.addEventListener("touchstart", touchStart);
document.addEventListener("mouseup", endDrag);
document.addEventListener("touchend", endDrag);
}
});
);
if (!props.disabled) {
uContainer.addEventListener("mousedown", startDrag);
@@ -216,7 +204,6 @@ export default defineComponent({
document.addEventListener("touchend", endDrag);
}
onBeforeUnmount(() => {
uContainer.removeEventListener("mousedown", startDrag);
uContainer.removeEventListener("touchstart", touchStart);
@@ -231,4 +218,4 @@ export default defineComponent({
};
},
});
</script>
</script>
+11 -12
View File
@@ -1,21 +1,18 @@
<template>
<div
:id="uuid"
:class="['world_map', `map_theme_${mapTheme}`, 'draw_required']"
>
<div :id="uuid" :class="['world_map', `map_theme_${mapName}`, 'draw_required']">
<div
class="map_title obj_tooltip"
data-bs-toggle="tooltip"
data-bs-placement="top"
data-tooltip-class="map_title_tooltiptext"
>
<span class="map_title_text"> </span>
<span class="tooltiptext"></span>
<span class="map_title_text" />
<span class="tooltiptext" />
</div>
<div class="map_body">
<div class="map_bglayer1"></div>
<div class="map_bglayer2"></div>
<div class="map_bgroad"></div>
<div class="map_bglayer1" />
<div class="map_bglayer2" />
<div class="map_bgroad" />
<div class="map_button_stack">
<button
type="button"
@@ -38,8 +35,8 @@
</div>
</div>
<div class="city_tooltip">
<div class="city_name"></div>
<div class="nation_name"></div>
<div class="city_name" />
<div class="nation_name" />
</div>
</div>
</template>
@@ -51,7 +48,7 @@ import { v4 as uuidv4 } from "uuid";
export type { MapCityParsed };
export default defineComponent({
props: {
mapTheme: {
mapName: {
type: String,
required: true,
},
@@ -59,6 +56,7 @@ export default defineComponent({
clickableAll: { type: Boolean, default: undefined, required: false },
selectCallback: {
type: Function as PropType<loadMapOption["selectCallback"]>,
default: undefined,
required: false,
},
hrefTemplate: { type: String, default: undefined, required: false },
@@ -88,6 +86,7 @@ export default defineComponent({
modelValue: {
type: Object as PropType<MapCityParsed>,
default: undefined,
required: false,
},
},
+13 -11
View File
@@ -1,32 +1,32 @@
<template>
<div class="row form-group number-input-with-info">
<label v-if="!right" class="col-6 col-form-label ">{{ title }}</label>
<label v-if="!right" class="col-6 col-form-label">{{ title }}</label>
<div class="col-6">
<input
ref="input"
v-model="rawValue"
type="number"
:step="step ?? undefined"
v-model="rawValue"
class="form-control f_tnum"
:min="min ?? undefined"
:max="max ?? undefined"
:style="{ display: editmode ? undefined : 'none' }"
@blur="onBlurNumber"
@input="updateValue"
:style="{ display: editmode ? undefined : 'none' }"
/>
<input
type="text"
class="form-control f_tnum"
:readonly="readonly"
:value="printValue"
@focus="onFocusText"
:style="{ display: !editmode ? undefined : 'none' }"
@focus="onFocusText"
/>
</div>
<label v-if="right" class="col-6 col-form-label">{{ title }}</label>
</div>
<div style="text-align: right">
<small class="form-text text-muted"><slot></slot></small>
<small class="form-text text-muted"><slot /></small>
</div>
</template>
<script lang="ts">
@@ -56,10 +56,12 @@ export default defineComponent({
},
max: {
type: Number,
default: undefined,
required: false,
},
step: {
type: Number,
default: undefined,
required: false,
},
modelValue: {
@@ -70,7 +72,7 @@ export default defineComponent({
type: Boolean,
required: false,
default: false,
}
},
},
emits: ["update:modelValue"],
data() {
@@ -80,15 +82,15 @@ export default defineComponent({
printValue: this.modelValue.toLocaleString(),
};
},
watch:{
modelValue: function(newVal:number){
watch: {
modelValue: function (newVal: number) {
this.rawValue = newVal;
this.printValue = newVal.toLocaleString();
}
},
},
methods: {
updateValue() {
if(this.readonly){
if (this.readonly) {
return;
}
if (this.int) {
@@ -102,7 +104,7 @@ export default defineComponent({
this.printValue = this.rawValue.toLocaleString();
},
onFocusText() {
if(this.readonly){
if (this.readonly) {
return;
}
this.editmode = true;
+25 -22
View File
@@ -1,31 +1,34 @@
<template>
<span class="time-zone">{{serverNow}}</span>
<span class="time-zone">{{ serverNow }}</span>
</template>
<script lang="ts" setup>
import { addMilliseconds } from 'date-fns';
import { type PropType, ref, onMounted, watch } from 'vue';
import { formatTime } from '@/util/formatTime';
import { addMilliseconds } from "date-fns";
import { type PropType, ref, onMounted, watch } from "vue";
import { formatTime } from "@/util/formatTime";
const props = defineProps({
serverTime: {
type: Object as PropType<Date>,
required: false,
default: new Date(),
},
timeFormat: {
type: String,
required: false,
default: 'HH:mm:ss'
}
})
serverTime: {
type: Object as PropType<Date>,
required: false,
default: new Date(),
},
timeFormat: {
type: String,
required: false,
default: "HH:mm:ss",
},
});
const timeDiff = ref(0);
const serverNow = ref('');
const serverNow = ref("");
watch(()=>props.serverTime, (newValue)=>{
watch(
() => props.serverTime,
(newValue) => {
const clientNow = new Date();
timeDiff.value = newValue.getTime() - clientNow.getTime();
});
}
);
function updateNow() {
const serverNowObj = addMilliseconds(new Date(), timeDiff.value);
@@ -36,8 +39,8 @@ function updateNow() {
}
onMounted(() => {
const clientNow = new Date();
timeDiff.value = props.serverTime.getTime() - clientNow.getTime();
updateNow();
const clientNow = new Date();
timeDiff.value = props.serverTime.getTime() - clientNow.getTime();
updateNow();
});
</script>
</script>
+326 -346
View File
@@ -1,53 +1,54 @@
<template>
<b-button-toolbar key-nav v-if="editable && editor" class="bg-dark">
<b-button-group class="mx-1">
<b-button
@click="editor.commands.undo()"
<BButtonToolbar v-if="editable && editor" key-nav class="bg-dark">
<BButtonGroup class="mx-1">
<BButton v-b-tooltip.hover title="되돌리기" @click="editor?.commands.undo()">
<i class="bi bi-arrow-90deg-left" />
</BButton>
<BButton v-b-tooltip.hover title="재실행" @click="editor?.commands.redo()">
<i class="bi bi-arrow-90deg-right" />
</BButton>
</BButtonGroup>
<BButtonGroup class="mx-1">
<BButton
v-b-tooltip.hover
title="되돌리기"
><i class="bi bi-arrow-90deg-left"></i
></b-button>
<b-button @click="editor.commands.redo()" v-b-tooltip.hover title="재실행"
><i class="bi bi-arrow-90deg-right"></i
></b-button>
</b-button-group>
<b-button-group class="mx-1">
<b-button
@click="editor.chain().focus().toggleBold().run()"
:class="{ 'is-active': editor.isActive('bold') }"
v-b-tooltip.hover
title="진하게"
><i class="bi bi-type-bold"></i
></b-button>
<b-button
@click="editor.chain().focus().toggleItalic().run()"
@click="editor?.chain().focus().toggleBold().run()"
>
<i class="bi bi-type-bold" />
</BButton>
<BButton
v-b-tooltip.hover
:class="{ 'is-active': editor.isActive('italic') }"
v-b-tooltip.hover
title="기울이기"
><i class="bi bi-type-italic"></i
></b-button>
<b-button
@click="editor.chain().focus().toggleUnderline().run()"
:class="{ 'is-active': editor.isActive('underline') }"
@click="editor?.chain().focus().toggleItalic().run()"
>
<i class="bi bi-type-italic" />
</BButton>
<BButton
v-b-tooltip.hover
:class="{ 'is-active': editor.isActive('underline') }"
title="밑줄"
><i class="bi bi-type-underline"></i
></b-button>
@click="editor?.chain().focus().toggleUnderline().run()"
>
<i class="bi bi-type-underline" />
</BButton>
<!-- 효과 지우기 -->
</b-button-group>
</BButtonGroup>
<b-button-group class="mx-1">
<b-dropdown>
<BButtonGroup class="mx-1">
<BDropdown>
<template #button-content> 크기 </template>
<b-dropdown-item @click="editor.chain().focus().unsetFontSize().run()"
><span>기본</span></b-dropdown-item
>
<b-dropdown-divider />
<b-dropdown-item
<BDropdownItem @click="editor?.chain().focus().unsetFontSize().run()">
<span>기본</span>
</BDropdownItem>
<BDropdownDivider />
<BDropdownItem
v-for="sizeItem in fontSize"
:key="sizeItem"
@click="editor.chain().focus().setFontSize(sizeItem).run()"
><span
@click="editor?.chain().focus().setFontSize(sizeItem).run()"
>
<span
:style="{
'font-size': sizeItem,
'text-decoration': editor.isActive('textStyle', {
@@ -57,235 +58,230 @@
: undefined,
}"
>{{ sizeItem }}</span
></b-dropdown-item
>
</b-dropdown>
>
</BDropdownItem>
</BDropdown>
<!-- 글꼴 -->
</b-button-group>
</BButtonGroup>
<b-button-group class="mx-1">
<b-button
@click="editor.chain().focus().toggleStrike().run()"
:class="{ 'is-active': editor.isActive('strike') }"
<BButtonGroup class="mx-1">
<BButton
v-b-tooltip.hover
:class="{ 'is-active': editor.isActive('strike') }"
title="가로선"
><i class="bi bi-type-strikethrough"></i
></b-button>
@click="editor?.chain().focus().toggleStrike().run()"
>
<i class="bi bi-type-strikethrough" />
</BButton>
<!-- 윗첨자, 아랫첨자 -->
</b-button-group>
</BButtonGroup>
<b-button-group class="mx-1">
<b-button
@click="
editor.chain().focus().unsetColor().unsetBackgroundColor().run()
"
<BButtonGroup class="mx-1">
<BButton
v-b-tooltip.hover
title="색상 취소"
><i class="bi bi-droplet"></i
></b-button>
@click="editor?.chain().focus().unsetColor().unsetBackgroundColor().run()"
>
<i class="bi bi-droplet" />
</BButton>
<input
v-b-tooltip.hover
type="color"
class="form-control form-control-color"
:value="
colorConvert(editor.getAttributes('textStyle').color, '#ffffff')
"
@input="editor.chain().focus().setColor(($event.target as HTMLInputElement).value).run()"
v-b-tooltip.hover
:value="colorConvert(editor.getAttributes('textStyle').color, '#ffffff')"
title="글자색"
@input="editor?.chain().focus().setColor(($event.target as HTMLInputElement).value).run()"
/>
<input
v-b-tooltip.hover
type="color"
class="form-control form-control-color"
:value="
colorConvert(
editor.getAttributes('textStyle').backgroundColor,
'#000000'
)
"
@input="
editor.chain().focus().setBackgroundColor(($event.target as HTMLInputElement).value).run()
"
v-b-tooltip.hover
:value="colorConvert(editor.getAttributes('textStyle').backgroundColor, '#000000')"
title="배경색"
@input="
editor?.chain().focus().setBackgroundColor(($event.target as HTMLInputElement).value).run()
"
/>
</b-button-group>
</BButtonGroup>
<b-button-group class="mx-1">
<b-button
v-b-tooltip.hover
@click="showImageModal = true"
title="이미지 추가"
><i class="bi bi-image"></i
></b-button>
<BButtonGroup class="mx-1">
<BButton v-b-tooltip.hover title="이미지 추가" @click="showImageModal = true">
<i class="bi bi-image" />
</BButton>
<!-- 이미지추가 -->
<!-- 링크 -->
<!-- 영상링크 -->
<!-- -->
<!-- 구분선 삽입 -->
<b-button
@click="editor.chain().focus().setHorizontalRule().run()"
v-b-tooltip.hover
title="구분선"
><i class="bi bi-hr"></i
></b-button>
</b-button-group>
<BButton v-b-tooltip.hover title="구분선" @click="editor?.chain().focus().setHorizontalRule().run()">
<i class="bi bi-hr" />
</BButton>
</BButtonGroup>
<b-button-group class="mx-1">
<BButtonGroup class="mx-1">
<!-- 글머리 기호 -->
<!-- 번호 매기기 -->
<b-button
@click="editor.chain().focus().setTextAlign('left').run()"
<BButton
v-b-tooltip.hover
:class="{ 'is-active': editor.isActive({ textAlign: 'left' }) }"
v-b-tooltip.hover
title="왼쪽 정렬"
><i class="bi bi-text-left"></i
></b-button>
<b-button
@click="editor.chain().focus().setTextAlign('center').run()"
@click="editor?.chain().focus().setTextAlign('left').run()"
>
<i class="bi bi-text-left" />
</BButton>
<BButton
v-b-tooltip.hover
:class="{ 'is-active': editor.isActive({ textAlign: 'center' }) }"
v-b-tooltip.hover
title="가운데 정렬"
><i class="bi bi-text-center"></i
></b-button>
<b-button
@click="editor.chain().focus().setTextAlign('right').run()"
:class="{ 'is-active': editor.isActive({ textAlign: 'right' }) }"
@click="editor?.chain().focus().setTextAlign('center').run()"
>
<i class="bi bi-text-center" />
</BButton>
<BButton
v-b-tooltip.hover
:class="{ 'is-active': editor.isActive({ textAlign: 'right' }) }"
title="오른쪽 정렬"
><i class="bi bi-text-right"></i
></b-button>
@click="editor?.chain().focus().setTextAlign('right').run()"
>
<i class="bi bi-text-right" />
</BButton>
<!-- 문단정렬(, , , )(내어, 들여) -->
</b-button-group>
</BButtonGroup>
<b-button-group class="mx-1"> </b-button-group>
<BButtonGroup class="mx-1" />
<b-button-group class="mx-1">
<BButtonGroup class="mx-1">
<!-- 줄간격 (1.0, 1.2, 1.4, 1.5, 1.6, 1.8, 2.0, 3.0) -->
</b-button-group>
</BButtonGroup>
<b-button-group class="mx-1">
<BButtonGroup class="mx-1">
<!-- 원본 코드 -->
</b-button-group>
</b-button-toolbar>
<bubble-menu
:tippy-options="{ animation: false, maxWidth: 600 }"
:editor="editor"
</BButtonGroup>
</BButtonToolbar>
<BubbleMenu
v-if="editable && editor"
v-show="editor.isActive('custom-image')"
:tippyOptions="{ animation: false, maxWidth: 600 }"
:editor="editor"
>
<b-button-toolbar>
<b-button-group class="mx-1">
<b-button
@click="editor.chain().focus().setImageEx({ size: 'small' }).run()"
<BButtonToolbar>
<BButtonGroup class="mx-1">
<BButton
v-b-tooltip.hover
:class="{
'is-active': editor.isActive('custom-image', {
size: 'small',
}),
f_frac: true,
}"
v-b-tooltip.hover
title="1/4 너비로 채우기"
>1/4</b-button
@click="editor?.chain().focus().setImageEx({ size: 'small' }).run()"
>
<b-button
@click="editor.chain().focus().setImageEx({ size: 'medium' }).run()"
1/4
</BButton>
<BButton
v-b-tooltip.hover
:class="{
'is-active': editor.isActive('custom-image', {
size: 'medium',
}),
f_frac: true,
}"
v-b-tooltip.hover
title="1/2 너비로 채우기"
>1/2</b-button
@click="editor?.chain().focus().setImageEx({ size: 'medium' }).run()"
>
<b-button
@click="editor.chain().focus().setImageEx({ size: 'large' }).run()"
1/2
</BButton>
<BButton
v-b-tooltip.hover
:class="{
'is-active': editor.isActive('custom-image', {
size: 'large',
}),
f_frac: true,
}"
v-b-tooltip.hover
title="가득 채우기"
>1</b-button
@click="editor?.chain().focus().setImageEx({ size: 'large' }).run()"
>
<b-button
@click="editor.chain().focus().setImageEx({ size: 'original' }).run()"
1
</BButton>
<BButton
:class="{
'is-active': editor.isActive('custom-image', {
size: 'original',
}),
}"
>원본</b-button
@click="editor?.chain().focus().setImageEx({ size: 'original' }).run()"
>
</b-button-group>
<b-button-group class="mx-1">
<b-button
@click="
editor.chain().focus().setImageEx({ align: 'float-left' }).run()
"
원본
</BButton>
</BButtonGroup>
<BButtonGroup class="mx-1">
<BButton
v-b-tooltip.hover
:class="{
'is-active': editor.isActive('custom-image', {
float: 'float-left',
}),
}"
v-b-tooltip.hover
title="왼쪽으로 붙이기"
><i class="bi bi-chevron-bar-left"></i
></b-button>
<b-button
@click="editor.chain().focus().setImageEx({ align: 'left' }).run()"
@click="editor?.chain().focus().setImageEx({ align: 'float-left' }).run()"
>
<i class="bi bi-chevron-bar-left" />
</BButton>
<BButton
v-b-tooltip.hover
:class="{
'is-active': editor.isActive('custom-image', {
float: 'left',
}),
}"
v-b-tooltip.hover
title="왼쪽으로"
><i class="bi bi-align-start"></i
></b-button>
<b-button
@click="editor.chain().focus().setImageEx({ align: 'center' }).run()"
@click="editor?.chain().focus().setImageEx({ align: 'left' }).run()"
>
<i class="bi bi-align-start" />
</BButton>
<BButton
v-b-tooltip.hover
:class="{
'is-active': editor.isActive('custom-image', {
float: 'center',
}),
}"
v-b-tooltip.hover
title="가운데로"
><i class="bi bi-align-center"></i
></b-button>
<b-button
@click="editor.chain().focus().setImageEx({ align: 'right' }).run()"
@click="editor?.chain().focus().setImageEx({ align: 'center' }).run()"
>
<i class="bi bi-align-center" />
</BButton>
<BButton
v-b-tooltip.hover
:class="{
'is-active': editor.isActive('custom-image', {
float: 'right',
}),
}"
v-b-tooltip.hover
title="오른쪽으로 붙이기"
><i class="bi bi-align-end"></i
></b-button>
<b-button
@click="
editor.chain().focus().setImageEx({ align: 'float-right' }).run()
"
@click="editor?.chain().focus().setImageEx({ align: 'right' }).run()"
>
<i class="bi bi-align-end" />
</BButton>
<BButton
v-b-tooltip.hover
:class="{
'is-active': editor.isActive('custom-image', {
float: 'float-right',
}),
}"
v-b-tooltip.hover
title="오른쪽으로 붙이기"
><i class="bi bi-chevron-bar-right"></i
></b-button>
</b-button-group>
</b-button-toolbar>
</bubble-menu>
<editor-content :editor="editor" class="tiptap-editor" />
<b-modal
@click="editor?.chain().focus().setImageEx({ align: 'float-right' }).run()"
>
<i class="bi bi-chevron-bar-right" />
</BButton>
</BButtonGroup>
</BButtonToolbar>
</BubbleMenu>
<EditorContent :editor="editor" class="tiptap-editor" />
<BModal
v-model="showImageModal"
title="이미지 추가"
okTitle="추가"
@@ -295,7 +291,7 @@
@hidden="resetModal"
>
<div class="bg-light text-dark">
<b-form-group
<BFormGroup
label-cols-sm="4"
label-cols-lg="3"
content-cols-sm
@@ -306,14 +302,14 @@
:label-for="`${uuid}_image_upload`"
>
<input
:id="`${uuid}_image_upload`"
class="form-control"
type="file"
:id="`${uuid}_image_upload`"
@change="chooseImage"
accept=".jpg,.jpeg,.png,.gif,.webp"
@change="chooseImage"
/>
</b-form-group>
<b-form-group
</BFormGroup>
<BFormGroup
label-cols-sm="4"
label-cols-lg="3"
content-cols-sm
@@ -323,15 +319,15 @@
label-align="right"
:label-for="`${uuid}_image_link`"
>
<b-form-input v-model="imageLink"></b-form-input>
</b-form-group>
<BFormInput v-model="imageLink" />
</BFormGroup>
</div>
</b-modal>
</BModal>
</template>
<script lang="ts">
<script lang="ts" setup>
//import "@scss/common/bootstrap5.scss";
import { defineComponent } from "vue";
import { onBeforeUnmount, onMounted, ref, watch } from "vue";
import { Editor, EditorContent, BubbleMenu } from "@tiptap/vue-3";
import { FontSize } from "@/tiptap-ext/FontSize";
import StarterKit from "@tiptap/starter-kit";
@@ -351,6 +347,8 @@ import {
BDropdownItem,
BDropdownDivider,
BModal,
BFormGroup,
BFormInput,
} from "bootstrap-vue-3";
import { v4 as uuidv4 } from "uuid";
import { unwrap } from "@/util/unwrap";
@@ -359,178 +357,160 @@ import { isObject, isString } from "lodash";
import type { AxiosError } from "axios";
import { SammoAPI } from "@/SammoAPI";
const compoment = defineComponent({
components: {
EditorContent,
BubbleMenu,
BModal,
BButtonGroup,
BButtonToolbar,
BButton,
BDropdown,
BDropdownItem,
BDropdownDivider,
const props = defineProps({
modelValue: {
type: String,
default: "",
},
emits: ["ready", "update:modelValue"],
methods: {
unwrap,
chooseImage(e: Event) {
const target = unwrap(e.target) as HTMLInputElement;
this.imageUploadFiles = target.files;
},
colorConvert(val: string | undefined, defaultVal: string) {
if (!val) {
return defaultVal;
}
if (val.startsWith("rgb")) {
const rgb = val.split("(")[1].split(")")[0].split(",");
const vals: string[] = [];
for (const subColor of rgb) {
const hexSubColor = parseInt(subColor).toString(16);
if (hexSubColor.length == 1) {
vals.push("0");
}
vals.push(hexSubColor);
}
return `#${vals.join("")}`;
}
return val;
},
async tryAddImage(bvModalEvt: Event) {
if (this.imageUploadFiles === null || this.imageUploadFiles.length == 0) {
this.addImageLink(bvModalEvt);
return;
}
const targetImage = unwrap(this.imageUploadFiles.item(0));
let imageResult: {
result: true;
path: string;
};
try {
const base64Binary = await getBase64FromFileObject(targetImage);
imageResult = await SammoAPI.Misc.UploadImage({
imageData: base64Binary,
});
} catch (e) {
if (isString(e)) {
alert(e);
bvModalEvt.preventDefault();
}
if (isObject(e) && "response" in e) {
const axiosErr = e as AxiosError;
if (axiosErr.response?.status === 413) {
alert("허용 용량을 초과했습니다.");
bvModalEvt.preventDefault();
}
}
console.error(e);
return false;
}
const imagePath = imageResult.path;
this.editor.chain().focus().setImageEx({ src: imagePath }).run();
},
addImageLink(bvModalEvt: Event) {
if (!this.imageLink) {
alert("업로드할 이미지를 선택하거나, 이미지 주소를 입력해주세요.");
bvModalEvt.preventDefault();
return false;
}
this.editor.chain().focus().setImageEx({ src: this.imageLink }).run();
},
resetModal() {
this.imageLink = "";
this.imageUploadFiles = null;
},
},
props: {
modelValue: {
type: String,
default: "",
},
editable: {
type: Boolean,
default: true,
},
},
data() {
return {
uuid: uuidv4(),
editor: null as unknown as InstanceType<typeof Editor>,
fontList: ["Pretendard", "맑은 고딕", "궁서", "돋움"],
fontSize: [
"8px",
"10px",
"12px",
"14px",
"18px",
"22px",
"28px",
"36px",
"48px",
"72px",
],
imageUploadFiles: null as FileList | null,
imageLink: "",
showImageModal: false,
};
},
watch: {
modelValue(value: string) {
const isSame = this.editor.getHTML() === value;
if (isSame) {
return;
}
this.editor.commands.setContent(value, false);
},
editable(value: boolean) {
this.editor.options.editable = value;
if (value == true) {
this.editor.commands.focus();
}
},
},
mounted() {
const editor = new Editor({
extensions: [
StarterKit,
Underline,
FontSize,
TextStyle,
TextAlign.configure({
types: ["heading", "paragraph"],
}),
Color.configure({
types: ["textStyle"],
}),
BackgroundColor.configure({
types: ["textStyle"],
}),
CustomImage,
Link,
],
editable: this.editable,
content: this.modelValue,
onUpdate: () => {
this.$emit("update:modelValue", this.editor.getHTML());
},
onCreate: () => {
this.$emit("ready");
}
});
this.editor = editor;
},
beforeUnmount() {
this.editor.destroy();
editable: {
type: Boolean,
default: true,
},
});
export default compoment;
</script>
const emit = defineEmits(["ready", "update:modelValue"]);
const uuid = ref(uuidv4());
const editor = ref<InstanceType<typeof Editor>>();
//const fontList = ref(["Pretendard", "맑은 고딕", "궁서", "돋움"]);
const fontSize = ref(["8px", "10px", "12px", "14px", "18px", "22px", "28px", "36px", "48px", "72px"]);
const imageUploadFiles = ref(null as FileList | null);
const imageLink = ref("");
const showImageModal = ref(false);
watch(
() => props.modelValue,
(value: string) => {
const isSame = editor.value?.getHTML() === value;
if (isSame) {
return;
}
editor.value?.commands.setContent(value, false);
}
);
watch(
() => props.editable,
(value: boolean) => {
if (!editor.value) {
return;
}
editor.value.options.editable = value;
if (value == true) {
editor.value.commands.focus();
}
}
);
onMounted(() => {
const vEditor = new Editor({
extensions: [
StarterKit,
Underline,
FontSize,
TextStyle,
TextAlign.configure({
types: ["heading", "paragraph"],
}),
Color.configure({
types: ["textStyle"],
}),
BackgroundColor.configure({
types: ["textStyle"],
}),
CustomImage,
Link,
],
editable: props.editable,
content: props.modelValue,
onUpdate: () => {
emit("update:modelValue", editor.value?.getHTML());
},
onCreate: () => {
emit("ready");
},
});
editor.value = vEditor;
});
onBeforeUnmount(() => {
editor.value?.destroy();
});
function chooseImage(e: Event) {
const target = unwrap(e.target) as HTMLInputElement;
imageUploadFiles.value = target.files;
}
function colorConvert(val: string | undefined, defaultVal: string) {
if (!val) {
return defaultVal;
}
if (val.startsWith("rgb")) {
const rgb = val.split("(")[1].split(")")[0].split(",");
const vals: string[] = [];
for (const subColor of rgb) {
const hexSubColor = parseInt(subColor).toString(16);
if (hexSubColor.length == 1) {
vals.push("0");
}
vals.push(hexSubColor);
}
return `#${vals.join("")}`;
}
return val;
}
async function tryAddImage(bvModalEvt: Event) {
if (imageUploadFiles.value === null || imageUploadFiles.value.length == 0) {
addImageLink(bvModalEvt);
return;
}
const targetImage = unwrap(unwrap(imageUploadFiles.value).item(0));
let imageResult: {
result: true;
path: string;
};
try {
const base64Binary = await getBase64FromFileObject(targetImage);
imageResult = await SammoAPI.Misc.UploadImage({
imageData: base64Binary,
});
} catch (e) {
if (isString(e)) {
alert(e);
bvModalEvt.preventDefault();
}
if (isObject(e) && "response" in e) {
const axiosErr = e as AxiosError;
if (axiosErr.response?.status === 413) {
alert("허용 용량을 초과했습니다.");
bvModalEvt.preventDefault();
}
}
console.error(e);
return false;
}
const imagePath = imageResult.path;
editor.value?.chain().focus().setImageEx({ src: imagePath }).run();
}
function addImageLink(bvModalEvt: Event) {
if (!imageLink.value) {
alert("업로드할 이미지를 선택하거나, 이미지 주소를 입력해주세요.");
bvModalEvt.preventDefault();
return false;
}
editor.value?.chain().focus().setImageEx({ src: imageLink.value }).run();
}
function resetModal() {
imageLink.value = "";
imageUploadFiles.value = null;
}
</script>
+49 -64
View File
@@ -1,85 +1,70 @@
<template>
<div class="bg0 back_bar">
<button type="button" class="btn btn-sammo-base2 back_btn" @click="back">
돌아가기</button
><button
type="button"
v-if="reloadable"
class="btn btn-sammo-base2 reload_btn"
@click="reload"
>
갱신
</button>
<div v-else></div>
<h2 class="title">{{ title }}</h2>
<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"
v-if="toggleSearch !== undefined"
@click="toggleSearch = !toggleSearch"
>{{ toggleSearch ? "검색 켜짐" : "검색 꺼짐" }}</b-button
>
{{ toggleSearch ? "검색 켜짐" : "검색 꺼짐" }}
</b-button>
</div>
</template>
<script lang="ts">
<script lang="ts" setup>
import "@scss/game_bg.scss";
import { defineComponent, type PropType } from "vue";
import { type PropType, ref, watch } from "vue";
import VueTypes from "vue-types";
export default defineComponent({
name: "TopBackBar",
methods: {
back() {
if (this.type === "normal") {
location.href = "./";
} else if (this.type == "chief") {
location.href = "v_chiefCenter.php";
} else {
//TODO: window.close하려면 부모창이 있어야함!
window.close();
}
},
reload() {
this.$emit("reload");
},
const props = defineProps({
title: VueTypes.string.isRequired,
type: {
type: String as PropType<"normal" | "chief" | "close">,
default: "normal",
required: false,
},
data() {
return {
toggleSearch: this.searchable,
};
searchable: {
type: Boolean,
default: undefined,
required: false,
},
emits: ["update:searchable", "reload"],
watch: {
toggleSearch(val: boolean) {
this.$emit("update:searchable", val);
},
},
props: {
title: {
type: String,
required: true,
},
type: {
type: String as PropType<"normal" | "chief" | "close">,
default: "normal",
required: false,
},
searchable: {
type: Boolean,
default: undefined,
required: false,
},
reloadable: {
type: Boolean,
default: undefined,
required: false,
},
reloadable: {
type: Boolean,
default: undefined,
required: false,
},
});
</script>
const emit = defineEmits(["update:searchable", "reload"]);
const toggleSearch = ref(props.searchable);
watch(toggleSearch, (val) => {
emit("update:searchable", val);
});
function back() {
if (props.type === "normal") {
location.href = "./";
} else if (props.type == "chief") {
location.href = "v_chiefCenter.php";
} else {
//TODO: window.close하려면 부모창이 있어야함!
window.close();
}
}
function reload() {
emit("reload");
}
</script>
<style scoped>
.back_bar {
@@ -112,4 +97,4 @@ export default defineComponent({
font-size: 18pt;
margin: 0;
}
</style>
</style>
+8 -6
View File
@@ -2,15 +2,17 @@
<div v-for="(cityList, distance) in distanceList" :key="distance">
{{ distance }} 떨어진 도시:
<template v-for="(cityID, key) in cityList" :key="key">
<template v-if="key !== 0">, </template>
<a :style="{ color: colorMap[distance] ?? undefined, textDecoration:'underline' }" @click="$emit('selected', cityID)">{{
citiesMap.get(cityID)?.name
}}</a>
<template v-if="key !== 0"> , </template>
<a
:style="{ color: colorMap[distance as keyof typeof colorMap] ?? undefined, textDecoration:'underline' }"
@click="$emit('selected', cityID)"
>{{ citiesMap.get(cityID)?.name }}</a
>
</template>
</div>
</template>
<script lang="ts">
import { defineComponent, PropType } from "vue";
import { defineComponent, type PropType } from "vue";
export default defineComponent({
props: {
@@ -23,7 +25,7 @@ export default defineComponent({
required: true,
},
},
emits: ['selected'],
emits: ["selected"],
data() {
return {
colorMap: {
+23 -45
View File
@@ -8,21 +8,15 @@
backgroundSize: '64px',
outline: 'solid 1px gray',
}"
></div>
/>
<div
:style="{
backgroundColor: crewType.notAvailable
? 'red'
: crewType.reqTech == 0
? 'green'
: 'limegreen',
backgroundColor: crewType.notAvailable ? 'red' : crewType.reqTech == 0 ? 'green' : 'limegreen',
height: '100%',
}"
class="d-grid crewTypeName"
>
<div style="margin: auto">
{{ crewType.name }}
</div>
<div style="margin: auto">{{ crewType.name }}</div>
</div>
<div>{{ crewType.attack }}</div>
<div>{{ crewType.defence }}</div>
@@ -31,37 +25,25 @@
<div>{{ crewType.baseCost.toFixed(1) }}</div>
<div>{{ crewType.baseRice.toFixed(1) }}</div>
<div class="crewTypePanel">
<b-button-group
><b-button class="py-1" variant="dark" @click="beHalf">절반</b-button
><b-button class="py-1" variant="dark" @click="beFilled"
>채우기</b-button
><b-button class="py-1" variant="dark" @click="beFull"
>가득</b-button
></b-button-group
>
<b-button-group>
<b-button class="py-1" variant="dark" @click="beHalf">절반</b-button>
<b-button class="py-1" variant="dark" @click="beFilled">채우기</b-button>
<b-button class="py-1" variant="dark" @click="beFull">가득</b-button>
</b-button-group>
<div class="row">
<div class="col mx-2">
<div class="input-group my-0">
<span class="input-group-text py-1">병력</span>
<input
type="number"
class="form-control py-1 f_tnum px-0 text-end"
v-model="amount"
min="1"
/>
<input v-model="amount" type="number" class="form-control py-1 f_tnum px-0 text-end" min="1" />
<span class="input-group-text py-1 f_tnum">00</span>
<span
class="input-group-text py-1 f_tnum"
style="
text-align: right;
min-width: 10ch;
color: #303030;
background-color: #ddd;
"
><div style="margin-left: auto">
{{ Math.ceil(amount * crewType.baseCost * goldCoeff).toLocaleString() }}
</div></span
style="text-align: right; min-width: 10ch; color: #303030; background-color: #ddd"
>
<div style="margin-left: auto">
{{ Math.ceil(amount * crewType.baseCost * goldCoeff).toLocaleString() }}
</div>
</span>
</div>
</div>
</div>
@@ -69,10 +51,9 @@
<div class="crewTypeBtn d-grid">
<b-button variant="primary" @click="doSubmit">{{ commandName }}</b-button>
</div>
<div
class="crewTypeInfo text-start"
v-html="crewType.info.join('<br>')"
></div>
<!-- eslint-disable-next-line vue/no-v-html -->
<div class="crewTypeInfo text-start" v-html="crewType.info.join('<br>')" />
</div>
</template>
<script lang="ts">
@@ -89,11 +70,6 @@ export default defineComponent({
goldCoeff: VueTypes.number.isRequired,
},
emits: ["submitOutput", "update:amount"],
watch: {
amount(val: number) {
this.$emit("update:amount", val);
},
},
setup(props, { emit }) {
const amount = ref(0);
@@ -103,10 +79,7 @@ export default defineComponent({
function beFilled() {
if (props.crewType.id == props.currentCrewType) {
amount.value = Math.max(
1,
props.leadership - Math.floor(props.crew / 100)
);
amount.value = Math.max(1, props.leadership - Math.floor(props.crew / 100));
} else {
amount.value = props.leadership;
}
@@ -130,5 +103,10 @@ export default defineComponent({
doSubmit,
};
},
watch: {
amount(val: number) {
this.$emit("update:amount", val);
},
},
});
</script>
+10 -15
View File
@@ -1,7 +1,7 @@
<template>
<TopBackBar :title="commandName" />
<div class="bg0" v-if="!available건국"> 이상 건국은 불가능합니다.</div>
<div class="bg0" v-else>
<div v-if="!available건국" class="bg0"> 이상 건국은 불가능합니다.</div>
<div v-else class="bg0">
<div>현재 도시에서 나라를 세웁니다. , 소도시에서만 가능합니다.</div>
<ul>
<li v-for="nationType in nationTypes" :key="nationType.type" class="row">
@@ -16,22 +16,17 @@
</li>
</ul>
<div class="row">
<div class="col-4 col-md-2">
국명 : <b-form-input maxlength="18" v-model="destNationName" />
</div>
<div class="col-3 col-md-2">
색상 : <ColorSelect :colors="colors" v-model="selectedColorID" />
</div>
<div class="col-4 col-md-2">국명 : <b-form-input v-model="destNationName" maxlength="18" /></div>
<div class="col-3 col-md-2">색상 : <ColorSelect v-model="selectedColorID" :colors="colors" /></div>
<div class="col-3 col-md-2">
<label>성향 :</label>
<b-form-select
:options="nationTypesOption"
v-model="selectedNationType"
/>
<b-form-select v-model="selectedNationType" :options="nationTypesOption" />
</div>
<div class="col-2 col-md-2 d-grid">
<b-button @click="submit">{{ commandName }}</b-button>
<b-button @click="submit">
{{ commandName }}
</b-button>
</div>
</div>
</div>
@@ -42,10 +37,10 @@
import ColorSelect from "@/processing/SelectColor.vue";
import { defineComponent, ref } from "vue";
import { unwrap } from "@/util/unwrap";
import { Args } from "@/processing/args";
import type { Args } from "@/processing/args";
import TopBackBar from "@/components/TopBackBar.vue";
import BottomBar from "@/components/BottomBar.vue";
import { procNationTypeList } from "../processingRes";
import type { procNationTypeList } from "../processingRes";
declare const commandName: string;
+8 -13
View File
@@ -1,28 +1,23 @@
<template>
<TopBackBar :title="commandName" />
<div class="bg0">
<div>
자신의 군량을 사거나 팝니다.<br />
</div>
<div>자신의 군량을 사거나 팝니다.<br /></div>
<div class="row">
<div class="col-2 col-md-1">
군량을 :
<b-button-group>
<b-button :pressed="buyRice" @click="buyRice=true"></b-button>
<b-button :pressed="!buyRice" @click="buyRice=false"></b-button>
<b-button :pressed="buyRice" @click="buyRice = true"> </b-button>
<b-button :pressed="!buyRice" @click="buyRice = false"> </b-button>
</b-button-group>
</div>
<div class="col-7 col-md-4">
금액 :
<SelectAmount
:amountGuide="amountGuide"
v-model="amount"
:maxAmount="maxAmount"
:minAmount="minAmount"
/>
<SelectAmount v-model="amount" :amountGuide="amountGuide" :maxAmount="maxAmount" :minAmount="minAmount" />
</div>
<div class="col-3 col-md-2 d-grid">
<b-button variant="primary" @click="submit">{{ commandName }}</b-button>
<b-button variant="primary" @click="submit">
{{ commandName }}
</b-button>
</div>
</div>
</div>
@@ -33,7 +28,7 @@
import SelectAmount from "@/processing/SelectAmount.vue";
import { defineComponent, ref } from "vue";
import { unwrap } from "@/util/unwrap";
import { Args } from "@/processing/args";
import type { Args } from "@/processing/args";
import TopBackBar from "@/components/TopBackBar.vue";
import BottomBar from "@/components/BottomBar.vue";
declare const commandName: string;
+13 -16
View File
@@ -1,5 +1,5 @@
<template>
<TopBackBar :title="commandName" v-model:searchable="searchable" />
<TopBackBar v-model:searchable="searchable" :title="commandName" />
<div class="bg0">
<div>
재야나 타국의 장수를 등용합니다.<br />
@@ -10,15 +10,17 @@
<div class="col-12 col-md-6">
장수 :
<SelectGeneral
v-model="selectedGeneralID"
:generals="generalList"
:groupByNation="nationList"
:textHelper="textHelpGeneral"
:searchable="searchable"
v-model="selectedGeneralID"
/>
</div>
<div class="col-4 col-md-2 d-grid">
<b-button variant="primary" @click="submit">{{ commandName }}</b-button>
<b-button variant="primary" @click="submit">
{{ commandName }}
</b-button>
</div>
</div>
</div>
@@ -29,17 +31,17 @@
import SelectGeneral from "@/processing/SelectGeneral.vue";
import { defineComponent, ref } from "vue";
import { unwrap } from "@/util/unwrap";
import { Args } from "@/processing/args";
import type { Args } from "@/processing/args";
import TopBackBar from "@/components/TopBackBar.vue";
import BottomBar from "@/components/BottomBar.vue";
import {
convertGeneralList,
getProcSearchable,
procGeneralItem,
procGeneralKey,
procGeneralRawItemList,
procNationItem,
procNationList,
type procGeneralItem,
type procGeneralKey,
type procGeneralRawItemList,
type procNationItem,
type procNationList,
} from "../processingRes";
import { getNpcColor } from "@/common_legacy";
declare const commandName: string;
@@ -57,18 +59,13 @@ export default defineComponent({
BottomBar,
},
setup() {
const generalList = convertGeneralList(
procRes.generalsKey,
procRes.generals
);
const generalList = convertGeneralList(procRes.generalsKey, procRes.generals);
const selectedGeneralID = ref(generalList[0].no);
function textHelpGeneral(gen: procGeneralItem): string {
const nameColor = getNpcColor(gen.npc);
const name = nameColor
? `<span style="color:${nameColor}">${gen.name}</span>`
: gen.name;
const name = nameColor ? `<span style="color:${nameColor}">${gen.name}</span>` : gen.name;
return name;
}
+11 -14
View File
@@ -1,5 +1,5 @@
<template>
<TopBackBar :title="commandName" v-model:searchable="searchable" />
<TopBackBar v-model:searchable="searchable" :title="commandName" />
<div class="bg0">
<div v-if="commandName == '등용'">
재야나 타국의 장수를 등용합니다.<br />
@@ -14,14 +14,16 @@
<div class="col-9 col-md-4">
장수 :
<SelectGeneral
v-model="selectedGeneralID"
:generals="generalList"
:textHelper="textHelpGeneral"
:searchable="searchable"
v-model="selectedGeneralID"
/>
</div>
<div class="col-3 col-md-2 d-grid">
<b-button variant="primary" @click="submit">{{ commandName }}</b-button>
<b-button variant="primary" @click="submit">
{{ commandName }}
</b-button>
</div>
</div>
</div>
@@ -32,15 +34,15 @@
import SelectGeneral from "@/processing/SelectGeneral.vue";
import { defineComponent, ref } from "vue";
import { unwrap } from "@/util/unwrap";
import { Args } from "@/processing/args";
import type { Args } from "@/processing/args";
import TopBackBar from "@/components/TopBackBar.vue";
import BottomBar from "@/components/BottomBar.vue";
import {
convertGeneralList,
getProcSearchable,
procGeneralItem,
procGeneralKey,
procGeneralRawItemList,
type procGeneralItem,
type procGeneralKey,
type procGeneralRawItemList,
} from "../processingRes";
import { getNpcColor } from "@/common_legacy";
declare const commandName: string;
@@ -57,18 +59,13 @@ export default defineComponent({
BottomBar,
},
setup() {
const generalList = convertGeneralList(
procRes.generalsKey,
procRes.generals
);
const generalList = convertGeneralList(procRes.generalsKey, procRes.generals);
const selectedGeneralID = ref(generalList[0].no);
function textHelpGeneral(gen: procGeneralItem): string {
const nameColor = getNpcColor(gen.npc);
const name = nameColor
? `<span style="color:${nameColor}">${gen.name}</span>`
: gen.name;
const name = nameColor ? `<span style="color:${nameColor}">${gen.name}</span>` : gen.name;
return `${name} (${gen.leadership}/${gen.strength}/${gen.intel})`;
}
+61 -45
View File
@@ -1,78 +1,91 @@
<template>
<TopBackBar :title="commandName" />
<div class="bg0">
<div>
본인의 특정 병종 숙련을 40% 줄이고, 줄어든 숙련 9/10(90%p) 다른 병종
숙련으로 전환합니다.
</div>
<div>본인의 특정 병종 숙련을 40% 줄이고, 줄어든 숙련 9/10(90%p) 다른 병종 숙련으로 전환합니다.</div>
<div class="row">
<div class="col-4 col-md-2">
감소 대상 숙련 :
<b-form-select v-model="srcArmTypeID">
<b-form-select-option
v-for="[armType, dexInfo] in dexFullInfo"
:key="armType"
:value="armType"
>{{ dexInfo.name }} (<span
:style="{ color: dexInfo.currentInfo.color }"
>{{ dexInfo.currentInfo.name }}</span>)</b-form-select-option
>
<b-form-select-option v-for="[armType, dexInfo] in dexFullInfo" :key="armType" :value="armType">
{{ dexInfo.name }} (<span :style="{ color: dexInfo.currentInfo.color }">{{ dexInfo.currentInfo.name }}</span
>)
</b-form-select-option>
</b-form-select>
</div>
<div class="col-4 col-md-2">
전환 대상 숙련 :
<b-form-select v-model="destArmTypeID">
<b-form-select-option
v-for="[armType, dexInfo] in dexFullInfo"
:key="armType"
:value="armType"
>{{ dexInfo.name }} (<span
:style="{ color: dexInfo.currentInfo.color }"
>{{ dexInfo.currentInfo.name }}</span>)</b-form-select-option
>
<b-form-select-option v-for="[armType, dexInfo] in dexFullInfo" :key="armType" :value="armType">
{{ dexInfo.name }} (<span :style="{ color: dexInfo.currentInfo.color }">{{ dexInfo.currentInfo.name }}</span
>)
</b-form-select-option>
</b-form-select>
</div>
<div class="col-4 col-md-2 d-grid">
<b-button @click="submit">{{ commandName }}</b-button>
<b-button @click="submit">
{{ commandName }}
</b-button>
</div>
<div :style="{
display:'grid',
gridTemplateColumns: '3ch 1ch 2ch 10ch 1ch 3ch 1ch 2ch 10ch 1ch'
}">
<div>{{dexFullInfo.get(srcArmTypeID).name}}</div>
<div
:style="{
display: 'grid',
gridTemplateColumns: '3ch 1ch 2ch 10ch 1ch 3ch 1ch 2ch 10ch 1ch',
}"
>
<div>{{ unwrap(dexFullInfo.get(srcArmTypeID)).name }}</div>
<div class="text-end">[</div>
<div :style="`color:${dexFullInfo.get(srcArmTypeID).currentInfo.color}`">{{dexFullInfo.get(srcArmTypeID).currentInfo.name}}</div>
<div class="f_tnum text-end">{{convNumberFormat(dexFullInfo.get(srcArmTypeID).currentInfo.amount)}}</div>
<div :style="`color:${unwrap(dexFullInfo.get(srcArmTypeID)).currentInfo.color}`">
{{ unwrap(dexFullInfo.get(srcArmTypeID)).currentInfo.name }}
</div>
<div class="f_tnum text-end">
{{ convNumberFormat(unwrap(dexFullInfo.get(srcArmTypeID)).currentInfo.amount) }}
</div>
<div>]</div>
<div class="text-center"></div>
<div class="text-end">[</div>
<div :style="`color:${dexFullInfo.get(srcArmTypeID).decreasedInfo.color}`">{{dexFullInfo.get(srcArmTypeID).decreasedInfo.name}}</div>
<div class="f_tnum text-end">{{convNumberFormat(dexFullInfo.get(srcArmTypeID).decreasedInfo.amount)}}</div>
<div :style="`color:${unwrap(dexFullInfo.get(srcArmTypeID)).decreasedInfo.color}`">
{{ unwrap(dexFullInfo.get(srcArmTypeID)).decreasedInfo.name }}
</div>
<div class="f_tnum text-end">
{{ convNumberFormat(unwrap(dexFullInfo.get(srcArmTypeID)).decreasedInfo.amount) }}
</div>
<div>]</div>
</div>
<div :style="{
display:'grid',
gridTemplateColumns: '3ch 1ch 2ch 10ch 1ch 3ch 1ch 2ch 10ch 1ch'
}">
<div>{{dexFullInfo.get(destArmTypeID).name}}</div>
<div
:style="{
display: 'grid',
gridTemplateColumns: '3ch 1ch 2ch 10ch 1ch 3ch 1ch 2ch 10ch 1ch',
}"
>
<div>{{ unwrap(dexFullInfo.get(destArmTypeID)).name }}</div>
<div class="text-end">[</div>
<template v-if="srcArmTypeID == destArmTypeID">
<div :style="`color:${dexFullInfo.get(destArmTypeID).decreasedInfo.color}`">{{dexFullInfo.get(destArmTypeID).decreasedInfo.name}}</div>
<div class="f_tnum text-end">{{convNumberFormat(dexFullInfo.get(destArmTypeID).decreasedInfo.amount)}}</div>
<div :style="`color:${unwrap(dexFullInfo.get(destArmTypeID)).decreasedInfo.color}`">
{{ unwrap(dexFullInfo.get(destArmTypeID)).decreasedInfo.name }}
</div>
<div class="f_tnum text-end">
{{ convNumberFormat(unwrap(dexFullInfo.get(destArmTypeID)).decreasedInfo.amount) }}
</div>
</template>
<template v-else>
<div :style="`color:${dexFullInfo.get(destArmTypeID).currentInfo.color}`">{{dexFullInfo.get(destArmTypeID).currentInfo.name}}</div>
<div class="f_tnum text-end">{{convNumberFormat(dexFullInfo.get(destArmTypeID).currentInfo.amount)}}</div>
<div :style="`color:${unwrap(dexFullInfo.get(destArmTypeID)).currentInfo.color}`">
{{ unwrap(dexFullInfo.get(destArmTypeID)).currentInfo.name }}
</div>
<div class="f_tnum text-end">
{{ convNumberFormat(unwrap(dexFullInfo.get(destArmTypeID)).currentInfo.amount) }}
</div>
</template>
<div>]</div>
<div class="text-center"></div>
<div class="text-end">[</div>
<div :style="`color:${dexFullInfo.get(destArmTypeID).afterInfo.get(srcArmTypeID).color}`">{{dexFullInfo.get(destArmTypeID).afterInfo.get(srcArmTypeID).name}}</div>
<div class="f_tnum text-end">{{convNumberFormat(dexFullInfo.get(destArmTypeID).afterInfo.get(srcArmTypeID).amount)}}</div>
<div :style="`color:${unwrap(unwrap(dexFullInfo.get(destArmTypeID)).afterInfo.get(srcArmTypeID)).color}`">
{{ unwrap(unwrap(dexFullInfo.get(destArmTypeID)).afterInfo.get(srcArmTypeID)).name }}
</div>
<div class="f_tnum text-end">
{{ convNumberFormat(unwrap(unwrap(dexFullInfo.get(destArmTypeID)).afterInfo.get(srcArmTypeID)).amount) }}
</div>
<div>]</div>
</div>
</div>
</div>
<BottomBar :title="commandName" />
@@ -81,7 +94,7 @@
<script lang="ts">
import { defineComponent, ref } from "vue";
import { unwrap } from "@/util/unwrap";
import { Args } from "@/processing/args";
import type { Args } from "@/processing/args";
import TopBackBar from "@/components/TopBackBar.vue";
import BottomBar from "@/components/BottomBar.vue";
@@ -195,7 +208,9 @@ export default defineComponent({
function convDexFormat(value: dexInfo): string {
const amount = convNumberFormat(value.amount);
return `<span class="f_tnum" style="color:${value.color}">${value.name}</span>,${"\xa0".repeat(Math.max(0, 3 - value.name.length))} ${amount}`;
return `<span class="f_tnum" style="color:${value.color}">${value.name}</span>,${"\xa0".repeat(
Math.max(0, 3 - value.name.length)
)} ${amount}`;
}
function convNumberFormat(value: number): string {
@@ -203,6 +218,7 @@ export default defineComponent({
}
return {
unwrap,
...procRes,
srcArmTypeID,
destArmTypeID,
+19 -15
View File
@@ -1,23 +1,25 @@
<template>
<TopBackBar :title="commandName" v-model:searchable="searchable" />
<TopBackBar v-model:searchable="searchable" :title="commandName" />
<div class="bg0">
<div>
국가에 임관합니다.<br />
이미 임관/등용되었던 국가는 다시 임관할 없습니다.<br />
바로 군주의 위치로 이동합니다.<br />
임관할 국가를 목록에서 선택하세요.<br />
국가에 임관합니다.
<br>
이미 임관/등용되었던 국가는 다시 임관할 없습니다.
<br>
바로 군주의 위치로 이동합니다.
<br>
임관할 국가를 목록에서 선택하세요.
<br>
</div>
<div class="row">
<div class="col-6 col-md-3">
국가 :
<SelectNation
:nations="nationList"
v-model="selectedNationID"
:searchable="searchable"
/>
<SelectNation v-model="selectedNationID" :nations="nationList" :searchable="searchable" />
</div>
<div class="col-4 col-md-2 d-grid">
<b-button @click="submit">{{ commandName }}</b-button>
<b-button @click="submit">
{{ commandName }}
</b-button>
</div>
</div>
<div class="nation-list">
@@ -26,12 +28,13 @@
<div>임관권유문</div>
<div class="zoom-toggle d-grid">
<b-button
v-model="toggleZoom"
:pressed="toggleZoom"
:variant="toggleZoom ? 'info' : 'secondary'"
v-model="toggleZoom"
@click="toggleZoom = !toggleZoom"
>{{ toggleZoom ? "작게 보기" : "크게 보기" }}</b-button
>
{{ toggleZoom ? "작게 보기" : "크게 보기" }}
</b-button>
</div>
</div>
<div
@@ -51,6 +54,7 @@
<div class="align-self-center center">{{ nation.name }}</div>
</div>
<div class="nation-scout-plate align-self-center">
<!-- eslint-disable-next-line vue/no-v-html -->
<div class="nation-scout-msg" v-html="nation.scoutMsg" />
</div>
</div>
@@ -63,10 +67,10 @@
import SelectNation from "@/processing/SelectNation.vue";
import { defineComponent, ref } from "vue";
import { unwrap } from "@/util/unwrap";
import { Args } from "@/processing/args";
import type { Args } from "@/processing/args";
import TopBackBar from "@/components/TopBackBar.vue";
import BottomBar from "@/components/BottomBar.vue";
import { procNationItem, procNationList, getProcSearchable } from "../processingRes";
import { type procNationItem, type procNationList, getProcSearchable } from "../processingRes";
import { isBrightColor } from "@/util/isBrightColor";
declare const commandName: string;
+38 -23
View File
@@ -1,12 +1,16 @@
<template>
<TopBackBar :title="commandName" v-model:searchable="searchable" />
<!-- eslint-disable vue/no-v-html -->
<TopBackBar v-model:searchable="searchable" :title="commandName" />
<div class="bg0">
<div>
장비를 구입하거나 매각합니다.<br />
장비를 구입하거나 매각합니다.
<br>
현재 구입 불가능한 것은 <span style="color: red">붉은색</span>으로
표시됩니다.<br />
표시됩니다.
<br>
현재 도시 치안 : {{ citySecu.toLocaleString() }} &nbsp;&nbsp;&nbsp;현재
자금 : {{ gold.toLocaleString() }}<br />
자금 : {{ gold.toLocaleString() }}
<br>
</div>
<div class="row">
<div class="col-8 col-md-4">
@@ -22,27 +26,27 @@
label="searchText"
track-by="simpleName"
:show-labels="false"
selectLabel="선택(엔터)"
selectGroupLabel=""
selectedLabel="선택됨"
deselectLabel="해제(엔터)"
deselectGroupLabel=""
select-label="선택(엔터)"
select-group-label
selected-label="선택됨"
deselect-label="해제(엔터)"
deselect-group-label
placeholder="아이템 선택"
:maxHeight="400"
:max-height="400"
:searchable="searchable"
>
<template v-slot:option="props">
<template #option="props">
<div
v-if="props.option.html"
:style="{
color: props.option.notAvailable ? 'red' : undefined,
}"
v-html="
`${props.option.html} ${
props.option.notAvailable ? '(불가)' : ''
}`
"
:style="{
color: props.option.notAvailable ? 'red' : undefined,
}"
></div>
/>
<div
v-else-if="props.option.simpleName"
:style="{
@@ -53,21 +57,23 @@
{{ props.option.notAvailable ? "(불가)" : undefined }}
</div>
</template>
<template v-slot:singleLabel="props">
[{{ ItemTypeNameMap[props.option.type] }}]
<template #singleLabel="props">
[{{ ItemTypeNameMap[props.option.type as keyof typeof ItemTypeNameMap] }}]
{{ props.option.simpleName }}
</template>
</v-multiselect>
</div>
<div class="col-4 col-md-2 d-grid">
<b-button @click="submit">{{ commandName }}</b-button>
<b-button @click="submit">
{{ commandName }}
</b-button>
</div>
</div>
<div v-if="selectedItemObj.obj.id != NoneValue" class="row">
<div class="col-4 col-md-2 align-self-center text-center">
{{ selectedItemObj.obj.name }}
</div>
<div class="col" v-html="selectedItemObj.obj.info"></div>
<div class="col" v-html="selectedItemObj.obj.info" />
</div>
</div>
@@ -78,11 +84,20 @@
import { defineComponent, ref } from "vue";
import { unwrap } from "@/util/unwrap";
import { entriesWithType } from "@util/entriesWithType";
import { Args } from "@/processing/args";
import type { Args } from "@/processing/args";
import TopBackBar from "@/components/TopBackBar.vue";
import BottomBar from "@/components/BottomBar.vue";
import { getProcSearchable, procItemList, procItemType } from "../processingRes";
import { ItemTypeKey, ItemTypeNameMap, NoneValue, ValuesOf } from "@/defs";
import {
getProcSearchable,
type procItemList,
type procItemType,
} from "../processingRes";
import {
type ItemTypeKey,
ItemTypeNameMap,
NoneValue,
type ValuesOf,
} from "@/defs";
import { convertSearch초성 } from "@/util/convertSearch초성";
declare const commandName: string;
@@ -187,4 +202,4 @@ export default defineComponent({
};
},
});
</script>
</script>
@@ -1,25 +1,33 @@
<template>
<TopBackBar :title="commandName" v-model:searchable="searchable" />
<TopBackBar
v-model:searchable="searchable"
:title="commandName"
/>
<div class="bg0">
<div>
장수를 따라 임관합니다.<br />
이미 임관/등용되었던 국가는 다시 임관할 없습니다.<br />
바로 군주의 위치로 이동합니다.<br />
임관할 국가를 목록에서 선택하세요.<br />
장수를 따라 임관합니다.<br>
이미 임관/등용되었던 국가는 다시 임관할 없습니다.<br>
바로 군주의 위치로 이동합니다.<br>
임관할 국가를 목록에서 선택하세요.<br>
</div>
<div class="row">
<div class="col-8 col-md-4">
장수 :
<SelectGeneral
v-model="selectedGeneralID"
:generals="generalList"
:groupByNation="nationList"
:textHelper="textHelpGeneral"
:searchable="searchable"
v-model="selectedGeneralID"
/>
</div>
<div class="col-4 col-md-2 d-grid">
<b-button variant="primary" @click="submit">{{ commandName }}</b-button>
<b-button
variant="primary"
@click="submit"
>
{{ commandName }}
</b-button>
</div>
</div>
<div class="nation-list">
@@ -28,12 +36,13 @@
<div>임관권유문</div>
<div class="zoom-toggle d-grid">
<b-button
v-model="toggleZoom"
:pressed="toggleZoom"
:variant="toggleZoom ? 'info' : 'secondary'"
v-model="toggleZoom"
@click="toggleZoom = !toggleZoom"
>{{ toggleZoom ? "작게 보기" : "크게 보기" }}</b-button
>
{{ toggleZoom ? "작게 보기" : "크게 보기" }}
</b-button>
</div>
</div>
<div
@@ -49,9 +58,12 @@
}"
class="d-grid"
>
<div class="align-self-center center">{{ nation.name }}</div>
<div class="align-self-center center">
{{ nation.name }}
</div>
</div>
<div class="nation-scout-plate align-self-center">
<!-- eslint-disable-next-line vue/no-v-html -->
<div class="nation-scout-msg" v-html="nation.scoutMsg" />
</div>
</div>
@@ -64,17 +76,17 @@
import SelectGeneral from "@/processing/SelectGeneral.vue";
import { defineComponent, ref } from "vue";
import { unwrap } from "@/util/unwrap";
import { Args } from "@/processing/args";
import type { Args } from "@/processing/args";
import TopBackBar from "@/components/TopBackBar.vue";
import BottomBar from "@/components/BottomBar.vue";
import {
convertGeneralList,
getProcSearchable,
procGeneralItem,
procGeneralKey,
procGeneralRawItemList,
procNationItem,
procNationList,
type procGeneralItem,
type procGeneralKey,
type procGeneralRawItemList,
type procNationItem,
type procNationList,
} from "../processingRes";
import { getNpcColor } from "@/common_legacy";
import { isBrightColor } from "@/util/isBrightColor";
+92 -52
View File
@@ -4,42 +4,51 @@
<div>
병사를 모집합니다.
<template v-if="commandName == '징병'">
훈련과 사기치는 낮지만 가격이 저렴합니다.<br />
훈련과 사기치는 낮지만 가격이 저렴합니다.<br>
</template>
<template v-else-if="commandName == '모병'">
훈련과 사기치는 높지만 자금이 많이 듭니다.<br />
훈련과 사기치는 높지만 자금이 많이 듭니다.<br>
</template>
가능한 수보다 많게 입력하면 가능한 최대 병사를 모집합니다.<br />
가능한 수보다 많게 입력하면 가능한 최대 병사를 모집합니다.<br>
이미 병사가 있는 경우 추가 {{ commandName }}되며, 병종이 다를경우는 기존의
병사는 소집해제됩니다. <br />
병사는 소집해제됩니다. <br>
현재 {{ commandName }} 가능한 병종은
<span style="color: green">녹색</span>으로 표시되며, 현재
{{ commandName }} 가능한 특수병종은
<span style="color: limegreen">초록색</span>으로 표시됩니다.
</div>
<div class="crewTypeList" ref="defaultTarget">
<div
ref="defaultTarget"
class="crewTypeList"
>
<div class="listFront">
<div class="row gx-0 bg0">
<div class="col-12 col-md-12 d-flex align-items-center">
<div v-if="commandName == '모병'" class="text-center w-100">
모병은 가격 2배의 자금이 소요됩니다.<br />
<div
v-if="commandName == '모병'"
class="text-center w-100"
>
모병은 가격 2배의 자금이 소요됩니다.<br>
</div>
</div>
</div>
<div class="row text-center bg2 gx-0">
<div class="col-4 col-md-2">현재 기술력 : {{ techLevel }}등급</div>
<div class="col-4 col-md-2">
현재 기술력 : {{ techLevel }}등급
</div>
<div class="col-4 col-md-2">
현재 통솔 :
<span
:style="{
color: leadership < fullLeadership ? 'red' : undefined,
}"
>{{ leadership }}</span
>
>{{ leadership }}</span>
</div>
<div class="col-4 col-md-2">최대 통솔 : {{ fullLeadership }}</div>
<div class="col-4 col-md-2">
현재 병종 : {{ crewTypeMap.get(currentCrewType).name }}
최대 통솔 : {{ fullLeadership }}
</div>
<div class="col-4 col-md-2">
현재 병종 : {{ crewTypeMap?.get(currentCrewType)?.name }}
</div>
<div class="col-4 col-md-2">
현재 병사 : {{ crew.toLocaleString() }}
@@ -58,15 +67,15 @@
outline: 'solid 1px gray',
height: '64px',
}"
></div>
/>
<div
:style="{
backgroundColor:
(destCrewType.notAvailable
? 'red'
: destCrewType.reqTech == 0
? 'green'
: 'limegreen') + ' !important',
? 'green'
: 'limegreen') + ' !important',
height: '100%',
}"
class="d-grid"
@@ -75,27 +84,39 @@
{{ destCrewType.name }}
</div>
</div>
<div></div>
<div />
<div class="crewTypePanel">
<b-button-group
><b-button class="py-1" variant="dark" @click="beHalf"
>절반</b-button
><b-button class="py-1" variant="dark" @click="beFilled"
>채우기</b-button
><b-button class="py-1" variant="dark" @click="beFull"
>가득</b-button
></b-button-group
>
<b-button-group>
<b-button
class="py-1"
variant="dark"
@click="beHalf"
>
절반
</b-button><b-button
class="py-1"
variant="dark"
@click="beFilled"
>
채우기
</b-button><b-button
class="py-1"
variant="dark"
@click="beFull"
>
가득
</b-button>
</b-button-group>
<div class="row">
<div class="col mx-2">
<div class="input-group my-0">
<span class="input-group-text py-1">병력</span>
<input
v-model="amount"
type="number"
class="form-control py-1 f_tnum px-0 text-end"
v-model="amount"
min="1"
/>
>
<span class="input-group-text py-1 f_tnum">00</span>
<span
class="input-group-text py-1 f_tnum"
@@ -105,35 +126,49 @@
color: #303030;
background-color: #ddd;
"
><div style="margin-left: auto">
{{
Math.ceil(
amount * destCrewType.baseCost * goldCoeff
).toLocaleString()
}}
</div></span
>
><div style="margin-left: auto">
{{
Math.ceil(
amount * destCrewType.baseCost * goldCoeff
).toLocaleString()
}}
</div></span>
</div>
</div>
</div>
</div>
<div></div>
<b-button variant="primary" @click="submit">{{
commandName
}}</b-button>
<div />
<b-button
variant="primary"
@click="submit"
>
{{
commandName
}}
</b-button>
</div>
<div class="listHeader crewTypeSubGrid text-center bg1">
<div class="crewTypeImg">사진</div>
<div class="crewTypeName">병종</div>
<div class="crewTypeImg">
사진
</div>
<div class="crewTypeName">
병종
</div>
<div>공격</div>
<div>방어</div>
<div>기동</div>
<div>회피</div>
<div>가격</div>
<div>군량</div>
<div class="crewTypePanel">병사 </div>
<div class="crewTypeBtn">행동</div>
<div class="crewTypeInfo">특징</div>
<div class="crewTypePanel">
병사
</div>
<div class="crewTypeBtn">
행동
</div>
<div class="crewTypeInfo">
특징
</div>
</div>
</div>
<div class="listMain">
@@ -156,19 +191,23 @@
:pressed="showNotAvailable.get(armCrewType.armType)"
class="btn-sm"
@click="toggleShowNotAvailable(armCrewType.armType)"
>{{
>
{{
showNotAvailable.get(armCrewType.armType)
? "선택 할 수 있는 병종만 보기"
: "선택 할 수 없는 병종도 보기"
}}</b-button
>
}}
</b-button>
</div>
</div>
<template v-for="crewType in armCrewType.values" :key="crewType.id">
<template
v-for="crewType in armCrewType.values"
:key="crewType.id"
>
<CrewTypeItem
v-if="
showNotAvailable.get(armCrewType.armType) ||
!crewType.notAvailable
!crewType.notAvailable
"
:crewType="crewType"
:leadership="fullLeadership"
@@ -178,7 +217,7 @@
:goldCoeff="goldCoeff"
@submitOutput="trySubmit"
@click="
destCrewType = crewTypeMap.get(crewType.id);
destCrewType = unwrap(crewTypeMap.get(crewType.id));
beFilled();
"
/>
@@ -194,10 +233,10 @@
import CrewTypeItem from "@/processing/CrewTypeItem.vue";
import { defineComponent, ref } from "vue";
import { unwrap } from "@/util/unwrap";
import { Args } from "@/processing/args";
import type { Args } from "@/processing/args";
import TopBackBar from "@/components/TopBackBar.vue";
import BottomBar from "@/components/BottomBar.vue";
import { procArmTypeItem, procCrewTypeItem } from "../processingRes";
import type { procArmTypeItem, procCrewTypeItem } from "../processingRes";
declare const commandName: string;
declare const procRes: {
@@ -292,6 +331,7 @@ export default defineComponent({
submit,
toggleShowNotAvailable,
trySubmit,
unwrap,
};
},
});
+20 -5
View File
@@ -8,21 +8,36 @@
<div class="col-2 col-md-1">
자원 :
<b-button-group>
<b-button :pressed="isGold" @click="isGold = true"></b-button>
<b-button :pressed="!isGold" @click="isGold = false"></b-button>
<b-button
:pressed="isGold"
@click="isGold = true"
>
</b-button>
<b-button
:pressed="!isGold"
@click="isGold = false"
>
</b-button>
</b-button-group>
</div>
<div class="col-7 col-md-4">
금액 :
<SelectAmount
:amountGuide="amountGuide"
v-model="amount"
:amountGuide="amountGuide"
:maxAmount="maxAmount"
:minAmount="minAmount"
/>
</div>
<div class="col-3 col-md-2 d-grid">
<b-button variant="primary" @click="submit">{{ commandName }}</b-button>
<b-button
variant="primary"
@click="submit"
>
{{ commandName }}
</b-button>
</div>
</div>
</div>
@@ -33,7 +48,7 @@
import SelectAmount from "@/processing/SelectAmount.vue";
import { defineComponent, ref } from "vue";
import { unwrap } from "@/util/unwrap";
import { Args } from "@/processing/args";
import type { Args } from "@/processing/args";
import TopBackBar from "@/components/TopBackBar.vue";
import BottomBar from "@/components/BottomBar.vue";
declare const commandName: string;
+15 -4
View File
@@ -1,5 +1,8 @@
<template>
<TopBackBar :title="commandName" type="chief" />
<TopBackBar
:title="commandName"
type="chief"
/>
<div class="bg0">
<div>
국기를 변경합니다. 1 가능합니다.<br>
@@ -7,14 +10,22 @@
<div class="row">
<div class="col-6 col-md-3">
색상 :
<ColorSelect :colors="colors" v-model="selectedColorID" />
<ColorSelect
v-model="selectedColorID"
:colors="colors"
/>
</div>
<div class="col-4 col-md-2 d-grid">
<b-button @click="submit">{{ commandName }}</b-button>
<b-button @click="submit">
{{ commandName }}
</b-button>
</div>
</div>
</div>
<BottomBar :title="commandName" type="chief" />
<BottomBar
:title="commandName"
type="chief"
/>
</template>
<script lang="ts">
+16 -5
View File
@@ -1,20 +1,31 @@
<template>
<TopBackBar :title="commandName" type="chief" />
<TopBackBar
:title="commandName"
type="chief"
/>
<div class="bg0">
<div>
나라의 이름을 바꿉니다. 황제가 1 가능합니다.<br />
나라의 이름을 바꿉니다. 황제가 1 가능합니다.<br>
</div>
<div class="row">
<div class="col-6 col-md-3">
국명 :
<b-form-input maxlength="18" v-model="destNationName"/>
<b-form-input
v-model="destNationName"
maxlength="18"
/>
</div>
<div class="col-4 col-md-2 d-grid">
<b-button @click="submit">{{ commandName }}</b-button>
<b-button @click="submit">
{{ commandName }}
</b-button>
</div>
</div>
</div>
<BottomBar :title="commandName" type="chief" />
<BottomBar
:title="commandName"
type="chief"
/>
</template>
<script lang="ts">
+27 -25
View File
@@ -1,13 +1,13 @@
<template>
<TopBackBar :title="commandName" type="chief" v-model:searchable="searchable" />
<TopBackBar v-model:searchable="searchable" :title="commandName" type="chief" />
<div class="bg0">
<MapLegacyTemplate
v-model="selectedCityObj"
:isDetailMap="false"
:clickableAll="true"
:neutralView="true"
:useCachedMap="true"
:mapTheme="mapTheme"
v-model="selectedCityObj"
:mapName="mapName"
/>
<div>
타국에게 원조합니다.<br />
@@ -37,14 +37,14 @@
<div class="row">
<div class="col-6 col-md-3">
국가 :
<SelectNation :nations="nationList" v-model="selectedNationID" :searchable="searchable" />
<SelectNation v-model="selectedNationID" :nations="nationList" :searchable="searchable" />
</div>
<div class="col-6 col-md-0"></div>
<div class="col-6 col-md-0" />
<div class="col-8 col-md-4">
:
<SelectAmount
:amountGuide="amountGuide"
v-model="goldAmount"
:amountGuide="amountGuide"
:step="10"
:maxAmount="maxAmount"
:minAmount="minAmount"
@@ -53,15 +53,17 @@
<div class="col-8 col-md-4">
:
<SelectAmount
:amountGuide="amountGuide"
v-model="riceAmount"
:amountGuide="amountGuide"
:step="10"
:maxAmount="maxAmount"
:minAmount="minAmount"
/>
</div>
<div class="col-4 col-md-2 d-grid">
<b-button variant="primary" @click="submit">{{ commandName }}</b-button>
<b-button variant="primary" @click="submit">
{{ commandName }}
</b-button>
</div>
</div>
</div>
@@ -69,19 +71,19 @@
</template>
<script lang="ts">
import MapLegacyTemplate, {
MapCityParsed,
} from "@/components/MapLegacyTemplate.vue";
import MapLegacyTemplate, { type MapCityParsed } from "@/components/MapLegacyTemplate.vue";
import SelectNation from "@/processing/SelectNation.vue";
import SelectAmount from "@/processing/SelectAmount.vue";
import { defineComponent, ref } from "vue";
import { unwrap } from "@/util/unwrap";
import { Args } from "@/processing/args";
import type { Args } from "@/processing/args";
import TopBackBar from "@/components/TopBackBar.vue";
import BottomBar from "@/components/BottomBar.vue";
import { getProcSearchable, procNationItem, procNationList } from "../processingRes";
declare const mapTheme: string;
declare const commandName: string;
import { getProcSearchable, type procNationItem, type procNationList } from "../processingRes";
declare const staticValues: {
mapName: string;
commandName: string;
};
declare const procRes: {
nationList: procNationList;
@@ -106,14 +108,6 @@ export default defineComponent({
TopBackBar,
BottomBar,
},
watch: {
selectedCityObj(city: MapCityParsed) {
if (!city.nationID) {
return;
}
this.selectedNationID = city.nationID;
},
},
setup() {
const nationList = new Map<number, procNationItem>();
for (const nationItem of procRes.nationList) {
@@ -138,7 +132,7 @@ export default defineComponent({
return {
searchable: getProcSearchable(),
mapTheme,
mapName: staticValues.mapName,
goldAmount,
riceAmount,
nationList,
@@ -149,9 +143,17 @@ export default defineComponent({
minAmount: ref(procRes.minAmount),
maxAmount: ref(procRes.maxAmount),
amountGuide: procRes.amountGuide,
commandName,
commandName: staticValues.commandName,
submit,
};
},
watch: {
selectedCityObj(city: MapCityParsed) {
if (!city.nationID) {
return;
}
this.selectedNationID = city.nationID;
},
},
});
</script>
+35 -33
View File
@@ -1,13 +1,13 @@
<template>
<TopBackBar :title="commandName" type="chief" v-model:searchable="searchable" />
<TopBackBar v-model:searchable="searchable" :title="commandName" type="chief" />
<div class="bg0">
<MapLegacyTemplate
v-model="selectedCityObj"
:isDetailMap="false"
:clickableAll="true"
:neutralView="true"
:useCachedMap="true"
:mapTheme="mapTheme"
v-model="selectedCityObj"
:mapName="mapName"
/>
<div>
@@ -19,20 +19,22 @@
<div class="col-12 col-md-6">
장수 :
<SelectGeneral
v-model="selectedGeneralID"
:cities="citiesMap"
:generals="generalList"
:troops="troops"
:textHelper="textHelpGeneral"
:searchable="searchable"
v-model="selectedGeneralID"
/>
</div>
<div class="col-6 col-md-4">
도시 :
<SelectCity :cities="citiesMap" v-model="selectedCityID" :searchable="searchable" />
<SelectCity v-model="selectedCityID" :cities="citiesMap" :searchable="searchable" />
</div>
<div class="col-4 col-md-2 d-grid">
<b-button variant="primary" @click="submit">{{ commandName }}</b-button>
<b-button variant="primary" @click="submit">
{{ commandName }}
</b-button>
</div>
</div>
</div>
@@ -40,28 +42,29 @@
</template>
<script lang="ts">
import MapLegacyTemplate, {
MapCityParsed,
} from "@/components/MapLegacyTemplate.vue";
import MapLegacyTemplate, { type MapCityParsed } from "@/components/MapLegacyTemplate.vue";
import SelectCity from "@/processing/SelectCity.vue";
import SelectGeneral from "@/processing/SelectGeneral.vue";
import { defineComponent, ref } from "vue";
import { unwrap } from "@/util/unwrap";
import { Args } from "@/processing/args";
import type { Args } from "@/processing/args";
import TopBackBar from "@/components/TopBackBar.vue";
import BottomBar from "@/components/BottomBar.vue";
import {
convertGeneralList,
getProcSearchable,
procGeneralItem,
procGeneralKey,
procGeneralRawItemList,
procTroopList,
type procGeneralItem,
type procGeneralKey,
type procGeneralRawItemList,
type procTroopList,
} from "../processingRes";
import { getNpcColor } from "@/common_legacy";
declare const mapTheme: string;
declare const currentCity: number;
declare const commandName: string;
declare const staticValues: {
mapName: string;
currentCity: number;
commandName: string;
};
declare const procRes: {
distanceList: Record<number, number[]>;
@@ -79,11 +82,6 @@ export default defineComponent({
TopBackBar,
BottomBar,
},
watch: {
selectedCityObj(city: MapCityParsed) {
this.selectedCityID = city.id;
},
},
setup() {
const citiesMap = new Map<
number,
@@ -96,11 +94,8 @@ export default defineComponent({
citiesMap.set(id, { name });
}
const generalList = convertGeneralList(
procRes.generalsKey,
procRes.generals
);
const selectedCityID = ref(currentCity);
const generalList = convertGeneralList(procRes.generalsKey, procRes.generals);
const selectedCityID = ref(staticValues.currentCity);
function selectedCity(cityID: number) {
selectedCityID.value = cityID;
@@ -108,11 +103,13 @@ export default defineComponent({
const selectedGeneralID = ref(generalList[0].no);
function textHelpGeneral(gen: procGeneralItem): string{
const troops = (!gen.troopID)?'':`,${procRes.troops[gen.troopID].name}`;
function textHelpGeneral(gen: procGeneralItem): string {
const troops = !gen.troopID ? "" : `,${procRes.troops[gen.troopID].name}`;
const nameColor = getNpcColor(gen.npc);
const name = nameColor?`<span style="color:${nameColor}">${gen.name}</span>`:gen.name;
return `${name} [${citiesMap.get(unwrap(gen.cityID))?.name}${troops}] (${gen.leadership}/${gen.strength}/${gen.intel}) <병${unwrap(gen.crew).toLocaleString()}/훈${gen.train}/사${gen.atmos}>`;
const name = nameColor ? `<span style="color:${nameColor}">${gen.name}</span>` : gen.name;
return `${name} [${citiesMap.get(unwrap(gen.cityID))?.name}${troops}] (${gen.leadership}/${gen.strength}/${
gen.intel
}) <병${unwrap(gen.crew).toLocaleString()}/훈${gen.train}/사${gen.atmos}>`;
}
async function submit(e: Event) {
@@ -127,7 +124,7 @@ export default defineComponent({
return {
searchable: getProcSearchable(),
mapTheme: ref(mapTheme),
mapName: ref(staticValues.mapName),
citiesMap: ref(citiesMap),
selectedCityID,
selectedGeneralID,
@@ -135,11 +132,16 @@ export default defineComponent({
distanceList: procRes.distanceList,
troops: procRes.troops,
generalList,
commandName,
commandName: staticValues.commandName,
selectedCity,
textHelpGeneral,
submit,
};
},
watch: {
selectedCityObj(city: MapCityParsed) {
this.selectedCityID = city.id;
},
},
});
</script>
@@ -1,13 +1,13 @@
<template>
<TopBackBar :title="commandName" type="chief" v-model:searchable="searchable" />
<TopBackBar v-model:searchable="searchable" :title="commandName" type="chief" />
<div class="bg0">
<MapLegacyTemplate
v-model="selectedCityObj"
:isDetailMap="false"
:clickableAll="true"
:neutralView="true"
:useCachedMap="true"
:mapTheme="mapTheme"
v-model="selectedCityObj"
:mapName="mapName"
/>
<div>
@@ -20,30 +20,29 @@
<div class="row">
<div class="col-4 col-md-3">
국가 :
<SelectNation :nations="nationList" v-model="selectedNationID" :searchable="searchable" />
<SelectNation v-model="selectedNationID" :nations="nationList" :searchable="searchable" />
</div>
<div class="col-5 col-md-3">
기간 :
<div class="input-group">
<b-form-select class="text-end selectedYear" v-model="selectedYear"
><b-form-select-option
v-for="yearP in maxYear - minYear + 1"
:key="yearP"
:value="yearP + minYear - 1"
>{{ yearP + minYear - 1 }}</b-form-select-option
>
<b-form-select v-model="selectedYear" class="text-end selectedYear">
<b-form-select-option v-for="yearP in maxYear - minYear + 1" :key="yearP" :value="yearP + minYear - 1">
{{ yearP + minYear - 1 }}
</b-form-select-option>
</b-form-select>
<span class="input-group-text px-2"></span>
<b-form-select class="text-center" v-model="selectedMonth"
><b-form-select-option v-for="month in 12" :key="month" :value="month">{{
month
}}</b-form-select-option>
<b-form-select v-model="selectedMonth" class="text-center">
<b-form-select-option v-for="month in 12" :key="month" :value="month">
{{ month }}
</b-form-select-option>
</b-form-select>
<span class="input-group-text px-2"></span>
</div>
</div>
<div class="col-3 col-md-2 d-grid">
<b-button @click="submit">{{ commandName }}</b-button>
<b-button @click="submit">
{{ commandName }}
</b-button>
</div>
</div>
</div>
@@ -51,18 +50,19 @@
</template>
<script lang="ts">
import MapLegacyTemplate, {
MapCityParsed,
} from "@/components/MapLegacyTemplate.vue";
import MapLegacyTemplate, { type MapCityParsed } from "@/components/MapLegacyTemplate.vue";
import SelectNation from "@/processing/SelectNation.vue";
import { defineComponent, ref } from "vue";
import { unwrap } from "@/util/unwrap";
import { Args } from "@/processing/args";
import type { Args } from "@/processing/args";
import TopBackBar from "@/components/TopBackBar.vue";
import BottomBar from "@/components/BottomBar.vue";
import { getProcSearchable, procNationItem, procNationList } from "../processingRes";
declare const mapTheme: string;
declare const commandName: string;
import { getProcSearchable, type procNationItem, type procNationList } from "../processingRes";
declare const staticValues: {
mapName: string,
commandName: string,
}
declare const procRes: {
nationList: procNationList;
@@ -79,14 +79,6 @@ export default defineComponent({
TopBackBar,
BottomBar,
},
watch: {
selectedCityObj(city: MapCityParsed) {
if (!city.nationID) {
return;
}
this.selectedNationID = city.nationID;
},
},
setup() {
const nationList = new Map<number, procNationItem>();
for (const nationItem of procRes.nationList) {
@@ -119,20 +111,28 @@ export default defineComponent({
...procRes,
selectedYear,
selectedMonth,
mapTheme: ref(mapTheme),
mapName: staticValues.mapName,
nationList: ref(nationList),
selectedCityObj,
selectedNationID,
commandName,
commandName: staticValues.commandName,
selectedNation,
submit,
};
},
watch: {
selectedCityObj(city: MapCityParsed) {
if (!city.nationID) {
return;
}
this.selectedNationID = city.nationID;
},
},
});
</script>
<style lang="scss" scoped>
.selectedYear{
width:32%;
.selectedYear {
width: 32%;
}
</style>
</style>
+28 -31
View File
@@ -1,13 +1,13 @@
<template>
<TopBackBar :title="commandName" type="chief" v-model:searchable="searchable" />
<TopBackBar v-model:searchable="searchable" :title="commandName" type="chief" />
<div class="bg0">
<MapLegacyTemplate
v-model="selectedCityObj"
:isDetailMap="false"
:clickableAll="true"
:neutralView="true"
:useCachedMap="true"
:mapTheme="mapTheme"
v-model="selectedCityObj"
:mapName="mapName"
/>
<div>
선택된 국가에 피장파장을 발동합니다.<br />
@@ -22,17 +22,16 @@
<div class="row">
<div class="col-6 col-md-3">
국가 :
<SelectNation :nations="nationList" v-model="selectedNationID" :searchable="searchable" />
<SelectNation v-model="selectedNationID" :nations="nationList" :searchable="searchable" />
</div>
<div class="col-3 col-md-2">
<label>전략 :</label>
<b-form-select
:options="commandTypesOption"
v-model="selectedCommandID"
/>
<b-form-select v-model="selectedCommandID" :options="commandTypesOption" />
</div>
<div class="col-3 col-md-2 d-grid">
<b-button @click="submit">{{ commandName }}</b-button>
<b-button @click="submit">
{{ commandName }}
</b-button>
</div>
</div>
</div>
@@ -40,18 +39,19 @@
</template>
<script lang="ts">
import MapLegacyTemplate, {
MapCityParsed,
} from "@/components/MapLegacyTemplate.vue";
import MapLegacyTemplate, { type MapCityParsed } from "@/components/MapLegacyTemplate.vue";
import SelectNation from "@/processing/SelectNation.vue";
import { defineComponent, ref } from "vue";
import { unwrap } from "@/util/unwrap";
import { Args } from "@/processing/args";
import type { Args } from "@/processing/args";
import TopBackBar from "@/components/TopBackBar.vue";
import BottomBar from "@/components/BottomBar.vue";
import { getProcSearchable, procNationItem, procNationList } from "../processingRes";
declare const mapTheme: string;
declare const commandName: string;
import { getProcSearchable, type procNationItem, type procNationList } from "../processingRes";
declare const staticValues: {
mapName: string;
commandName: string;
};
declare const procRes: {
nationList: procNationList;
@@ -74,14 +74,6 @@ export default defineComponent({
TopBackBar,
BottomBar,
},
watch: {
selectedCityObj(city: MapCityParsed) {
if (!city.nationID) {
return;
}
this.selectedNationID = city.nationID;
},
},
setup() {
const nationList = new Map<number, procNationItem>();
for (const nationItem of procRes.nationList) {
@@ -94,18 +86,16 @@ export default defineComponent({
const commandTypesOption: { html: string; value: string }[] = [];
for (const [commandTypeID, commandTypeInfo] of Object.entries(procRes.availableCommandTypeList)) {
const notAvailable = commandTypeInfo.remainTurn > 0;
const notAvailableText = notAvailable?' (불가)':'';
const notAvailableText = notAvailable ? " (불가)" : "";
const name = `${commandTypeInfo.name}${notAvailableText}`;
const html = notAvailable?`<span style='color:red;'>${name}</span>`:name;
const html = notAvailable ? `<span style='color:red;'>${name}</span>` : name;
commandTypesOption.push({
html,
value: commandTypeID,
});
}
const selectedCommandID = ref(
Object.keys(procRes.availableCommandTypeList)[0]
);
const selectedCommandID = ref(Object.keys(procRes.availableCommandTypeList)[0]);
function selectedNation(nationID: number) {
selectedNationID.value = nationID;
@@ -124,16 +114,23 @@ export default defineComponent({
return {
searchable: getProcSearchable(),
...procRes,
...staticValues,
selectedCommandID,
commandTypesOption,
mapTheme: ref(mapTheme),
nationList: ref(nationList),
selectedCityObj,
selectedNationID,
commandName,
selectedNation,
submit,
};
},
watch: {
selectedCityObj(city: MapCityParsed) {
if (!city.nationID) {
return;
}
this.selectedNationID = city.nationID;
},
},
});
</script>
+67 -80
View File
@@ -1,13 +1,13 @@
<template>
<TopBackBar :title="commandName" :type="procEntryMode" v-model:searchable="searchable" />
<TopBackBar v-model:searchable="searchable" :title="commandName" :type="procEntryMode" />
<div class="bg0">
<MapLegacyTemplate
v-model="selectedCityObj"
:isDetailMap="false"
:clickableAll="true"
:neutralView="true"
:useCachedMap="true"
:mapTheme="mapTheme"
v-model="selectedCityObj"
:mapName="mapName"
/>
<div v-if="commandName == '강행'">
@@ -31,8 +31,7 @@
목록을 선택하거나 도시를 클릭하세요.<br />
</div>
<div v-else-if="commandName in { 화계: 1, 탈취: 1, 파괴: 1, 선동: 1 }">
선택된 도시에 {{ commandName
}}{{ JosaPick(commandName, "") }} 실행합니다.<br />
선택된 도시에 {{ commandName }}{{ JosaPick(commandName, "") }} 실행합니다.<br />
목록을 선택하거나 도시를 클릭하세요.<br />
</div>
<div v-else-if="commandName == '수몰'">
@@ -57,104 +56,92 @@
</div>
<div v-else-if="commandName == '초토화'">
선택된 도시를 초토화 시킵니다.<br />
도시가 공백지가 되며, 도시의 인구, 내정 상태에 따라 상당량의 국고가
확보됩니다.<br />
도시가 공백지가 되며, 도시의 인구, 내정 상태에 따라 상당량의 국고가 확보됩니다.<br />
국가의 수뇌들은 명성을 잃고, 모든 장수들은 배신 수치가 1 증가합니다.<br />
목록을 선택하거나 도시를 클릭하세요.<br />
</div>
<div class="row">
<div class="col-4 col-md-2">
도시:
<SelectCity :cities="citiesMap" v-model="selectedCityID" :searchable="searchable" />
<SelectCity v-model="selectedCityID" :cities="citiesMap" :searchable="searchable" />
</div>
<div class="col-4 col-md-2 d-grid">
<b-button @click="submit">{{ commandName }}</b-button>
<b-button @click="submit">
{{ commandName }}
</b-button>
</div>
</div>
<CityBasedOnDistance
:citiesMap="citiesMap"
:distanceList="distanceList"
@selected="selected"
/>
<CityBasedOnDistance :citiesMap="citiesMap" :distanceList="distanceList" @selected="selected" />
</div>
<BottomBar :title="commandName" :type="procEntryMode" />
</template>
<script lang="ts">
import MapLegacyTemplate, {
MapCityParsed,
} from "@/components/MapLegacyTemplate.vue";
import SelectCity from "@/processing/SelectCity.vue";
import CityBasedOnDistance from "@/processing/CitiesBasedOnDistance.vue";
import { defineComponent, ref } from "vue";
import { unwrap } from "@/util/unwrap";
import { Args } from "@/processing/args";
import TopBackBar from "@/components/TopBackBar.vue";
import BottomBar from "@/components/BottomBar.vue";
import { pick as JosaPick } from "@util/JosaUtil";
import { getProcSearchable } from "./processingRes";
declare const mapTheme: string;
declare const currentCity: number;
declare const commandName: string;
declare const staticValues: {
mapName: string;
currentCity: number;
commandName: string;
entryInfo: ["General" | "Nation", unknown];
};
declare const procRes: {
distanceList: Record<number, number[]>;
cities: [number, string][];
};
declare const entryInfo: ['General'|'Nation', unknown];
</script>
export default defineComponent({
components: {
MapLegacyTemplate,
SelectCity,
CityBasedOnDistance,
TopBackBar,
BottomBar,
},
watch: {
selectedCityObj(city: MapCityParsed) {
this.selectedCityID = city.id;
<script lang="ts" setup>
import MapLegacyTemplate, { type MapCityParsed } from "@/components/MapLegacyTemplate.vue";
import SelectCity from "@/processing/SelectCity.vue";
import CityBasedOnDistance from "@/processing/CitiesBasedOnDistance.vue";
import { ref, type Ref, watch } from "vue";
import { unwrap } from "@/util/unwrap";
import type { Args } from "@/processing/args";
import TopBackBar from "@/components/TopBackBar.vue";
import BottomBar from "@/components/BottomBar.vue";
import { pick as JosaPick } from "@util/JosaUtil";
import { getProcSearchable } from "./processingRes";
const { distanceList } = procRes;
const citiesMap = ref(
new Map<
number,
{
name: string;
info?: string;
}
>()
);
for (const [id, name] of procRes.cities) {
citiesMap.value.set(id, { name });
}
const selectedCityID = ref(staticValues.currentCity);
function selected(cityID: number) {
selectedCityID.value = cityID;
}
async function submit(e: Event) {
const event = new CustomEvent<Args>("customSubmit", {
detail: {
destCityID: selectedCityID.value,
},
},
setup() {
const citiesMap = new Map<
number,
{
name: string;
info?: string;
}
>();
for (const [id, name] of procRes.cities) {
citiesMap.set(id, { name });
}
});
unwrap(e.target).dispatchEvent(event);
}
const selectedCityID = ref(currentCity);
const searchable = getProcSearchable();
const mapName = staticValues.mapName;
function selected(cityID: number) {
selectedCityID.value = cityID;
}
const procEntryMode: Ref<"chief" | "normal"> = ref(staticValues.entryInfo[0] == "Nation" ? "chief" : "normal");
const selectedCityObj = ref<MapCityParsed>();
const commandName = ref(staticValues.commandName);
async function submit(e: Event) {
const event = new CustomEvent<Args>("customSubmit", {
detail: {
destCityID: selectedCityID.value,
},
});
unwrap(e.target).dispatchEvent(event);
}
return {
procEntryMode: entryInfo[0] == 'Nation'?'chief':'normal',
searchable: getProcSearchable(),
mapTheme: ref(mapTheme),
citiesMap: ref(citiesMap),
selectedCityID,
selectedCityObj: ref(undefined as MapCityParsed | undefined),
distanceList: procRes.distanceList,
commandName,
JosaPick,
selected,
submit,
};
},
watch(selectedCityObj, (city?: MapCityParsed) => {
if (city === undefined) {
return;
}
selectedCityID.value = city.id;
});
</script>
+37 -15
View File
@@ -1,12 +1,16 @@
<template>
<TopBackBar :title="commandName" :type="procEntryMode" v-model:searchable="searchable" />
<TopBackBar
v-model:searchable="searchable"
:title="commandName"
:type="procEntryMode"
/>
<div class="bg0">
<div v-if="commandName == '몰수'">
장수의 자금이나 군량을 몰수합니다.<br />
몰수한것은 국가재산으로 귀속됩니다.<br />
장수의 자금이나 군량을 몰수합니다.<br>
몰수한것은 국가재산으로 귀속됩니다.<br>
</div>
<div v-else-if="commandName == '포상'">
국고로 장수에게 자금이나 군량을 지급합니다.<br />
국고로 장수에게 자금이나 군량을 지급합니다.<br>
</div>
<div v-else-if="commandName == '증여'">
자신의 자금이나 군량을 다른 장수에게 증여합니다.<br>
@@ -15,9 +19,9 @@
<div class="col-12 col-md-5">
장수 :
<SelectGeneral
v-model="selectedGeneralID"
:cities="citiesMap"
:generals="generalList"
v-model="selectedGeneralID"
:textHelper="textHelpGeneral"
:searchable="searchable"
/>
@@ -25,25 +29,43 @@
<div class="col-2 col-md-1">
자원 :
<b-button-group>
<b-button :pressed="isGold" @click="isGold=true"></b-button>
<b-button :pressed="!isGold" @click="isGold=false"></b-button>
<b-button
:pressed="isGold"
@click="isGold=true"
>
</b-button>
<b-button
:pressed="!isGold"
@click="isGold=false"
>
</b-button>
</b-button-group>
</div>
<div class="col-7 col-md-4">
금액 :
<SelectAmount
:amountGuide="amountGuide"
v-model="amount"
:amountGuide="amountGuide"
:maxAmount="maxAmount"
:minAmount="minAmount"
/>
</div>
<div class="col-3 col-md-2 d-grid">
<b-button variant="primary" @click="submit">{{ commandName }}</b-button>
<b-button
variant="primary"
@click="submit"
>
{{ commandName }}
</b-button>
</div>
</div>
</div>
<BottomBar :title="commandName" :type="procEntryMode" />
<BottomBar
:title="commandName"
:type="procEntryMode"
/>
</template>
<script lang="ts">
@@ -51,15 +73,15 @@ import SelectGeneral from "@/processing/SelectGeneral.vue";
import SelectAmount from "@/processing/SelectAmount.vue";
import { defineComponent, ref } from "vue";
import { unwrap } from "@/util/unwrap";
import { Args } from "@/processing/args";
import type { Args } from "@/processing/args";
import TopBackBar from "@/components/TopBackBar.vue";
import BottomBar from "@/components/BottomBar.vue";
import {
convertGeneralList,
getProcSearchable,
procGeneralItem,
procGeneralKey,
procGeneralRawItemList,
type procGeneralItem,
type procGeneralKey,
type procGeneralRawItemList,
} from "./processingRes";
import { getNpcColor } from "@/common_legacy";
declare const commandName: string;
@@ -125,7 +147,7 @@ export default defineComponent({
}
return {
procEntryMode: entryInfo[0] == 'Nation'?'chief':'normal',
procEntryMode: <'chief'|'normal'>(entryInfo[0] == 'Nation'?'chief':'normal'),
searchable: getProcSearchable(),
amount,
isGold,
+27 -38
View File
@@ -1,25 +1,20 @@
<template>
<TopBackBar
:title="commandName"
:type="procEntryMode"
v-model:searchable="searchable"
/>
<TopBackBar v-model:searchable="searchable" :title="commandName" :type="procEntryMode" />
<div class="bg0">
<MapLegacyTemplate
v-model="selectedCityObj"
:isDetailMap="false"
:clickableAll="true"
:neutralView="true"
:useCachedMap="true"
:mapTheme="mapTheme"
v-model="selectedCityObj"
:mapName="mapName"
/>
<div v-if="commandName == '선전포고'">
타국에게 선전 포고합니다.<br />
선전 포고할 국가를 목록에서 선택하세요.<br />
고립되지 않은 아국 도시에서 인접한 국가에 선포 가능합니다.<br />
초반제한 해제 2년전부터 선포가 가능합니다. ({{ startYear + 1 }} 1월부터
가능)<br />
초반제한 해제 2년전부터 선포가 가능합니다. ({{ startYear + 1 }} 1월부터 가능)<br />
현재 선포가 불가능한 국가는 배경색이
<span style="color: red">붉은색</span>으로 표시됩니다.<br />
</div>
@@ -58,14 +53,12 @@
<div class="row">
<div class="col-6 col-md-3">
국가 :
<SelectNation
:nations="nationList"
v-model="selectedNationID"
:searchable="searchable"
/>
<SelectNation v-model="selectedNationID" :nations="nationList" :searchable="searchable" />
</div>
<div class="col-4 col-md-2 d-grid">
<b-button @click="submit">{{ commandName }}</b-button>
<b-button @click="submit">
{{ commandName }}
</b-button>
</div>
</div>
</div>
@@ -73,24 +66,20 @@
</template>
<script lang="ts">
import MapLegacyTemplate, {
MapCityParsed,
} from "@/components/MapLegacyTemplate.vue";
import MapLegacyTemplate, { type MapCityParsed } from "@/components/MapLegacyTemplate.vue";
import SelectNation from "@/processing/SelectNation.vue";
import { defineComponent, ref } from "vue";
import { unwrap } from "@/util/unwrap";
import { Args } from "@/processing/args";
import type { Args } from "@/processing/args";
import TopBackBar from "@/components/TopBackBar.vue";
import BottomBar from "@/components/BottomBar.vue";
import {
getProcSearchable,
procNationItem,
procNationList,
} from "./processingRes";
declare const mapTheme: string;
declare const commandName: string;
declare const entryInfo: ['General'|'Nation', unknown];
import { getProcSearchable, type procNationItem, type procNationList } from "./processingRes";
declare const staticValues: {
mapName: string;
commandName: string;
entryInfo: ["General" | "Nation", unknown];
};
declare const procRes: {
nationList: procNationList;
startYear: number;
@@ -103,14 +92,6 @@ export default defineComponent({
TopBackBar,
BottomBar,
},
watch: {
selectedCityObj(city: MapCityParsed) {
if (!city.nationID) {
return;
}
this.selectedNationID = city.nationID;
},
},
setup() {
const nationList = new Map<number, procNationItem>();
for (const nationItem of procRes.nationList) {
@@ -134,17 +115,25 @@ export default defineComponent({
}
return {
procEntryMode: entryInfo[0] == "Nation" ? "chief" : "normal",
procEntryMode: (staticValues.entryInfo[0] == "Nation" ? "chief" : "normal") as 'chief'|'normal',
searchable: getProcSearchable(),
startYear: procRes.startYear,
mapTheme: ref(mapTheme),
mapName: staticValues.mapName,
nationList: ref(nationList),
selectedCityObj,
selectedNationID,
commandName,
commandName: staticValues.commandName,
selectedNation,
submit,
};
},
watch: {
selectedCityObj(city: MapCityParsed) {
if (!city.nationID) {
return;
}
this.selectedNationID = city.nationID;
},
},
});
</script>
+33 -74
View File
@@ -1,104 +1,63 @@
<template>
<div class="input-group">
<b-button
v-if="maxAmount > 20000"
class="btn-sm"
@click="amount = Math.max(amount - 10000, minAmount)"
>-</b-button
>
<b-button
v-if="maxAmount > 2000"
class="btn-sm"
@click="amount = Math.max(amount - 1000, minAmount)"
>-</b-button
>
<b-button
v-if="maxAmount > 200"
class="btn-sm"
@click="amount = Math.max(amount - 100, minAmount)"
>-</b-button
>
<b-button v-if="maxAmount > 20000" class="btn-sm" @click="amount = Math.max(amount - 10000, minAmount)">
-만
</b-button>
<b-button v-if="maxAmount > 2000" class="btn-sm" @click="amount = Math.max(amount - 1000, minAmount)">
-천
</b-button>
<b-button v-if="maxAmount > 200" class="btn-sm" @click="amount = Math.max(amount - 100, minAmount)"> -백 </b-button>
<input
v-model="amount"
type="number"
class="form-control text-end"
:max="maxAmount"
:min="minAmount"
:step="step"
v-model="amount"
placeholder="금액"
/>
<b-dropdown right text="" class="amount-dropdown" v-if="amountGuide">
<b-dropdown-item
v-for="guide in amountGuide"
:key="guide"
@click="amount = guide"
><div class="text-end">
<b-dropdown v-if="amountGuide" right text="" class="amount-dropdown">
<b-dropdown-item v-for="guide in amountGuide" :key="guide" @click="amount = guide">
<div class="text-end">
{{ guide.toLocaleString() }}
</div></b-dropdown-item
>
</div>
</b-dropdown-item>
</b-dropdown>
<b-button
v-if="maxAmount > 200"
class="btn-sm"
@click="amount = Math.min(amount + 100, maxAmount)"
>+</b-button
>
<b-button
v-if="maxAmount > 2000"
class="btn-sm"
@click="amount = Math.min(amount + 1000, maxAmount)"
>+</b-button
>
<b-button
v-if="maxAmount > 20000"
class="btn-sm"
@click="amount = Math.min(amount + 10000, maxAmount)"
>+</b-button
>
<b-button v-if="maxAmount > 200" class="btn-sm" @click="amount = Math.min(amount + 100, maxAmount)"> + </b-button>
<b-button v-if="maxAmount > 2000" class="btn-sm" @click="amount = Math.min(amount + 1000, maxAmount)">
+
</b-button>
<b-button v-if="maxAmount > 20000" class="btn-sm" @click="amount = Math.min(amount + 10000, maxAmount)">
+
</b-button>
</div>
</template>
<script lang="ts">
import { defineComponent, PropType } from "vue";
import { defineComponent } from "vue";
import VueTypes from "vue-types";
export default defineComponent({
props: {
modelValue: {
type: Number,
required: true,
},
minAmount: {
type: Number,
required: true,
},
maxAmount: {
type: Number,
required: true,
},
amountGuide: {
type: Array as PropType<number[]>,
required: false,
},
step: {
type: Number,
required: false,
default: 1,
},
modelValue: VueTypes.number.isRequired,
minAmount: VueTypes.number.isRequired,
maxAmount: VueTypes.number.isRequired,
amountGuide: VueTypes.arrayOf(Number).def([1000, 2000, 5000, 10000]),
step: VueTypes.number.def(1),
},
emits: ["update:modelValue"],
watch: {
amount(val: number) {
this.$emit("update:modelValue", val);
},
},
data() {
return {
amount: this.modelValue,
};
},
watch: {
amount(val: number) {
this.$emit("update:modelValue", val);
},
},
});
</script>
<style lang="scss">
.btn-group.amount-dropdown > .btn {
border-radius: 0;
@@ -107,4 +66,4 @@ export default defineComponent({
.btn-group.amount-dropdown .dropdown-menu.show {
min-width: 6rem;
}
</style>
</style>
+17 -15
View File
@@ -16,28 +16,30 @@
:maxHeight="400"
:searchable="searchable"
>
<template v-slot:option="props"
<template #option="props"
><span
:style="{
color: props.option.notAvailable ? 'red' : undefined,
}"
>
{{ props.option.title }}
<span v-if="props.option.info">({{ props.option.info }})</span> {{ props.option.notAvailable ? "(불가)" : undefined }}</span
<span v-if="props.option.info">({{ props.option.info }})</span>
{{ props.option.notAvailable ? "(불가)" : undefined }}</span
>
</template>
<template v-slot:singleLabel="props">
<template #singleLabel="props">
<span
:style="{
color: props.option.notAvailable ? 'red' : undefined,
}"
>{{ props.option.simpleName }} {{ props.option.notAvailable ? "(불가)" : undefined }}</span>
>{{ props.option.simpleName }} {{ props.option.notAvailable ? "(불가)" : undefined }}</span
>
</template>
</v-multiselect>
</template>
<script lang="ts">
import { convertSearch초성 } from "@/util/convertSearch초성";
import { defineComponent, PropType } from "vue";
import { defineComponent, type PropType } from "vue";
type SelectedCity = {
value: number;
@@ -65,15 +67,6 @@ export default defineComponent({
},
},
emits: ["update:modelValue"],
watch: {
modelValue(val: number) {
const target = this.targets.get(val);
this.selectedCity = target;
},
selectedCity(val: SelectedCity) {
this.$emit("update:modelValue", val.value);
},
},
data() {
const citiesForFind = [];
const targets = new Map<number, SelectedCity>();
@@ -84,7 +77,7 @@ export default defineComponent({
title: name,
info: info,
simpleName: name,
searchText: convertSearch초성(name).join('|'),
searchText: convertSearch초성(name).join("|"),
};
if (value == this.modelValue) {
selectedCity = obj;
@@ -98,5 +91,14 @@ export default defineComponent({
targets,
};
},
watch: {
modelValue(val: number) {
const target = this.targets.get(val);
this.selectedCity = target;
},
selectedCity(val: SelectedCity) {
this.$emit("update:modelValue", val.value);
},
},
});
</script>
+18 -16
View File
@@ -16,7 +16,7 @@
:maxHeight="400"
:searchable="false"
>
<template v-slot:option="props">
<template #option="props">
<div
:class="`sam-color-${props.option.title.slice(1)}`"
:style="{
@@ -33,27 +33,29 @@
</div>
</div>
</template>
<template v-slot:singleLabel="props">
<template #singleLabel="props">
<div
:class="`sam-color-${props.option.title.slice(1)}`"
:style="{
margin: '-0.25rem -0.75rem',
}"
><div
>
<div
class="sam-nation-own-bgcolor"
:style="{
padding: '0.30rem 0.75rem',
borderRadius: '0.25rem',
}"
>{{ props.option.title }}</div
></div
>
>
{{ props.option.title }}
</div>
</div>
</template>
</v-multiselect>
</template>
<script lang="ts">
import { unwrap } from "@/util/unwrap";
import { defineComponent, PropType } from "vue";
import { defineComponent, type PropType } from "vue";
type SelectedColor = {
value: number;
@@ -72,15 +74,6 @@ export default defineComponent({
},
},
emits: ["update:modelValue"],
watch: {
modelValue(val: number) {
const target = unwrap(this.targets.get(val));
this.selectedColor = target;
},
selectedColor(val: SelectedColor) {
this.$emit("update:modelValue", val.value);
},
},
data() {
const forFind = [];
const targets = new Map<number, SelectedColor>();
@@ -101,5 +94,14 @@ export default defineComponent({
targets,
};
},
watch: {
modelValue(val: number) {
const target = unwrap(this.targets.get(val));
this.selectedColor = target;
},
selectedColor(val: SelectedColor) {
this.$emit("update:modelValue", val.value);
},
},
});
</script>
+28 -40
View File
@@ -19,25 +19,23 @@
:maxHeight="400"
:searchable="searchable"
>
<template v-slot:option="props">
<div v-if="props.option.title" v-html="props.option.title"></div>
<template #option="props">
<!-- eslint-disable-next-line vue/no-v-html -->
<div v-if="props.option.title" v-html="props.option.title" />
<div
v-if="props.option.$groupLabel !== undefined"
class="margin-filler"
:style="{
backgroundColor: groupByNation.get(props.option.$groupLabel).color,
color: isBrightColor(
groupByNation.get(props.option.$groupLabel).color
)
? 'black'
: 'white',
backgroundColor: groupByNation?.get(props.option.$groupLabel)?.color,
color: isBrightColor(groupByNation?.get(props.option.$groupLabel)?.color ?? '#ffffff') ? 'black' : 'white',
}"
>
{{ groupByNation.get(props.option.$groupLabel).name }}
{{ groupByNation?.get(props.option.$groupLabel)?.name }}
</div>
</template>
<template v-slot:singleLabel="props">
{{ props.option.simpleName }} {{groupByNation?`[${groupByNation.get(props.option.obj.nationID).name}]`:undefined}}
<template #singleLabel="props">
{{ props.option.simpleName }}
{{ groupByNation ? `[${groupByNation.get(props.option.obj.nationID)?.name}]` : undefined }}
</template>
</v-multiselect>
</template>
@@ -46,12 +44,9 @@ import { getNpcColor } from "@/common_legacy";
import { convertSearch초성 } from "@/util/convertSearch초성";
import { isBrightColor } from "@/util/isBrightColor";
import { unwrap } from "@/util/unwrap";
import { defineComponent, PropType } from "vue";
import {
procGeneralItem,
procGeneralList,
procNationItem,
} from "./processingRes";
import { defineComponent, type PropType } from "vue";
import VueTypes from "vue-types";
import type { procGeneralItem, procGeneralList, procNationItem } from "./processingRes";
type SelectedGeneral = {
value: number;
@@ -63,10 +58,7 @@ type SelectedGeneral = {
export default defineComponent({
props: {
modelValue: {
type: Number,
required: true,
},
modelValue: VueTypes.number.isRequired,
generals: {
type: Array as PropType<procGeneralList>,
required: true,
@@ -81,22 +73,13 @@ export default defineComponent({
required: false,
default: undefined,
},
searchable:{
searchable: {
type: Boolean,
required: false,
default: true,
}
},
},
emits: ["update:modelValue"],
watch: {
modelValue(val: number) {
const target = this.targets.get(val);
this.selectedGeneral = target;
},
selectedGeneral(val: SelectedGeneral) {
this.$emit("update:modelValue", val.value);
},
},
data() {
const forFind: (
| SelectedGeneral
@@ -130,17 +113,13 @@ export default defineComponent({
}
const nameColor = getNpcColor(gen.npc);
const name = nameColor
? `<span style="color:${nameColor}">${gen.name}</span>`
: gen.name;
const name = nameColor ? `<span style="color:${nameColor}">${gen.name}</span>` : gen.name;
const obj: SelectedGeneral = {
value: gen.no,
title: this.textHelper
? this.textHelper(gen)
: `${name} (${gen.leadership}/${gen.strength}/${gen.intel})`,
title: this.textHelper ? this.textHelper(gen) : `${name} (${gen.leadership}/${gen.strength}/${gen.intel})`,
simpleName: gen.name,
searchText: convertSearch초성(gen.name).join('|'),
searchText: convertSearch초성(gen.name).join("|"),
obj: gen,
};
if (gen.no == this.modelValue) {
@@ -156,6 +135,15 @@ export default defineComponent({
isBrightColor,
};
},
watch: {
modelValue(val: number) {
const target = this.targets.get(val);
this.selectedGeneral = target;
},
selectedGeneral(val: SelectedGeneral) {
this.$emit("update:modelValue", val.value);
},
},
});
</script>
<style lang="scss">
@@ -186,4 +174,4 @@ export default defineComponent({
padding: calc($vue-multiselect-padding-y + 0.17em) $vue-multiselect-padding-x;
}
}
</style>
</style>
+15 -15
View File
@@ -16,7 +16,7 @@
:maxHeight="400"
:searchable="searchable"
>
<template v-slot:option="props">
<template #option="props">
<span
:style="{
color: props.option.notAvailable ? 'red' : undefined,
@@ -27,7 +27,7 @@
{{ props.option.notAvailable ? "(불가)" : undefined }}
</span>
</template>
<template v-slot:singleLabel="props">
<template #singleLabel="props">
<span
:style="{
color: props.option.notAvailable ? 'red' : undefined,
@@ -41,8 +41,8 @@
</template>
<script lang="ts">
import { convertSearch초성 } from "@/util/convertSearch초성";
import { defineComponent, PropType } from "vue";
import { procNationItem } from "./processingRes";
import { defineComponent, type PropType } from "vue";
import type { procNationItem } from "./processingRes";
type SelectedNation = {
value: number;
@@ -67,18 +67,9 @@ export default defineComponent({
type: Boolean,
required: false,
default: true,
}
},
},
emits: ["update:modelValue"],
watch: {
modelValue(val: number) {
const target = this.targets.get(val);
this.selectedNation = target;
},
selectedNation(val: SelectedNation) {
this.$emit("update:modelValue", val.value);
},
},
data() {
const forFind = [];
const targets = new Map<number, SelectedNation>();
@@ -90,7 +81,7 @@ export default defineComponent({
info: nationItem.info,
simpleName: nationItem.name,
notAvailable: nationItem.notAvailable,
searchText: convertSearch초성(nationItem.name).join('|'),
searchText: convertSearch초성(nationItem.name).join("|"),
};
if (nationItem.id == this.modelValue) {
selectedNation = obj;
@@ -104,5 +95,14 @@ export default defineComponent({
targets,
};
},
watch: {
modelValue(val: number) {
const target = this.targets.get(val);
this.selectedNation = target;
},
selectedNation(val: SelectedNation) {
this.$emit("update:modelValue", val.value);
},
},
});
</script>
+1961 -6
View File
@@ -56,7 +56,9 @@
"downloadjs": "^1.4.7",
"esbuild-loader": "^2.18.0",
"eslint": "^8.11.0",
"eslint-config-prettier": "^8.5.0",
"eslint-import-resolver-alias": "^1.1.2",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-vue": "^8.5.0",
"file-loader": "^6.2.0",
"jquery": "^3.6.0",
@@ -70,6 +72,8 @@
"postcss-import": "^14.0.2",
"postcss-loader": "^6.2.1",
"pre-commit": "^1.2.2",
"prettier": "^2.6.1",
"prettier-eslint": "^8.2.2",
"query-string": "^7.1.1",
"sass": "^1.49.9",
"sass-loader": "^12.6.0",
@@ -3128,6 +3132,14 @@
"node": ">=6"
}
},
"node_modules/ansi-escapes": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz",
"integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==",
"engines": {
"node": ">=4"
}
},
"node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
@@ -3248,6 +3260,71 @@
"follow-redirects": "^1.14.8"
}
},
"node_modules/babel-code-frame": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
"integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
"dependencies": {
"chalk": "^1.1.3",
"esutils": "^2.0.2",
"js-tokens": "^3.0.2"
}
},
"node_modules/babel-code-frame/node_modules/ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/babel-code-frame/node_modules/ansi-styles": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
"integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/babel-code-frame/node_modules/chalk": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dependencies": {
"ansi-styles": "^2.2.1",
"escape-string-regexp": "^1.0.2",
"has-ansi": "^2.0.0",
"strip-ansi": "^3.0.0",
"supports-color": "^2.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/babel-code-frame/node_modules/js-tokens": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
"integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls="
},
"node_modules/babel-code-frame/node_modules/strip-ansi": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dependencies": {
"ansi-regex": "^2.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/babel-code-frame/node_modules/supports-color": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
"integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/babel-plugin-dynamic-import-node": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz",
@@ -3479,6 +3556,25 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/caller-path": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz",
"integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=",
"dependencies": {
"callsites": "^0.2.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/caller-path/node_modules/callsites": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz",
"integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@@ -3559,6 +3655,11 @@
"node": ">=4"
}
},
"node_modules/chardet": {
"version": "0.4.2",
"resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz",
"integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I="
},
"node_modules/check-error": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
@@ -3643,6 +3744,12 @@
"node": ">=6.0"
}
},
"node_modules/circular-json": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz",
"integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==",
"deprecated": "CircularJSON is in maintenance only, flatted is its successor."
},
"node_modules/clean-terminal-webpack-plugin": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/clean-terminal-webpack-plugin/-/clean-terminal-webpack-plugin-3.0.0.tgz",
@@ -3651,6 +3758,22 @@
"webpack": "^4.0.0 || ^5.0.0"
}
},
"node_modules/cli-cursor": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
"integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
"dependencies": {
"restore-cursor": "^2.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/cli-width": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz",
"integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw=="
},
"node_modules/cliui": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
@@ -3674,6 +3797,15 @@
"node": ">=6"
}
},
"node_modules/co": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
"integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
"engines": {
"iojs": ">= 1.0.0",
"node": ">= 0.12.0"
}
},
"node_modules/color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@@ -3700,6 +3832,14 @@
"node": ">= 6"
}
},
"node_modules/common-tags": {
"version": "1.8.2",
"resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz",
"integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==",
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -4104,6 +4244,11 @@
"node": ">=8"
}
},
"node_modules/dlv": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="
},
"node_modules/doctrine": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
@@ -4651,6 +4796,17 @@
"url": "https://opencollective.com/eslint"
}
},
"node_modules/eslint-config-prettier": {
"version": "8.5.0",
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz",
"integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==",
"bin": {
"eslint-config-prettier": "bin/cli.js"
},
"peerDependencies": {
"eslint": ">=7.0.0"
}
},
"node_modules/eslint-import-resolver-alias": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/eslint-import-resolver-alias/-/eslint-import-resolver-alias-1.1.2.tgz",
@@ -4837,6 +4993,26 @@
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"peer": true
},
"node_modules/eslint-plugin-prettier": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.0.0.tgz",
"integrity": "sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ==",
"dependencies": {
"prettier-linter-helpers": "^1.0.0"
},
"engines": {
"node": ">=6.0.0"
},
"peerDependencies": {
"eslint": ">=7.28.0",
"prettier": ">=2.0.0"
},
"peerDependenciesMeta": {
"eslint-config-prettier": {
"optional": true
}
}
},
"node_modules/eslint-plugin-vue": {
"version": "8.5.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-8.5.0.tgz",
@@ -5062,6 +5238,18 @@
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/esprima": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
"bin": {
"esparse": "bin/esparse.js",
"esvalidate": "bin/esvalidate.js"
},
"engines": {
"node": ">=4"
}
},
"node_modules/esquery": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz",
@@ -5151,11 +5339,29 @@
"url": "https://github.com/sindresorhus/execa?sponsor=1"
}
},
"node_modules/external-editor": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz",
"integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==",
"dependencies": {
"chardet": "^0.4.0",
"iconv-lite": "^0.4.17",
"tmp": "^0.0.33"
},
"engines": {
"node": ">=0.12"
}
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
},
"node_modules/fast-diff": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
"integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w=="
},
"node_modules/fast-glob": {
"version": "3.2.11",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz",
@@ -5194,6 +5400,17 @@
"reusify": "^1.0.4"
}
},
"node_modules/figures": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
"integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
"dependencies": {
"escape-string-regexp": "^1.0.5"
},
"engines": {
"node": ">=4"
}
},
"node_modules/file-entry-cache": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
@@ -5528,6 +5745,25 @@
"node": ">= 0.4.0"
}
},
"node_modules/has-ansi": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
"integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
"dependencies": {
"ansi-regex": "^2.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/has-ansi/node_modules/ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/has-bigints": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz",
@@ -5595,6 +5831,17 @@
"node": ">=10.17.0"
}
},
"node_modules/iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/icss-utils": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz",
@@ -5684,6 +5931,14 @@
"node": ">=0.8.19"
}
},
"node_modules/indent-string": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz",
"integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=",
"engines": {
"node": ">=4"
}
},
"node_modules/inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
@@ -5698,6 +5953,66 @@
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/inquirer": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz",
"integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==",
"dependencies": {
"ansi-escapes": "^3.0.0",
"chalk": "^2.0.0",
"cli-cursor": "^2.1.0",
"cli-width": "^2.0.0",
"external-editor": "^2.0.4",
"figures": "^2.0.0",
"lodash": "^4.3.0",
"mute-stream": "0.0.7",
"run-async": "^2.2.0",
"rx-lite": "^4.0.8",
"rx-lite-aggregates": "^4.0.8",
"string-width": "^2.1.0",
"strip-ansi": "^4.0.0",
"through": "^2.3.6"
}
},
"node_modules/inquirer/node_modules/ansi-regex": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz",
"integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==",
"engines": {
"node": ">=4"
}
},
"node_modules/inquirer/node_modules/is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
"engines": {
"node": ">=4"
}
},
"node_modules/inquirer/node_modules/string-width": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
"dependencies": {
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^4.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/inquirer/node_modules/strip-ansi": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
"dependencies": {
"ansi-regex": "^3.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/internal-slot": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz",
@@ -5880,6 +6195,11 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-resolvable": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz",
"integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg=="
},
"node_modules/is-shared-array-buffer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz",
@@ -6224,6 +6544,11 @@
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
},
"node_modules/lodash.unescape": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz",
"integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw="
},
"node_modules/lodash.uniq": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
@@ -6308,6 +6633,77 @@
"node": ">=8"
}
},
"node_modules/loglevel": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.0.tgz",
"integrity": "sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA==",
"engines": {
"node": ">= 0.6.0"
},
"funding": {
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/loglevel"
}
},
"node_modules/loglevel-colored-level-prefix": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/loglevel-colored-level-prefix/-/loglevel-colored-level-prefix-1.0.0.tgz",
"integrity": "sha1-akAhj9x64V/HbD0PPmdsRlOIYD4=",
"dependencies": {
"chalk": "^1.1.3",
"loglevel": "^1.4.1"
}
},
"node_modules/loglevel-colored-level-prefix/node_modules/ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/loglevel-colored-level-prefix/node_modules/ansi-styles": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
"integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/loglevel-colored-level-prefix/node_modules/chalk": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dependencies": {
"ansi-styles": "^2.2.1",
"escape-string-regexp": "^1.0.2",
"has-ansi": "^2.0.0",
"strip-ansi": "^3.0.0",
"supports-color": "^2.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/loglevel-colored-level-prefix/node_modules/strip-ansi": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dependencies": {
"ansi-regex": "^2.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/loglevel-colored-level-prefix/node_modules/supports-color": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
"integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/loupe": {
"version": "2.3.4",
"resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz",
@@ -6505,9 +6901,20 @@
}
},
"node_modules/minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
},
"node_modules/mkdirp": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
"dependencies": {
"minimist": "^1.2.6"
},
"bin": {
"mkdirp": "bin/cmd.js"
}
},
"node_modules/mocha": {
"version": "9.2.2",
@@ -6656,6 +7063,11 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"node_modules/mute-stream": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
"integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s="
},
"node_modules/nanoid": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz",
@@ -6824,6 +7236,14 @@
"url": "https://github.com/fb55/nth-check?sponsor=1"
}
},
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/object-inspect": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.1.tgz",
@@ -6933,6 +7353,14 @@
"node": ">= 0.4.0"
}
},
"node_modules/os-tmpdir": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/p-limit": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
@@ -7024,6 +7452,11 @@
"node": ">=0.10.0"
}
},
"node_modules/path-is-inside": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
"integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM="
},
"node_modules/path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
@@ -7099,6 +7532,14 @@
"node": ">=8"
}
},
"node_modules/pluralize": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz",
"integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==",
"engines": {
"node": ">=4"
}
},
"node_modules/postcss": {
"version": "8.4.8",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.8.tgz",
@@ -7691,11 +8132,442 @@
"node": ">= 0.8.0"
}
},
"node_modules/prettier": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.1.tgz",
"integrity": "sha512-8UVbTBYGwN37Bs9LERmxCPjdvPxlEowx2urIL6urHzdb3SDq4B/Z6xLFCblrSnE4iKWcS6ziJ3aOYrc1kz/E2A==",
"bin": {
"prettier": "bin-prettier.js"
},
"engines": {
"node": ">=10.13.0"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/prettier-eslint": {
"version": "8.2.2",
"resolved": "https://registry.npmjs.org/prettier-eslint/-/prettier-eslint-8.2.2.tgz",
"integrity": "sha512-zeR/ZfoENuKupTd+l49aUSCKGAheIPIpZFErK2xJhPfqubg2iJy2velL72AnGkgXsXiyLwAthxoXHoL2HASctw==",
"dependencies": {
"common-tags": "^1.4.0",
"dlv": "^1.1.0",
"eslint": "^4.5.0",
"indent-string": "^3.2.0",
"lodash.merge": "^4.6.0",
"loglevel-colored-level-prefix": "^1.0.0",
"prettier": "^1.7.1",
"pretty-format": "^20.0.3",
"require-relative": "^0.8.7",
"typescript": "^2.5.1",
"typescript-eslint-parser": "^8.0.0"
},
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/prettier-eslint/node_modules/acorn": {
"version": "5.7.4",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz",
"integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==",
"bin": {
"acorn": "bin/acorn"
},
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/prettier-eslint/node_modules/acorn-jsx": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz",
"integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=",
"dependencies": {
"acorn": "^3.0.4"
}
},
"node_modules/prettier-eslint/node_modules/acorn-jsx/node_modules/acorn": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
"integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=",
"bin": {
"acorn": "bin/acorn"
},
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/prettier-eslint/node_modules/ajv": {
"version": "5.5.2",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
"integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
"dependencies": {
"co": "^4.6.0",
"fast-deep-equal": "^1.0.0",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.3.0"
}
},
"node_modules/prettier-eslint/node_modules/ansi-regex": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz",
"integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==",
"engines": {
"node": ">=4"
}
},
"node_modules/prettier-eslint/node_modules/argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
"dependencies": {
"sprintf-js": "~1.0.2"
}
},
"node_modules/prettier-eslint/node_modules/cross-spawn": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
"integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
"dependencies": {
"lru-cache": "^4.0.1",
"shebang-command": "^1.2.0",
"which": "^1.2.9"
}
},
"node_modules/prettier-eslint/node_modules/debug": {
"version": "3.2.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
"dependencies": {
"ms": "^2.1.1"
}
},
"node_modules/prettier-eslint/node_modules/doctrine": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
"integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
"dependencies": {
"esutils": "^2.0.2"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/prettier-eslint/node_modules/eslint": {
"version": "4.19.1",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-4.19.1.tgz",
"integrity": "sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ==",
"dependencies": {
"ajv": "^5.3.0",
"babel-code-frame": "^6.22.0",
"chalk": "^2.1.0",
"concat-stream": "^1.6.0",
"cross-spawn": "^5.1.0",
"debug": "^3.1.0",
"doctrine": "^2.1.0",
"eslint-scope": "^3.7.1",
"eslint-visitor-keys": "^1.0.0",
"espree": "^3.5.4",
"esquery": "^1.0.0",
"esutils": "^2.0.2",
"file-entry-cache": "^2.0.0",
"functional-red-black-tree": "^1.0.1",
"glob": "^7.1.2",
"globals": "^11.0.1",
"ignore": "^3.3.3",
"imurmurhash": "^0.1.4",
"inquirer": "^3.0.6",
"is-resolvable": "^1.0.0",
"js-yaml": "^3.9.1",
"json-stable-stringify-without-jsonify": "^1.0.1",
"levn": "^0.3.0",
"lodash": "^4.17.4",
"minimatch": "^3.0.2",
"mkdirp": "^0.5.1",
"natural-compare": "^1.4.0",
"optionator": "^0.8.2",
"path-is-inside": "^1.0.2",
"pluralize": "^7.0.0",
"progress": "^2.0.0",
"regexpp": "^1.0.1",
"require-uncached": "^1.0.3",
"semver": "^5.3.0",
"strip-ansi": "^4.0.0",
"strip-json-comments": "~2.0.1",
"table": "4.0.2",
"text-table": "~0.2.0"
},
"bin": {
"eslint": "bin/eslint.js"
},
"engines": {
"node": ">=4"
}
},
"node_modules/prettier-eslint/node_modules/eslint-scope": {
"version": "3.7.3",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.3.tgz",
"integrity": "sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA==",
"dependencies": {
"esrecurse": "^4.1.0",
"estraverse": "^4.1.1"
},
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/prettier-eslint/node_modules/eslint-visitor-keys": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
"integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
"engines": {
"node": ">=4"
}
},
"node_modules/prettier-eslint/node_modules/espree": {
"version": "3.5.4",
"resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz",
"integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==",
"dependencies": {
"acorn": "^5.5.0",
"acorn-jsx": "^3.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/prettier-eslint/node_modules/fast-deep-equal": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
"integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ="
},
"node_modules/prettier-eslint/node_modules/file-entry-cache": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz",
"integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=",
"dependencies": {
"flat-cache": "^1.2.1",
"object-assign": "^4.0.1"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/prettier-eslint/node_modules/flat-cache": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz",
"integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==",
"dependencies": {
"circular-json": "^0.3.1",
"graceful-fs": "^4.1.2",
"rimraf": "~2.6.2",
"write": "^0.2.1"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/prettier-eslint/node_modules/ignore": {
"version": "3.3.10",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz",
"integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug=="
},
"node_modules/prettier-eslint/node_modules/js-yaml": {
"version": "3.14.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
"dependencies": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
},
"bin": {
"js-yaml": "bin/js-yaml.js"
}
},
"node_modules/prettier-eslint/node_modules/json-schema-traverse": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
"integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A="
},
"node_modules/prettier-eslint/node_modules/levn": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
"integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
"dependencies": {
"prelude-ls": "~1.1.2",
"type-check": "~0.3.2"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/prettier-eslint/node_modules/optionator": {
"version": "0.8.3",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
"integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
"dependencies": {
"deep-is": "~0.1.3",
"fast-levenshtein": "~2.0.6",
"levn": "~0.3.0",
"prelude-ls": "~1.1.2",
"type-check": "~0.3.2",
"word-wrap": "~1.2.3"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/prettier-eslint/node_modules/prelude-ls": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
"integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/prettier-eslint/node_modules/prettier": {
"version": "1.19.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz",
"integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==",
"bin": {
"prettier": "bin-prettier.js"
},
"engines": {
"node": ">=4"
}
},
"node_modules/prettier-eslint/node_modules/regexpp": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz",
"integrity": "sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw==",
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/prettier-eslint/node_modules/rimraf": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
"integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
"dependencies": {
"glob": "^7.1.3"
},
"bin": {
"rimraf": "bin.js"
}
},
"node_modules/prettier-eslint/node_modules/shebang-command": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
"integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
"dependencies": {
"shebang-regex": "^1.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/prettier-eslint/node_modules/shebang-regex": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
"integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/prettier-eslint/node_modules/strip-ansi": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
"dependencies": {
"ansi-regex": "^3.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/prettier-eslint/node_modules/strip-json-comments": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/prettier-eslint/node_modules/type-check": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
"integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
"dependencies": {
"prelude-ls": "~1.1.2"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/prettier-eslint/node_modules/typescript": {
"version": "2.9.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz",
"integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=4.2.0"
}
},
"node_modules/prettier-eslint/node_modules/which": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
"integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
"dependencies": {
"isexe": "^2.0.0"
},
"bin": {
"which": "bin/which"
}
},
"node_modules/prettier-linter-helpers": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
"integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
"dependencies": {
"fast-diff": "^1.1.2"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/pretty-format": {
"version": "20.0.3",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-20.0.3.tgz",
"integrity": "sha1-Ag41ClYKH+GpjcO+tsz/s4beixQ=",
"dependencies": {
"ansi-regex": "^2.1.1",
"ansi-styles": "^3.0.0"
}
},
"node_modules/pretty-format/node_modules/ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
},
"node_modules/progress": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/prosemirror-commands": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.2.1.tgz",
@@ -8021,6 +8893,31 @@
"resolved": "https://registry.npmjs.org/require-package-name/-/require-package-name-2.0.1.tgz",
"integrity": "sha1-wR6XJ2tluOKSP3Xav1+y7ww4Qbk="
},
"node_modules/require-relative": {
"version": "0.8.7",
"resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz",
"integrity": "sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4="
},
"node_modules/require-uncached": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz",
"integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=",
"dependencies": {
"caller-path": "^0.1.0",
"resolve-from": "^1.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/require-uncached/node_modules/resolve-from": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz",
"integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/resolve": {
"version": "1.20.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
@@ -8052,6 +8949,37 @@
"node": ">=8"
}
},
"node_modules/restore-cursor": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
"integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
"dependencies": {
"onetime": "^2.0.0",
"signal-exit": "^3.0.2"
},
"engines": {
"node": ">=4"
}
},
"node_modules/restore-cursor/node_modules/mimic-fn": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
"integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==",
"engines": {
"node": ">=4"
}
},
"node_modules/restore-cursor/node_modules/onetime": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
"integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
"dependencies": {
"mimic-fn": "^1.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/reusify": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
@@ -8080,6 +9008,14 @@
"resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.2.tgz",
"integrity": "sha512-ku6MFrwEVSVmXLvy3dYph3LAMNS0890K7fabn+0YIRQ2T96T9F4gkFf0vf0WW0JUraNWwGRtInEpH7yO4tbQZg=="
},
"node_modules/run-async": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
"integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==",
"engines": {
"node": ">=0.12.0"
}
},
"node_modules/run-parallel": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
@@ -8102,11 +9038,29 @@
"queue-microtask": "^1.2.2"
}
},
"node_modules/rx-lite": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz",
"integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ="
},
"node_modules/rx-lite-aggregates": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz",
"integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=",
"dependencies": {
"rx-lite": "*"
}
},
"node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"node_modules/sass": {
"version": "1.49.9",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.49.9.tgz",
@@ -8272,6 +9226,25 @@
"node": ">=6"
}
},
"node_modules/slice-ansi": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz",
"integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==",
"dependencies": {
"is-fullwidth-code-point": "^2.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/slice-ansi/node_modules/is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
"engines": {
"node": ">=4"
}
},
"node_modules/sortablejs": {
"version": "1.14.0",
"resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.14.0.tgz",
@@ -8366,6 +9339,11 @@
"node": ">=6"
}
},
"node_modules/sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
},
"node_modules/stable": {
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz",
@@ -8547,6 +9525,87 @@
"node": ">= 10"
}
},
"node_modules/table": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz",
"integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==",
"dependencies": {
"ajv": "^5.2.3",
"ajv-keywords": "^2.1.0",
"chalk": "^2.1.0",
"lodash": "^4.17.4",
"slice-ansi": "1.0.0",
"string-width": "^2.1.1"
}
},
"node_modules/table/node_modules/ajv": {
"version": "5.5.2",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
"integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
"dependencies": {
"co": "^4.6.0",
"fast-deep-equal": "^1.0.0",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.3.0"
}
},
"node_modules/table/node_modules/ajv-keywords": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz",
"integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=",
"peerDependencies": {
"ajv": "^5.0.0"
}
},
"node_modules/table/node_modules/ansi-regex": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz",
"integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==",
"engines": {
"node": ">=4"
}
},
"node_modules/table/node_modules/fast-deep-equal": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
"integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ="
},
"node_modules/table/node_modules/is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
"engines": {
"node": ">=4"
}
},
"node_modules/table/node_modules/json-schema-traverse": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
"integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A="
},
"node_modules/table/node_modules/string-width": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
"dependencies": {
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^4.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/table/node_modules/strip-ansi": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
"dependencies": {
"ansi-regex": "^3.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/tapable": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.0.tgz",
@@ -8620,6 +9679,11 @@
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
"integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ="
},
"node_modules/through": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
},
"node_modules/timsort": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
@@ -8633,6 +9697,17 @@
"@popperjs/core": "^2.9.0"
}
},
"node_modules/tmp": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
"integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
"dependencies": {
"os-tmpdir": "~1.0.2"
},
"engines": {
"node": ">=0.6.0"
}
},
"node_modules/to-fast-properties": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
@@ -8790,6 +9865,29 @@
"node": ">=4.2.0"
}
},
"node_modules/typescript-eslint-parser": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/typescript-eslint-parser/-/typescript-eslint-parser-8.0.1.tgz",
"integrity": "sha1-6MrFN9mW4Ww9uw18TVCXmeZ6/gw=",
"dependencies": {
"lodash.unescape": "4.0.1",
"semver": "5.4.1"
},
"engines": {
"node": ">=4"
},
"peerDependencies": {
"typescript": "*"
}
},
"node_modules/typescript-eslint-parser/node_modules/semver": {
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz",
"integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==",
"bin": {
"semver": "bin/semver"
}
},
"node_modules/unbox-primitive": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz",
@@ -9480,6 +10578,17 @@
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
},
"node_modules/write": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz",
"integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=",
"dependencies": {
"mkdirp": "^0.5.1"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/ws": {
"version": "7.5.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz",
@@ -11723,6 +12832,11 @@
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
"integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA=="
},
"ansi-escapes": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz",
"integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ=="
},
"ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
@@ -11806,6 +12920,58 @@
"follow-redirects": "^1.14.8"
}
},
"babel-code-frame": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
"integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
"requires": {
"chalk": "^1.1.3",
"esutils": "^2.0.2",
"js-tokens": "^3.0.2"
},
"dependencies": {
"ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
},
"ansi-styles": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
"integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
},
"chalk": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"requires": {
"ansi-styles": "^2.2.1",
"escape-string-regexp": "^1.0.2",
"has-ansi": "^2.0.0",
"strip-ansi": "^3.0.0",
"supports-color": "^2.0.0"
}
},
"js-tokens": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
"integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls="
},
"strip-ansi": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"requires": {
"ansi-regex": "^2.0.0"
}
},
"supports-color": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
"integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
}
}
},
"babel-plugin-dynamic-import-node": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz",
@@ -11965,6 +13131,21 @@
"get-intrinsic": "^1.0.2"
}
},
"caller-path": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz",
"integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=",
"requires": {
"callsites": "^0.2.0"
},
"dependencies": {
"callsites": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz",
"integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo="
}
}
},
"callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@@ -12021,6 +13202,11 @@
"supports-color": "^5.3.0"
}
},
"chardet": {
"version": "0.4.2",
"resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz",
"integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I="
},
"check-error": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
@@ -12078,12 +13264,30 @@
"resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz",
"integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg=="
},
"circular-json": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz",
"integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A=="
},
"clean-terminal-webpack-plugin": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/clean-terminal-webpack-plugin/-/clean-terminal-webpack-plugin-3.0.0.tgz",
"integrity": "sha512-wcgkQZmwEWYYjHblXc0+UGFDtx37S+1qgUQl4EOhhinzSHbZpixWBiasQ91RoCMf5lAm67j1XOt9z+HN+sWkWA==",
"requires": {}
},
"cli-cursor": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
"integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
"requires": {
"restore-cursor": "^2.0.0"
}
},
"cli-width": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz",
"integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw=="
},
"cliui": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
@@ -12104,6 +13308,11 @@
"shallow-clone": "^3.0.0"
}
},
"co": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
"integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ="
},
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@@ -12127,6 +13336,11 @@
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
"integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="
},
"common-tags": {
"version": "1.8.2",
"resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz",
"integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA=="
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -12410,6 +13624,11 @@
"path-type": "^4.0.0"
}
},
"dlv": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="
},
"doctrine": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
@@ -12849,6 +14068,12 @@
}
}
},
"eslint-config-prettier": {
"version": "8.5.0",
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz",
"integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==",
"requires": {}
},
"eslint-import-resolver-alias": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/eslint-import-resolver-alias/-/eslint-import-resolver-alias-1.1.2.tgz",
@@ -13003,6 +14228,14 @@
}
}
},
"eslint-plugin-prettier": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.0.0.tgz",
"integrity": "sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ==",
"requires": {
"prettier-linter-helpers": "^1.0.0"
}
},
"eslint-plugin-vue": {
"version": "8.5.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-8.5.0.tgz",
@@ -13076,6 +14309,11 @@
"eslint-visitor-keys": "^3.3.0"
}
},
"esprima": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
},
"esquery": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz",
@@ -13142,11 +14380,26 @@
"strip-final-newline": "^2.0.0"
}
},
"external-editor": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz",
"integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==",
"requires": {
"chardet": "^0.4.0",
"iconv-lite": "^0.4.17",
"tmp": "^0.0.33"
}
},
"fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
},
"fast-diff": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
"integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w=="
},
"fast-glob": {
"version": "3.2.11",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz",
@@ -13182,6 +14435,14 @@
"reusify": "^1.0.4"
}
},
"figures": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
"integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
"requires": {
"escape-string-regexp": "^1.0.5"
}
},
"file-entry-cache": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
@@ -13404,6 +14665,21 @@
"function-bind": "^1.1.1"
}
},
"has-ansi": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
"integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
"requires": {
"ansi-regex": "^2.0.0"
},
"dependencies": {
"ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
}
}
},
"has-bigints": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz",
@@ -13447,6 +14723,14 @@
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
"integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="
},
"iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
},
"icss-utils": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz",
@@ -13498,6 +14782,11 @@
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
"integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o="
},
"indent-string": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz",
"integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok="
},
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
@@ -13512,6 +14801,56 @@
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"inquirer": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz",
"integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==",
"requires": {
"ansi-escapes": "^3.0.0",
"chalk": "^2.0.0",
"cli-cursor": "^2.1.0",
"cli-width": "^2.0.0",
"external-editor": "^2.0.4",
"figures": "^2.0.0",
"lodash": "^4.3.0",
"mute-stream": "0.0.7",
"run-async": "^2.2.0",
"rx-lite": "^4.0.8",
"rx-lite-aggregates": "^4.0.8",
"string-width": "^2.1.0",
"strip-ansi": "^4.0.0",
"through": "^2.3.6"
},
"dependencies": {
"ansi-regex": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz",
"integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw=="
},
"is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
},
"string-width": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
"requires": {
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^4.0.0"
}
},
"strip-ansi": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
"requires": {
"ansi-regex": "^3.0.0"
}
}
}
},
"internal-slot": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz",
@@ -13628,6 +14967,11 @@
"has-tostringtag": "^1.0.0"
}
},
"is-resolvable": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz",
"integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg=="
},
"is-shared-array-buffer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz",
@@ -13882,6 +15226,11 @@
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
},
"lodash.unescape": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz",
"integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw="
},
"lodash.uniq": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
@@ -13941,6 +15290,57 @@
}
}
},
"loglevel": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.0.tgz",
"integrity": "sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA=="
},
"loglevel-colored-level-prefix": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/loglevel-colored-level-prefix/-/loglevel-colored-level-prefix-1.0.0.tgz",
"integrity": "sha1-akAhj9x64V/HbD0PPmdsRlOIYD4=",
"requires": {
"chalk": "^1.1.3",
"loglevel": "^1.4.1"
},
"dependencies": {
"ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
},
"ansi-styles": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
"integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
},
"chalk": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"requires": {
"ansi-styles": "^2.2.1",
"escape-string-regexp": "^1.0.2",
"has-ansi": "^2.0.0",
"strip-ansi": "^3.0.0",
"supports-color": "^2.0.0"
}
},
"strip-ansi": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"requires": {
"ansi-regex": "^2.0.0"
}
},
"supports-color": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
"integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
}
}
},
"loupe": {
"version": "2.3.4",
"resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz",
@@ -14086,9 +15486,17 @@
}
},
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
},
"mkdirp": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
"requires": {
"minimist": "^1.2.6"
}
},
"mocha": {
"version": "9.2.2",
@@ -14189,6 +15597,11 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"mute-stream": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
"integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s="
},
"nanoid": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz",
@@ -14312,6 +15725,11 @@
"boolbase": "^1.0.0"
}
},
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
},
"object-inspect": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.1.tgz",
@@ -14388,6 +15806,11 @@
"resolved": "https://registry.npmjs.org/os-shim/-/os-shim-0.1.3.tgz",
"integrity": "sha1-a2LDeRz3kJ6jXtRuF2WLtBfLORc="
},
"os-tmpdir": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
},
"p-limit": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
@@ -14448,6 +15871,11 @@
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
},
"path-is-inside": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
"integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM="
},
"path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
@@ -14496,6 +15924,11 @@
"find-up": "^4.0.0"
}
},
"pluralize": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz",
"integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow=="
},
"postcss": {
"version": "8.4.8",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.8.tgz",
@@ -14866,11 +16299,339 @@
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="
},
"prettier": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.1.tgz",
"integrity": "sha512-8UVbTBYGwN37Bs9LERmxCPjdvPxlEowx2urIL6urHzdb3SDq4B/Z6xLFCblrSnE4iKWcS6ziJ3aOYrc1kz/E2A=="
},
"prettier-eslint": {
"version": "8.2.2",
"resolved": "https://registry.npmjs.org/prettier-eslint/-/prettier-eslint-8.2.2.tgz",
"integrity": "sha512-zeR/ZfoENuKupTd+l49aUSCKGAheIPIpZFErK2xJhPfqubg2iJy2velL72AnGkgXsXiyLwAthxoXHoL2HASctw==",
"requires": {
"common-tags": "^1.4.0",
"dlv": "^1.1.0",
"eslint": "^4.5.0",
"indent-string": "^3.2.0",
"lodash.merge": "^4.6.0",
"loglevel-colored-level-prefix": "^1.0.0",
"prettier": "^1.7.1",
"pretty-format": "^20.0.3",
"require-relative": "^0.8.7",
"typescript": "^2.5.1",
"typescript-eslint-parser": "^8.0.0"
},
"dependencies": {
"acorn": {
"version": "5.7.4",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz",
"integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg=="
},
"acorn-jsx": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz",
"integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=",
"requires": {
"acorn": "^3.0.4"
},
"dependencies": {
"acorn": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
"integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo="
}
}
},
"ajv": {
"version": "5.5.2",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
"integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
"requires": {
"co": "^4.6.0",
"fast-deep-equal": "^1.0.0",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.3.0"
}
},
"ansi-regex": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz",
"integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw=="
},
"argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
"requires": {
"sprintf-js": "~1.0.2"
}
},
"cross-spawn": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
"integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
"requires": {
"lru-cache": "^4.0.1",
"shebang-command": "^1.2.0",
"which": "^1.2.9"
}
},
"debug": {
"version": "3.2.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
"requires": {
"ms": "^2.1.1"
}
},
"doctrine": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
"integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
"requires": {
"esutils": "^2.0.2"
}
},
"eslint": {
"version": "4.19.1",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-4.19.1.tgz",
"integrity": "sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ==",
"requires": {
"ajv": "^5.3.0",
"babel-code-frame": "^6.22.0",
"chalk": "^2.1.0",
"concat-stream": "^1.6.0",
"cross-spawn": "^5.1.0",
"debug": "^3.1.0",
"doctrine": "^2.1.0",
"eslint-scope": "^3.7.1",
"eslint-visitor-keys": "^1.0.0",
"espree": "^3.5.4",
"esquery": "^1.0.0",
"esutils": "^2.0.2",
"file-entry-cache": "^2.0.0",
"functional-red-black-tree": "^1.0.1",
"glob": "^7.1.2",
"globals": "^11.0.1",
"ignore": "^3.3.3",
"imurmurhash": "^0.1.4",
"inquirer": "^3.0.6",
"is-resolvable": "^1.0.0",
"js-yaml": "^3.9.1",
"json-stable-stringify-without-jsonify": "^1.0.1",
"levn": "^0.3.0",
"lodash": "^4.17.4",
"minimatch": "^3.0.2",
"mkdirp": "^0.5.1",
"natural-compare": "^1.4.0",
"optionator": "^0.8.2",
"path-is-inside": "^1.0.2",
"pluralize": "^7.0.0",
"progress": "^2.0.0",
"regexpp": "^1.0.1",
"require-uncached": "^1.0.3",
"semver": "^5.3.0",
"strip-ansi": "^4.0.0",
"strip-json-comments": "~2.0.1",
"table": "4.0.2",
"text-table": "~0.2.0"
}
},
"eslint-scope": {
"version": "3.7.3",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.3.tgz",
"integrity": "sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA==",
"requires": {
"esrecurse": "^4.1.0",
"estraverse": "^4.1.1"
}
},
"eslint-visitor-keys": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
"integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ=="
},
"espree": {
"version": "3.5.4",
"resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz",
"integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==",
"requires": {
"acorn": "^5.5.0",
"acorn-jsx": "^3.0.0"
}
},
"fast-deep-equal": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
"integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ="
},
"file-entry-cache": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz",
"integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=",
"requires": {
"flat-cache": "^1.2.1",
"object-assign": "^4.0.1"
}
},
"flat-cache": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz",
"integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==",
"requires": {
"circular-json": "^0.3.1",
"graceful-fs": "^4.1.2",
"rimraf": "~2.6.2",
"write": "^0.2.1"
}
},
"ignore": {
"version": "3.3.10",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz",
"integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug=="
},
"js-yaml": {
"version": "3.14.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
"requires": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
}
},
"json-schema-traverse": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
"integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A="
},
"levn": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
"integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
"requires": {
"prelude-ls": "~1.1.2",
"type-check": "~0.3.2"
}
},
"optionator": {
"version": "0.8.3",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
"integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
"requires": {
"deep-is": "~0.1.3",
"fast-levenshtein": "~2.0.6",
"levn": "~0.3.0",
"prelude-ls": "~1.1.2",
"type-check": "~0.3.2",
"word-wrap": "~1.2.3"
}
},
"prelude-ls": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
"integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ="
},
"prettier": {
"version": "1.19.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz",
"integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew=="
},
"regexpp": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz",
"integrity": "sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw=="
},
"rimraf": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
"integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
"requires": {
"glob": "^7.1.3"
}
},
"shebang-command": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
"integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
"requires": {
"shebang-regex": "^1.0.0"
}
},
"shebang-regex": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
"integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM="
},
"strip-ansi": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
"requires": {
"ansi-regex": "^3.0.0"
}
},
"strip-json-comments": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
},
"type-check": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
"integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
"requires": {
"prelude-ls": "~1.1.2"
}
},
"typescript": {
"version": "2.9.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz",
"integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w=="
},
"which": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
"integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
"requires": {
"isexe": "^2.0.0"
}
}
}
},
"prettier-linter-helpers": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
"integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
"requires": {
"fast-diff": "^1.1.2"
}
},
"pretty-format": {
"version": "20.0.3",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-20.0.3.tgz",
"integrity": "sha1-Ag41ClYKH+GpjcO+tsz/s4beixQ=",
"requires": {
"ansi-regex": "^2.1.1",
"ansi-styles": "^3.0.0"
},
"dependencies": {
"ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
}
}
},
"process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
},
"progress": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA=="
},
"prosemirror-commands": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.2.1.tgz",
@@ -15140,6 +16901,27 @@
"resolved": "https://registry.npmjs.org/require-package-name/-/require-package-name-2.0.1.tgz",
"integrity": "sha1-wR6XJ2tluOKSP3Xav1+y7ww4Qbk="
},
"require-relative": {
"version": "0.8.7",
"resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz",
"integrity": "sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4="
},
"require-uncached": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz",
"integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=",
"requires": {
"caller-path": "^0.1.0",
"resolve-from": "^1.0.0"
},
"dependencies": {
"resolve-from": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz",
"integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY="
}
}
},
"resolve": {
"version": "1.20.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
@@ -15162,6 +16944,30 @@
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
"integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="
},
"restore-cursor": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
"integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
"requires": {
"onetime": "^2.0.0",
"signal-exit": "^3.0.2"
},
"dependencies": {
"mimic-fn": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
"integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ=="
},
"onetime": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
"integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
"requires": {
"mimic-fn": "^1.0.0"
}
}
}
},
"reusify": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
@@ -15180,6 +16986,11 @@
"resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.2.tgz",
"integrity": "sha512-ku6MFrwEVSVmXLvy3dYph3LAMNS0890K7fabn+0YIRQ2T96T9F4gkFf0vf0WW0JUraNWwGRtInEpH7yO4tbQZg=="
},
"run-async": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
"integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ=="
},
"run-parallel": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
@@ -15188,11 +16999,29 @@
"queue-microtask": "^1.2.2"
}
},
"rx-lite": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz",
"integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ="
},
"rx-lite-aggregates": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz",
"integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=",
"requires": {
"rx-lite": "*"
}
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"sass": {
"version": "1.49.9",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.49.9.tgz",
@@ -15296,6 +17125,21 @@
"resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
"integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A=="
},
"slice-ansi": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz",
"integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==",
"requires": {
"is-fullwidth-code-point": "^2.0.0"
},
"dependencies": {
"is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
}
}
},
"sortablejs": {
"version": "1.14.0",
"resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.14.0.tgz",
@@ -15379,6 +17223,11 @@
"resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz",
"integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw=="
},
"sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
},
"stable": {
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz",
@@ -15502,6 +17351,75 @@
}
}
},
"table": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz",
"integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==",
"requires": {
"ajv": "^5.2.3",
"ajv-keywords": "^2.1.0",
"chalk": "^2.1.0",
"lodash": "^4.17.4",
"slice-ansi": "1.0.0",
"string-width": "^2.1.1"
},
"dependencies": {
"ajv": {
"version": "5.5.2",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
"integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
"requires": {
"co": "^4.6.0",
"fast-deep-equal": "^1.0.0",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.3.0"
}
},
"ajv-keywords": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz",
"integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=",
"requires": {}
},
"ansi-regex": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz",
"integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw=="
},
"fast-deep-equal": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
"integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ="
},
"is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
},
"json-schema-traverse": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
"integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A="
},
"string-width": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
"requires": {
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^4.0.0"
}
},
"strip-ansi": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
"requires": {
"ansi-regex": "^3.0.0"
}
}
}
},
"tapable": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.0.tgz",
@@ -15554,6 +17472,11 @@
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
"integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ="
},
"through": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
},
"timsort": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
@@ -15567,6 +17490,14 @@
"@popperjs/core": "^2.9.0"
}
},
"tmp": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
"integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
"requires": {
"os-tmpdir": "~1.0.2"
}
},
"to-fast-properties": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
@@ -15667,6 +17598,22 @@
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.2.tgz",
"integrity": "sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg=="
},
"typescript-eslint-parser": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/typescript-eslint-parser/-/typescript-eslint-parser-8.0.1.tgz",
"integrity": "sha1-6MrFN9mW4Ww9uw18TVCXmeZ6/gw=",
"requires": {
"lodash.unescape": "4.0.1",
"semver": "5.4.1"
},
"dependencies": {
"semver": {
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz",
"integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg=="
}
}
},
"unbox-primitive": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz",
@@ -16162,6 +18109,14 @@
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
},
"write": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz",
"integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=",
"requires": {
"mkdirp": "^0.5.1"
}
},
"ws": {
"version": "7.5.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz",
+4
View File
@@ -69,7 +69,9 @@
"downloadjs": "^1.4.7",
"esbuild-loader": "^2.18.0",
"eslint": "^8.11.0",
"eslint-config-prettier": "^8.5.0",
"eslint-import-resolver-alias": "^1.1.2",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-vue": "^8.5.0",
"file-loader": "^6.2.0",
"jquery": "^3.6.0",
@@ -83,6 +85,8 @@
"postcss-import": "^14.0.2",
"postcss-loader": "^6.2.1",
"pre-commit": "^1.2.2",
"prettier": "^2.6.1",
"prettier-eslint": "^8.2.2",
"query-string": "^7.1.1",
"sass": "^1.49.9",
"sass-loader": "^12.6.0",