Commit 78194dc2 authored by nanahira's avatar nanahira

Merge branch 'main' of github.com:jwyxym/tabulator

parents 4b61802e 02c3553d
<!DOCTYPE html>
<html lang="en">
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<script>
......
......@@ -32,6 +32,7 @@
"vite-plugin-vue-setup-extend": "^0.4.0",
"vue": "^3.4.21",
"vue-i18n": "^9.1.9",
"vue-tournament-bracket": "^3.0.0",
"ygopro-deck-encode": "^1.0.7"
},
"devDependencies": {
......@@ -11205,6 +11206,14 @@
"vue": "^3.2.0"
}
},
"node_modules/vue-tournament-bracket": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/vue-tournament-bracket/-/vue-tournament-bracket-3.0.0.tgz",
"integrity": "sha512-ewugJG94WYzJ+LIyAD1oDIxj5V3RFUGF8+UB9GBJfjhmIzjEGOxKQxNMa2kA+7bsJjydbiAgn4cV6lt+KTGNHQ==",
"dependencies": {
"vue": "^3.2.0"
}
},
"node_modules/w3c-hr-time": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
......
本项目基于[uni-app框架](https://uniapp.dcloud.net.cn/)
前置工作: (所需环境:node.js)
前置工作: (所需环境:[node.js](https://nodejs.org/))
```bash
npm install
```
......
<template>
<uni-card class = 'API'>
<uni-list>
<uni-list-item
v-show = 'api.list.length == 0'
title = '暂无密钥'
>
</uni-list-item>
<view v-for = '(i, v) in api.list'>
<uni-list-item
:clickable = true
>
<template v-slot:header>
<view id = 'header'>
<span>{{ i.name }}</span>
<br>
<span class = 'small'>{{ i.description }}</span>
<br v-if = 'i.expireAt'>
<span class = 'small' v-if = 'i.expireAt'>{{ `过期时间:${new Date(i.expireAt).toLocaleString()}` }}</span>
</view>
</template>
<template v-slot:footer>
<view id = 'footer'>
<view
class = 'button'
@click = 'api.change(v)'
>
<uni-icons :type = "api.changing == v ? 'closeempty' : 'settings'"></uni-icons>
</view>
<view
class = 'button'
@click = 'api.copy(i)'
>
<uni-icons type = 'redo'></uni-icons>
</view>
<view
class = 'button'
style = "border: 1px solid red;"
@click = 'api.del(i)'
>
<uni-icons type = 'trash' color = 'red'></uni-icons>
</view>
</view>
</template>
</uni-list-item>
<uni-forms v-show = 'api.changing == v'>
<uni-datetime-picker type = 'datetime' v-model = 'api.changeInfo.date'/>
<uni-easyinput type = 'text' placeholder = '名称' v-model = 'api.changeInfo.name'/>
<uni-easyinput type = 'text' placeholder = '描述' v-model = 'api.changeInfo.description'/>
<view
class = 'button'
@click = 'api.update(i.id, v)'
>
<uni-icons type = 'upload'></uni-icons>
</view>
</uni-forms>
</view>
<br>
<hr>
<view>
<br>
<h2>添加密钥</h2>
<br>
<uni-forms>
<uni-forms-item>
<uni-datetime-picker type = 'datetime' v-model = 'api.date'/>
<uni-easyinput type = 'text' placeholder = '名称' v-model = 'api.info.name'/>
<uni-easyinput type = 'text' placeholder = '描述' v-model = 'api.info.description'/>
</uni-forms-item>
<view
class = 'button'
@click = 'api.add()'
>
<uni-icons type = 'plusempty'></uni-icons>
</view>
</uni-forms>
</view>
</uni-list>
<br>
<uni-pagination
:current = 'api.page'
v-model = 'api.page'
pageSize = 20
:total = 'api.total'
@change = 'api.search()'
>
</uni-pagination>
</uni-card>
</template>
<script setup lang = 'ts'>
import { ref, reactive, onMounted, onUnmounted, onBeforeMount, watch} from 'vue';
import emitter from '../../script/emitter.ts';
import Const from '../../script/const.ts'
import Mycard from '../../script/mycard.ts';
import ApiKey from '../../script/apikey.ts';
import UniApp from '../../script/uniapp.ts';
import { Tabulator} from '../../script/post.ts';
import { ApiKeyCreateObject, ApiKeyFindObject} from '../../script/type.ts';
let api = reactive({
list : [] as Array<ApiKey>,
page : 1,
total : 0,
search : async () : Promise<void> => {
if (Mycard.id < 0) return;
const res = await Tabulator.ApiKey.FindALL(Mycard.token, {
pageCount : api.page,
userId : Mycard.id
});
api.list = res.api;
api.total = res.total;
},
del : async (i : ApiKey) : Promise<void> => {
if (await Tabulator.ApiKey.Delete(Mycard.token, i.id))
api.search();
},
add : async () : Promise<void> => {
if (await Tabulator.ApiKey.Create(Mycard.token, api.info)) {
api.clear();
api.search();
}
},
clear : () : void => {
api.info = {
name : '',
description : '',
expireAt : undefined as undefined | Date
} as ApiKeyCreateObject;
api.date = '';
},
copy : (i : ApiKey) : void => {
UniApp.copy(i.key);
},
change : (v : number) : void => {
if (api.changing == v) {
api.changing = -1;
api.changeInfo = {
date : '',
name : '',
description : '',
expireAt : undefined as undefined | Date
};
} else {
const i = api.list[v];
api.changeInfo = {
date : i.expireAt,
name : i.name,
description : i.description,
expireAt : new Date(i.expireAt)
};
api.changing = v;
}
},
update : async (id : number, v : number) : Promise<void> => {
if (await Tabulator.ApiKey.Update(Mycard.token, id, {
name : api.changeInfo.name,
description : api.changeInfo.description,
expireAt : api.changeInfo.expireAt
} as ApiKeyCreateObject))
api.search();
},
info : {
name : '',
description : '',
expireAt : undefined as undefined | Date
} as ApiKeyCreateObject,
date : '',
changing : -1,
changeInfo : {
date : '',
name : '',
description : '',
expireAt : undefined as undefined | Date
}
});
watch(() => { return api.date; }, () => {
const toDate = () => {
api.info.expireAt = new Date(api.date);
};
const toUndefined = () => {
api.info.expireAt = undefined;
};
api.date.length > 0 ? toDate() : toUndefined();
});
watch(() => { return api.list; }, () => {
api.changing = -1;
api.changeInfo = {
date : '',
name : '',
description : '',
expireAt : undefined as undefined | Date
};
}, { deep : true });
watch(() => { return api.changeInfo.date; }, () => {
const toDate = () => {
api.changeInfo.expireAt = new Date(api.changeInfo.date);
};
const toUndefined = () => {
api.changeInfo.expireAt = undefined;
};
api.changeInfo.date?.length ?? 0 > 0 ? toDate() : toUndefined();
});
onBeforeMount(() : void => {
api.search();
});
onUnmounted(() : void => {
});
</script>
<style lang = 'scss'>
#footer {
display: flex;
justify-content: center;
justify-items: center;
align-items: center;
column-gap: 10%;
}
.small {
margin-top: 6px;
color: #999;
font-size: 12px;
overflow: hidden;
}
</style>
\ No newline at end of file
......@@ -79,11 +79,11 @@
</template>
<script setup lang = 'ts'>
import { ref, reactive, onMounted, onUnmounted, onBeforeMount, watch} from 'vue';
import { TournamentFindObject, ruleSettings, UserObject } from '../script/type.ts';
import {Tabulator, User} from '../script/post.ts';
import Mycard from '../script/mycard.ts';
import emitter from '../script/emitter.ts'
import Const from '../script/const.ts'
import { TournamentFindObject, ruleSettings, UserObject } from '../../script/type.ts';
import {Tabulator, User} from '../../script/post.ts';
import Mycard from '../../script/mycard.ts';
import emitter from '../../script/emitter.ts'
import Const from '../../script/const.ts'
let create = reactive({
name : '',
......@@ -121,7 +121,7 @@
create.collaborators = [];
},
update : async() : Promise<void> => {
if (create.visibility.select == '')
if (!create.visibility.select)
// @ts-ignore
create.visibility.select = 'SingleElimination';
const collaborators = create.collaborators.map(user => user.id);
......
<template>
<uni-card class = 'Searcher' v-if = 'search'>
<uni-datetime-picker type = 'daterange' v-model = 'search.date'/>
<uni-easyinput
prefixIcon = 'search'
type = 'number'
placeholder = '组织者id'
cancelButton = 'none'
v-model = 'search.creator'
></uni-easyinput>
<view class = 'button' @click = 'search.mine()'>
我组织的比赛
</view>
<uni-easyinput
prefixIcon = 'search'
type = 'text'
placeholder = '比赛名称'
cancelButton = 'none'
v-model = 'search.info.name'
></uni-easyinput>
<uni-easyinput
prefixIcon = 'search'
type = 'number'
placeholder = '比赛id'
cancelButton = 'none'
v-model = 'search.id'
></uni-easyinput>
<uni-data-select
placeholder = '比赛规则'
v-model = 'search.info.rule'
:localdata = 'search.rule.range'
>
</uni-data-select>
<uni-data-select
placeholder = '状态'
v-model = 'search.info.status'
:localdata = 'search.status.range'
>
</uni-data-select>
<uni-data-select
placeholder = '可见性'
v-model = 'search.info.visibility'
:localdata = 'search.visibility.range'
v-show = 'Mycard.id >= 0'
>
</uni-data-select>
<br>
<view class = 'button' @click = 'search.on()'>
<view>
<span>搜索</span>
<uni-icons type = 'search'></uni-icons>
</view>
</view>
</uni-card>
</template>
<script setup lang = 'ts'>
import { ref, reactive, onMounted, onUnmounted, onBeforeMount, watch} from 'vue';
import emitter from '../../script/emitter.ts';
import Const from '../../script/const.ts'
import Mycard from '../../script/mycard.ts';
let search;
const init = (s) : void => {
search = s;
};
onBeforeMount(() : void => {
emitter.on(Const.searcherInit, init)
});
onUnmounted(() : void => {
emitter.off(Const.searcherInit, init)
});
</script>
\ No newline at end of file
<template>
<uni-card class = 'Setting' v-if = 'tournament'>
<uni-easyinput type = 'text' placeholder = '比赛名称' v-model = 'tournament.name' :disabled = 'tournament.operatorChk()'/>
<uni-easyinput type = 'text' placeholder = '比赛描述' v-model = 'tournament.description' :disabled = 'tournament.operatorChk()'/>
<uni-data-select
placeholder = '可见性'
v-model = 'tournament.visibility.select'
:localdata = 'tournament.visibility.range'
:disabled = 'tournament.operatorChk()'
></uni-data-select>
<uni-data-select
placeholder = '规则'
v-model = 'tournament.rule.select'
:localdata = 'tournament.rule.range'
:disabled = "tournament.this?.status != 'Ready' || tournament.operatorChk()"
></uni-data-select>
<view v-show = "tournament.rule.select == 'Swiss'">
<uni-easyinput type = 'number' placeholder = '轮数' v-model = 'tournament.rule.settings.rounds' :disabled = "tournament.this?.status != 'Ready' || tournament.operatorChk()"/>
<uni-easyinput type = 'number' placeholder = '胜利分' v-model = 'tournament.rule.settings.winScore' :disabled = "tournament.this?.status != 'Ready' || tournament.operatorChk()"/>
<uni-easyinput type = 'number' placeholder = '平局分' v-model = 'tournament.rule.settings.drawScore' :disabled = "tournament.this?.status != 'Ready' || tournament.operatorChk()"/>
<uni-easyinput type = 'number' placeholder = '轮空分' v-model = 'tournament.rule.settings.byeScore' :disabled = "tournament.this?.status != 'Ready' || tournament.operatorChk()"/>
</view>
<view v-show = "tournament.rule.select == 'SingleElimination'">
<checkbox-group @change = 'tournament.hasThirdPlaceMatch.select'>
<label>
<checkbox :checked = 'tournament.rule.settings.hasThirdPlaceMatch' :disabled = "tournament.this?.status != 'Ready' || tournament.operatorChk()"/>季军赛
</label>
</checkbox-group>
</view>
<br>
<uni-card
id = 'collaborators'
title = '协作者'
:is-full = 'true'
>
<uni-list>
<uni-list-chat
v-for = '(i, v) in tournament.collaborators'
:avatarCircle = 'true'
:clickable = true
:avatar = 'i.avatar'
:title = 'i.username'
:note = "i.id >= 0 ? i.id.toString() : ''"
@click = 'tournament.remove(v)'
>
<view>
<view class = 'button'>
<uni-icons type = 'trash'></uni-icons>
</view>
</view>
</uni-list-chat>
<uni-list-item>
<template v-slot:header>
<uni-forms>
<uni-forms-item id = 'header'>
<uni-easyinput type = 'text' placeholder = '添加协作者' v-model = 'tournament.collaborator' :disabled = 'tournament.operatorChk()'/>
</uni-forms-item>
</uni-forms>
</template>
<template v-slot:footer>
<view id = 'footer'>
<view
class = 'button'
:style = "{ '--color' : '#409eff' }"
@click = 'tournament.add()'
>
<uni-icons type = 'personadd'></uni-icons>
</view>
</view>
</template>
</uni-list-item>
</uni-list>
</uni-card>
<br>
<view class = 'button' @click = 'tournament.update()'>
<view>
<span>设置</span>
<uni-icons type = 'calendar'></uni-icons>
</view>
</view>
</uni-card>
</template>
<script setup lang = 'ts'>
import { ref, reactive, onMounted, onUnmounted, onBeforeMount, watch} from 'vue';
import emitter from '../../script/emitter.ts';
import Const from '../../script/const.ts'
let tournament;
const init = (t) : void => {
tournament = t;
};
onBeforeMount(() : void => {
emitter.on(Const.settingInit, init)
});
onUnmounted(() : void => {
emitter.off(Const.settingInit, init)
});
</script>
\ No newline at end of file
This diff is collapsed.
<template>
<uni-card
:is-full = 'true'
title = '对阵图'
>
<bracket :flat-tree = 'matches'>
<template #player = ' { player }'>
<view
class = 'player'
:style = "{ '--size' : `${size.width > size.height ? 10 : 20}vw`}"
>
{{ player.name }}
</view>
</template>
</bracket>
</uni-card>
</template>
<script setup lang = 'ts'>
import { defineProps, onBeforeMount, reactive, watch } from 'vue';
import Match from '../script/match';
import Uniapp from '../script/uniapp.ts';
import Participant from '../script/participant';
import Bracket from "vue-tournament-bracket";
const props = defineProps(['matches', 'participants']) as {
matches : Array<Match>,
participants : Array<Participant>
};
const getName = (id : number) : string => {
const p = props.participants.find(i => i.id == id);
return p?.name ?? '';
}
let size = reactive({
width : 0,
height : 0,
get : () => {
// @ts-ignore
size.width = uni.getSystemInfoSync().windowWidth;
size.height = uni.getSystemInfoSync().windowHeight;
}
});
interface player {
id : string;
name : string;
winner ?: boolean;
}
let matches : Array<{
id : number;
player1 : player;
player2 : player;
next ?: number;
}> = reactive([]);
onBeforeMount(() : void => {
Uniapp.chkScreen(size.get);
});
watch(() => { return props.matches; }, () => {
props.matches.forEach(i => {
matches.push({
id : i.id,
next : i.childMatchId,
player1: {
id: i.player1Id ? i.player1Id.toString() : '',
name: i.player1Id ? getName(i.player1Id) : '',
winner: i.winnerId ? i.player1Id == i.winnerId : undefined
},
player2: {
id: i.player2Id ? i.player2Id.toString() : '',
name: i.player2Id ? getName(i.player2Id) : '',
winner: i.winnerId ? i.player2Id == i.winnerId : undefined
}
});
});
matches.forEach(i => {
if (!i.player1.id || !i.player1.id) {
const parents = matches.filter(m => m.next == i.id).sort((a, b) => a.id - b.id);
if (parents) {
if (!i.player1.id && parents.length > 0) {
i.player1.id = parents[0].player1.winner ? parents[0].player1.id : parents[0].player2.winner ? parents[0].player2.id : '';
i.player1.name = parents[0].player1.winner ? parents[0].player1.name : parents[0].player2.winner ? parents[0].player2.name : '';
}
if (!i.player2.id && parents.length > 1) {
i.player2.id = parents[1].player1.winner ? parents[1].player1.id : parents[1].player2.winner ? parents[1].player2.id : '';
i.player2.name = parents[1].player1.winner ? parents[1].player1.name : parents[1].player2.winner ? parents[1].player2.name : '';
}
}
}
});
}, { immediate : true, deep : true });
</script>
<style scoped lang = 'scss'>
.uni-card {
min-width: 100%;
overflow-x: auto;
:deep(.player) {
width: var(--size);
white-space: nowrap;
text-overflow: ellipsis;
}
}
</style>
\ No newline at end of file
<template>
<view class = 'Pics'>
<transition name = 'move_right'>
<view v-show = 'deck.participant'>
<uni-card :title = "deck.main.length > 0 ? '主卡组' : '暂无主卡组'">
<image class = 'deck_cards' v-for = '(i, v) in deck.main' :src = '`https://cdn.233.momobako.com/ygopro/pics/${i}.jpg!half`' mode = 'aspectFit' @error = 'changeImg.main(v)'></image>
<uni-card
v-show = 'deck.participant'
:title = "`${deck.participant?.name ?? '...'} 的卡组`"
id = 'main'
spacing = '0px'
padding = '0px'
>
<!-- <view
class = 'button'
v-show = 'deck.main.length > 0 || deck.side.length > 0'
@click = 'deck.download()'
>
<uni-icons type = 'download'></uni-icons>
</view> -->
<uni-card class = 'deck' :title = "deck.main.length > 0 ? '主卡组' : '暂无主卡组'">
<cover-image class = 'deck_cards' v-for = '(i, v) in deck.main' :src = 'getImg(i)' @error = 'changeImg.main(v)'></cover-image>
</uni-card>
<uni-card class = 'deck' :title = "deck.side.length > 0 ? '副卡组' : '暂无副卡组'">
<cover-image class = 'deck_cards' v-for = '(i, v) in deck.side' :src = 'getImg(i)' @error = 'changeImg.main(v)'></cover-image>
</uni-card>
<uni-card :title = "deck.side.length > 0 ? '副卡组' : '暂无副卡组'">
<image class = 'deck_cards' v-for = '(i, v) in deck.side' :src = '`https://cdn.233.momobako.com/ygopro/pics/${i}.jpg!half`' mode = 'aspectFit' @error = 'changeImg.main(v)'></image>
</uni-card>
</view>
</transition>
</view>
</template>
......@@ -16,7 +29,12 @@
import { ref, reactive, onMounted, onUnmounted, onBeforeMount, watch} from 'vue';
import emitter from '../script/emitter.ts'
import Const from '../script/const.ts'
import Participant from '../script/participant.ts';
import Participant from '../script/participant.ts';
import Download from '../script/download.js';
const getImg = (i : number) => {
return i == 0 || Math.floor(Math.log10(Math.abs(i))) < 8 ? `https://cdn.233.momobako.com/ygopro/pics/${i}.jpg!half` : `https://cdn02.moecube.com:444/ygopro-super-pre/data/pics/${i}.jpg`
};
const changeImg = {
main : (v : number) : void => {
......@@ -32,19 +50,26 @@ import Participant from '../script/participant.ts';
chk : false,
main : [] as Array<number>,
side : [] as Array<number>,
init : (i : {
blob : undefined as Blob | undefined,
init : async (i : {
participant : Participant,
main : Array<number>,
side : Array<number>
}) : void => {
side : Array<number>,
blob : Blob,
}) : Promise<void> => {
if (deck.chk) return;
const participant = i.participant ?? undefined;
if (participant && deck.participant == participant) {
deck.off();
return
return;
}
if (deck.participant) {
deck.participant = undefined;
await (new Promise(resolve => setTimeout(resolve, 500)));
}
deck.main = i.main;
deck.side = i.side;
deck.blob = i.blob;
deck.participant = participant;
},
off : async () : Promise<void> => {
......@@ -54,15 +79,19 @@ import Participant from '../script/participant.ts';
deck.main = [];
deck.side = [];
deck.chk = false;
deck.blob = undefined;
},
clickClear : (e) : void => {
let element = e.target;
while (element) {
if (['body'].includes(element.id) || element.classList.contains('Pics'))
if (['deckbutton'].includes(element.id) || element.classList.contains('Pics'))
return undefined;
element = element.parentElement;
}
deck.off();
},
download : () : void => {
Download.start(deck.blob, `${deck.participant?.name}`);
}
});
......@@ -80,4 +109,14 @@ import Participant from '../script/participant.ts';
</script>
<style scoped lang = 'scss'>
@import '../style/transition.scss';
.button {
border: 1px solid #409eff;
display: flex;
width: 20%;
justify-content: center;
&:hover {
cursor: pointer;
background-color: #e6e6e6;
}
}
</style>
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
......@@ -5,6 +5,66 @@ class ConstData {
tournamentReload = 'tournamentReload';
createOff = 'createOff';
picsOpen = 'picsOpen';
searcherInit = 'searcherInit';
settingInit = 'settingInit';
changeUrl = 'changeUrl';
show = 'show';
showTournament = 'showTournament';
pic = {
hajimi : `
<pre> :==+++=-.
:++=++*+#@#=.
.##+=#%@%%##@=
.%#**#%#+*=+%#
.+@@#**===-*@-
.-#%#*=**%#-
.:-====#* .:-:
.#+ .:==+==%.
:+=-::.. .#-..... .-++=-.. #:
-#:::-====--::--==#=======++-: #-
:# ..:-==::..:. .... *-
.*: .::.. +=
=+ :-:::. .-=+=-=- ++
.#: .-==*+-=: :=+:-%@#@*. .++.
:#: .+**=@@@%%: :=#%+*@@@@@#. -*:
:#: .+#%*@@@@@@#. .=+%:%@@@@@@@#. :*:
.#- .+#@-#@@@@@@@*---==@=#@@@@@@@@@+ -*.
+* =+%=+@%%%%#+-.....=*#%##%##**+=#- *=
.%: .*--:=:::.. ......:. :*. :#.
-# -*----=. ... -- .*: .%:
=* -=:----+. .-=+*#**+=-. :+-.: .+: .%:
. -# -*:--+..***%%%#%%%%%#*=+@@:.= .:+: :%. ..::.
.-==+==-. .#. .#.--=- *@+=%@+====+*@*:#@-:- ::*. +#. .-+====++=.
-*=:...:=*- =+ +=::-+-:+**#= .. .+***--+=..:+- :%: :*+: .=#:
.#- :*= += .==:-*#*=----+***+=-:::=**+*-:=- .#= -*-. *=
.*= .*+ .+=. :==---=%++###*##**++++#::-++-.. -#= .-#: =*.
.#- .*=:.. -+:..:-++=-==#%@@%@@%%+----=++*****##%*--====*-.... .+*:::-::.
:----==%+: .:-------==-:.=#**+=++**#+%@#=--+@@#@@@@@@@@@@%%%#*+--:..:=**++=-: .-*=----=+*=.
.*+-:::::-=-.+#*%##+==. .:-+*%@@@@@@@@@*=%@*:=#@*#@@@@@@@@@@#+-:. =#+#####*+= . .**.
++ +%#%@#@##++. :=#@@@@@@@@@#**+*+++*@@@@@@@@@*-. :%##%#%%%%%#: .#-
+- .%##%%%%%@**+. .-*#%%%%%####+---======+++*- -*%#*%#####%##- .#:
:*: .***#=**#%####=. .-=-:.. +@%:. :--*@%#%*+**++. .. .:*=
:+=-----: .++++++**#=... :+**#%*. -*##**++=. .-++--==:
.:=%+:. .:===+=-. :. :=@@- .... .:--.. :**..
:#: --::...........::-+- =%#*=-. :#=====++++++===-==+. .#+
:%. :+=:--====---------::*- +@+:... .+*-::::::::-#*+=: -++:. .-#-
-*=:..:-=+: .+=. .+***%#. :+#=:. =%%##+. :=++===++:
.-====-: .-++:.. .=@%. ..:-==:.:=+=: .*@%%%@: ..::..
:*=-----*#+-+=---:. .-*+. .+***%@@:
=*: ......#:. ..:::-+#::**+=+@%=
.+=. == :=*#****+*%***#**=.
++::-::.. +#: :*##%#*=+++++#- ..
=#***#%##++-: .*:*::#%#####++=+++=#.
:%+=++%#%##%#%*:=- :+#@%*%#####++===+=
=#=+=+#*%#%##%=%+ .=#*#%%@#@%##+*-++
=*=+=+**##@%%=*+. :=+*%%#@%@%%*+@-
:#*=+++#*#@%#+: .-++%#+@%@#%+.
-@%#*#@#*%#-. .:-=*#*+:
.+##%#+-.
..
</pre>
`
}
}
const Const = new ConstData();
......
class DownloadFile {
start = async (blob, fileName) => {
// #ifdef H5
if (window.showSaveFilePicker) {
let opts = {
suggestedName: `${fileName}.ydk`,
types: [{
description: '',
accept: {
'application/octet-stream': ['.ydk']
}
}],
excludeAcceptAllOption: true
};
let handle = await window.showSaveFilePicker(opts);
let writable = await handle.createWritable();
await writable.write(blob);
await writable.close();
} else {
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = `${fileName}.ydk`;
link.click();
setTimeout(() => {
document.body.removeChild(link);
URL.revokeObjectURL(url);
}, 10000);
}
// #endif
// #ifdef APP-PLUS
// #endif
};
}
const Download = new DownloadFile();
export default Download;
\ No newline at end of file
import { Buffer } from 'buffer';
import { ref } from 'vue';
import emitter from './emitter.ts'
import Const from './const.ts'
interface MyCardSSOUser {
token : string;
......@@ -49,9 +52,16 @@ class MyCard {
logout() {
window.localStorage.removeItem('mycardLogin');
window.location.replace(window.location.href.replace(/\/\?.*/, ''))
this.user = {} as MyCardSSOUser;
this.id = -1;
this.token = '';
this.username = '';
this.email = '';
this.avatar = 'https://cdn02.moecube.com:444/accounts/default_avatar.jpg';
const url = window.location.href.split('/?');
emitter.emit(Const.changeUrl, url[0].endsWith('/') ? url[0] : `${url[0]}/`);
}
}
const Mycard = new MyCard();
const Mycard = ref(new MyCard()).value;
export default Mycard;
\ No newline at end of file
......@@ -12,7 +12,7 @@ class Participant {
deck : YGOProDeck | undefined;
constructor(obj: ParticipantObject) {
this.name = obj.name;
this.name = new TextDecoder('utf-8').decode(new Uint8Array([...obj.name].map(c => c.charCodeAt(0))));
this.tournamentId = obj.tournamentId;
this.id = obj.id;
this.score = obj.score;
......@@ -35,6 +35,11 @@ class Participant {
side : this.deck?.side.slice() ?? [],
};
}
Blob = () : Blob => {
const data = this.deck?.toYdkString() ?? '';
return new Blob([data], { type: 'text/plain' });
}
}
export default Participant;
\ No newline at end of file
......@@ -20,8 +20,9 @@ import {
AllTournament,
AllParticipant,
AllMatch,
AllAPI,
UserObject,
TournamentAParticipant
TournamentGet
} from './type.ts'
import UniApp from './uniapp.ts';
......@@ -78,7 +79,7 @@ class TabulatorAPI {
return false;
}
},
Find : async (token : string, id : number) : Promise<TournamentAParticipant> => {
Find : async (token : string, id : number) : Promise<TournamentGet> => {
let response : {
data : {
data : TournamentObject;
......@@ -91,14 +92,22 @@ class TabulatorAPI {
}
});
let participants : Array<Participant> = [];
let matches : Array<Match> = [];
response.data.data.participants.forEach((i : ParticipantObject) => {
participants.push(new Participant(i));
});
response.data.data.matches.forEach((i : MatchObject) => {
matches.push(new Match(i));
});
return {
tournament : new Tournament(response.data.data),
participant : {
participants : participants,
total : participants.length
},
match : {
matches : matches,
total : matches.length
}
};
}
......@@ -109,6 +118,10 @@ class TabulatorAPI {
participant : {
participants : [],
total : 0
},
match : {
matches : [],
total : 0
}
};
}
......@@ -256,6 +269,47 @@ class TabulatorAPI {
console.error(error);
return false;
}
},
Shuffle : async (token : string, id : number) : Promise<Boolean> => {
let response : {
data : {
success : boolean;
}
};
try {
response = await this.url.post(`/api/tournament/${id}/shuffle-participants`, {}, {
headers: {
'x-user-token' : token
}
});
return response.data.success;
}
catch(error) {
console.error(error);
return false;
}
},
Drag : async (token : string, id : number, from : number, to : number) : Promise<Boolean> => {
let response : {
data : {
success : boolean;
}
};
try {
response = await this.url.post(`/api/tournament/${id}/drag-participant`, {
draggingParticipantId : from,
placeAfterParticipantId : to
}, {
headers: {
'x-user-token' : token
}
});
return response.data.success;
}
catch(error) {
console.error(error);
return false;
}
}
}
Participant = {
......@@ -454,14 +508,14 @@ class TabulatorAPI {
})
return {
total : response.data.total,
matchs : matchs
matches : matchs
};
}
catch(error) {
console.error(error);
return {
total : 0,
matchs : []
matches : []
};
}
},
......@@ -485,7 +539,7 @@ class TabulatorAPI {
}
}
}
ApiKeyObject = {
ApiKey = {
Create : async (token : string, Data : ApiKeyCreateObject) : Promise<boolean> => {
let response : {
data : {
......@@ -493,7 +547,11 @@ class TabulatorAPI {
}
};
try {
response = await this.url.post(`/api/api-key`, Data, {
response = await this.url.post(`/api/api-key`, {
name : Data.name,
description : Data.description?.length ?? 0 > 0 ? Data.description : undefined,
expireAt : Data.expireAt?.toISOString() ?? undefined
}, {
headers : {
'x-user-token' : token
}
......@@ -501,6 +559,11 @@ class TabulatorAPI {
return response.data.success;
}
catch(error) {
uni.showModal({
title : '创建失败',
content : error.message,
showCancel : false
});
console.error(error);
return false;
}
......@@ -524,14 +587,17 @@ class TabulatorAPI {
return undefined;
}
},
FindALL : async (token : string, obj : ApiKeyFindObject = {}) : Promise<Array<ApiKey>> => {
FindALL : async (token : string, obj : ApiKeyFindObject = {}) : Promise<AllAPI> => {
let response : {
data : {
data : Array<ApiKeyObject>;
total : number;
}
};
try {
response = await this.url.get(`/api/match`, {
if (!obj.userId || obj.userId < 0)
throw new Error('未登录');
response = await this.url.get(`/api/api-key`, {
params : {
recordsPerPage : 20,
pageCount : obj.pageCount ?? 1,
......@@ -547,11 +613,17 @@ class TabulatorAPI {
response.data.data.forEach((i : ApiKeyObject) => {
keys.push(new ApiKey(i));
})
return keys;
return {
total : response.data.total,
api : keys
};
}
catch(error) {
console.error(error);
return [];
return {
total : 0,
api : []
};
}
},
Update : async (token : string, id : number, Data : ApiKeyCreateObject) : Promise<boolean> => {
......@@ -561,7 +633,12 @@ class TabulatorAPI {
}
};
try {
response = await this.url.patch(`/api/api-key/${id}`, Data, {
console.log(Data.expireAt?.toISOString())
response = await this.url.patch(`/api/api-key/${id}`, {
name : Data.name,
description : Data.description?.length ?? 0 > 0 ? Data.description : '',
expireAt : Data.expireAt?.toISOString() ?? new Date(2038, 0, 1).toISOString()
}, {
headers : {
'x-user-token' : token
}
......
......@@ -64,6 +64,7 @@ interface TournamentObject extends TournamentCreateObject {
creator : number;
createdAt : string;
participants : Array<ParticipantObject>;
matches : Array<MatchObject>
}
interface TournamentFindObject {
......@@ -121,8 +122,8 @@ interface ApiKeyFindObject {
interface ApiKeyCreateObject {
name : string;
description : string;
expireAt : string;
description ?: string;
expireAt ?: Date;
}
interface ApiKeyObject extends ApiKeyCreateObject {
......@@ -144,12 +145,17 @@ interface AllParticipant extends All {
}
interface AllMatch extends All {
matchs : Array<Match>;
matches : Array<Match>;
}
interface AllAPI extends All {
api : Array<ApiKey>;
}
interface TournamentAParticipant {
interface TournamentGet {
tournament : Tournament | undefined;
participant : AllParticipant
participant : AllParticipant,
match : AllMatch
}
interface UserObject {
......@@ -182,8 +188,9 @@ export {
AllTournament,
AllParticipant,
AllMatch,
AllAPI,
ruleSettings,
UserObject,
TournamentAParticipant,
TournamentGet,
Deck
}
\ No newline at end of file
......@@ -69,6 +69,22 @@ class UniappFuncs {
uni.offDeviceOrientationChange(chk)
// #endif
}
copy (string) : void {
uni.setClipboardData({
data: string,
success: () => {
uni.showToast({
title: '复制成功',
});
},
fail: () => {
uni.showToast({
title: '复制失败',
});
}
});
}
}
const UniApp = new UniappFuncs()
......
#page {
#head {
box-shadow: 2px 2px 10px rgba(0, 0, 0, 0.2);
.head {
display: grid;
grid-template-columns: repeat(5, 1fr);
grid-template-rows: 1fr;
align-items: center;
position: relative;
z-index: 0;
:deep(.uni-list) {
:hover {
cursor: pointer;
}
}
.button {
width: 30%;
}
#really {
box-shadow: 2px 2px 10px rgba(0, 0, 0, 0.2);
// background-color: white;
position: fixed;
width: 100%;
z-index: 1;
}
#body {
width: var(--size);
......@@ -27,11 +30,11 @@
}
}
:deep(#user) {
position: absolute;
position: fixed;
right: 2%;
z-index: 1;
z-index: 2;
max-width: var(--size);
width: 20%;
min-width: var(--minsize);
.user {
&:hover {
cursor: pointer;
......@@ -40,8 +43,8 @@
}
}
:deep(#drawer) {
position: absolute;
z-index: 1;
position: fixed;
z-index: 2;
width: var(--size);
height: 80%;
overflow-y: auto;
......@@ -68,34 +71,22 @@
}
}
button {
background-color: white;
color : #606266;
border-radius: 4px;
white-space: nowrap;
border: 0.02px solid #606266;
transition: all 0.3s ease;
&:hover {
color: #ecf5ff;
background-color: #ecf5ff;
border: 0.02px solid #409eff;
}
}
.Pics {
position: absolute;
z-index: 1;
position: fixed;
z-index: 2;
right: 2%;
width: 50%;
:deep(.uni-card) {
// display: flex;
max-width: var(--maxsize);
min-width: var(--minsize);
:deep(#main) {
overflow-y: auto;
max-height: 90vh;
.deck {
.deck_cards {
width: 10%;
height: 11.52vh;
aspect-ratio: 1 / 1.43;
width: var(--height);
display: inline-block;
vertical-align: top;
}
}
}
}
}
.uni-list-item, .uni-list-chat, .uni-list {
background: none !important;
}
#really{
background: linear-gradient(to right, rgb(255, 228, 230), white, white, white, rgb(204, 251, 241));
}
#main_page {
background: linear-gradient(to right, rgb(255, 228, 230), white, white, white, rgb(204, 251, 241));
height: 100%;
min-height: 100vh;
width: 100%;
.background {
// background: linear-gradient(to right, rgb(134, 239, 172), rgb(59, 130, 246), rgb(147, 51, 234));
background: linear-gradient(to right, rgb(255, 228, 230), white, white, white, rgb(204, 251, 241));
width: 100%;
height: 100vh;
font-family: 'Courier New', monospace;
.pic {
color: rgb(0, 70, 28);
font-size: 12px;
}
}
}
#page {
button {
background-color: white;
border-radius: 4px;
white-space: nowrap;
border: 0.02px solid white;
transition: all 0.3s ease;
&:hover {
background-color: rgb(254, 245, 245);
border: 0.02px solid rgb(255, 228, 230);
}
}
}
......@@ -43,13 +43,15 @@
:deep(.uni-list-item) {
.small {
margin-top: 6rpx;
margin-top: 6px;
color: #999;
font-size: 12px;
overflow: hidden;
}
#header {
min-width: 30%;
max-width: 30%;
text-overflow: ellipsis;
}
#body {
&:hover {
......@@ -63,7 +65,7 @@
justify-items: center;
align-items: center;
column-gap: 10%;
width: 10%;
width: 20%;
.button {
border: 0.5px solid var(--color);
width: auto;
......@@ -79,7 +81,7 @@
.match {
border: 1px solid gray;
z-index: 1;
z-index: 0;
// left : 100%;
width: 100%;
background-color: white;
......
......@@ -49,6 +49,12 @@
}
}
.move_right_slow, .move_left_slow {
&-leave-active {
transition: transform 1s ease;
}
}
.move_up {
&-enter-active,
&-leave-active {
......@@ -65,3 +71,23 @@
transform: translateY(0%);
}
}
.page {
&-enter-active,
&-leave-active {
transition: transform 0.5s ease;
}
&-enter-from {
transform: translateX(-200%);
}
&-enter-to,
&-leave-from {
transform: translateX(0%);
}
&-leave-to {
transform: translateX(200%);
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment