Commit 78194dc2 authored by nanahira's avatar nanahira

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

parents 4b61802e 02c3553d
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="zh-CN">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<script> <script>
......
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
"vite-plugin-vue-setup-extend": "^0.4.0", "vite-plugin-vue-setup-extend": "^0.4.0",
"vue": "^3.4.21", "vue": "^3.4.21",
"vue-i18n": "^9.1.9", "vue-i18n": "^9.1.9",
"vue-tournament-bracket": "^3.0.0",
"ygopro-deck-encode": "^1.0.7" "ygopro-deck-encode": "^1.0.7"
}, },
"devDependencies": { "devDependencies": {
...@@ -11205,6 +11206,14 @@ ...@@ -11205,6 +11206,14 @@
"vue": "^3.2.0" "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": { "node_modules/w3c-hr-time": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
......
...@@ -59,6 +59,7 @@ ...@@ -59,6 +59,7 @@
"vite-plugin-vue-setup-extend": "^0.4.0", "vite-plugin-vue-setup-extend": "^0.4.0",
"vue": "^3.4.21", "vue": "^3.4.21",
"vue-i18n": "^9.1.9", "vue-i18n": "^9.1.9",
"vue-tournament-bracket": "^3.0.0",
"ygopro-deck-encode": "^1.0.7" "ygopro-deck-encode": "^1.0.7"
}, },
"devDependencies": { "devDependencies": {
......
本项目基于[uni-app框架](https://uniapp.dcloud.net.cn/) 本项目基于[uni-app框架](https://uniapp.dcloud.net.cn/)
前置工作: (所需环境:node.js) 前置工作: (所需环境:[node.js](https://nodejs.org/))
```bash ```bash
npm install 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 @@ ...@@ -79,11 +79,11 @@
</template> </template>
<script setup lang = 'ts'> <script setup lang = 'ts'>
import { ref, reactive, onMounted, onUnmounted, onBeforeMount, watch} from 'vue'; import { ref, reactive, onMounted, onUnmounted, onBeforeMount, watch} from 'vue';
import { TournamentFindObject, ruleSettings, UserObject } from '../script/type.ts'; import { TournamentFindObject, ruleSettings, UserObject } from '../../script/type.ts';
import {Tabulator, User} from '../script/post.ts'; import {Tabulator, User} from '../../script/post.ts';
import Mycard from '../script/mycard.ts'; import Mycard from '../../script/mycard.ts';
import emitter from '../script/emitter.ts' import emitter from '../../script/emitter.ts'
import Const from '../script/const.ts' import Const from '../../script/const.ts'
let create = reactive({ let create = reactive({
name : '', name : '',
...@@ -121,7 +121,7 @@ ...@@ -121,7 +121,7 @@
create.collaborators = []; create.collaborators = [];
}, },
update : async() : Promise<void> => { update : async() : Promise<void> => {
if (create.visibility.select == '') if (!create.visibility.select)
// @ts-ignore // @ts-ignore
create.visibility.select = 'SingleElimination'; create.visibility.select = 'SingleElimination';
const collaborators = create.collaborators.map(user => user.id); 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> <template>
<view class = 'Pics'> <view class = 'Pics'>
<transition name = 'move_right'> <transition name = 'move_right'>
<view v-show = 'deck.participant'> <uni-card
<uni-card :title = "deck.main.length > 0 ? '主卡组' : '暂无主卡组'"> v-show = 'deck.participant'
<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> :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>
<uni-card :title = "deck.side.length > 0 ? '副卡组' : '暂无副卡组'"> <uni-card class = 'deck' :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> <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>
</view> </uni-card>
</transition> </transition>
</view> </view>
</template> </template>
...@@ -16,7 +29,12 @@ ...@@ -16,7 +29,12 @@
import { ref, reactive, onMounted, onUnmounted, onBeforeMount, watch} from 'vue'; import { ref, reactive, onMounted, onUnmounted, onBeforeMount, watch} from 'vue';
import emitter from '../script/emitter.ts' import emitter from '../script/emitter.ts'
import Const from '../script/const.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 = { const changeImg = {
main : (v : number) : void => { main : (v : number) : void => {
...@@ -32,19 +50,26 @@ import Participant from '../script/participant.ts'; ...@@ -32,19 +50,26 @@ import Participant from '../script/participant.ts';
chk : false, chk : false,
main : [] as Array<number>, main : [] as Array<number>,
side : [] as Array<number>, side : [] as Array<number>,
init : (i : { blob : undefined as Blob | undefined,
init : async (i : {
participant : Participant, participant : Participant,
main : Array<number>, main : Array<number>,
side : Array<number> side : Array<number>,
}) : void => { blob : Blob,
}) : Promise<void> => {
if (deck.chk) return; if (deck.chk) return;
const participant = i.participant ?? undefined; const participant = i.participant ?? undefined;
if (participant && deck.participant == participant) { if (participant && deck.participant == participant) {
deck.off(); deck.off();
return return;
}
if (deck.participant) {
deck.participant = undefined;
await (new Promise(resolve => setTimeout(resolve, 500)));
} }
deck.main = i.main; deck.main = i.main;
deck.side = i.side; deck.side = i.side;
deck.blob = i.blob;
deck.participant = participant; deck.participant = participant;
}, },
off : async () : Promise<void> => { off : async () : Promise<void> => {
...@@ -54,15 +79,19 @@ import Participant from '../script/participant.ts'; ...@@ -54,15 +79,19 @@ import Participant from '../script/participant.ts';
deck.main = []; deck.main = [];
deck.side = []; deck.side = [];
deck.chk = false; deck.chk = false;
deck.blob = undefined;
}, },
clickClear : (e) : void => { clickClear : (e) : void => {
let element = e.target; let element = e.target;
while (element) { while (element) {
if (['body'].includes(element.id) || element.classList.contains('Pics')) if (['deckbutton'].includes(element.id) || element.classList.contains('Pics'))
return undefined; return undefined;
element = element.parentElement; element = element.parentElement;
} }
deck.off(); deck.off();
},
download : () : void => {
Download.start(deck.blob, `${deck.participant?.name}`);
} }
}); });
...@@ -80,4 +109,14 @@ import Participant from '../script/participant.ts'; ...@@ -80,4 +109,14 @@ import Participant from '../script/participant.ts';
</script> </script>
<style scoped lang = 'scss'> <style scoped lang = 'scss'>
@import '../style/transition.scss'; @import '../style/transition.scss';
.button {
border: 1px solid #409eff;
display: flex;
width: 20%;
justify-content: center;
&:hover {
cursor: pointer;
background-color: #e6e6e6;
}
}
</style> </style>
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
...@@ -5,6 +5,66 @@ class ConstData { ...@@ -5,6 +5,66 @@ class ConstData {
tournamentReload = 'tournamentReload'; tournamentReload = 'tournamentReload';
createOff = 'createOff'; createOff = 'createOff';
picsOpen = 'picsOpen'; picsOpen = 'picsOpen';
searcherInit = 'searcherInit';
settingInit = 'settingInit';
changeUrl = 'changeUrl';
show = 'show';
showTournament = 'showTournament';
pic = {
hajimi : `
<pre> :==+++=-.
:++=++*+#@#=.
.##+=#%@%%##@=
.%#**#%#+*=+%#
.+@@#**===-*@-
.-#%#*=**%#-
.:-====#* .:-:
.#+ .:==+==%.
:+=-::.. .#-..... .-++=-.. #:
-#:::-====--::--==#=======++-: #-
:# ..:-==::..:. .... *-
.*: .::.. +=
=+ :-:::. .-=+=-=- ++
.#: .-==*+-=: :=+:-%@#@*. .++.
:#: .+**=@@@%%: :=#%+*@@@@@#. -*:
:#: .+#%*@@@@@@#. .=+%:%@@@@@@@#. :*:
.#- .+#@-#@@@@@@@*---==@=#@@@@@@@@@+ -*.
+* =+%=+@%%%%#+-.....=*#%##%##**+=#- *=
.%: .*--:=:::.. ......:. :*. :#.
-# -*----=. ... -- .*: .%:
=* -=:----+. .-=+*#**+=-. :+-.: .+: .%:
. -# -*:--+..***%%%#%%%%%#*=+@@:.= .:+: :%. ..::.
.-==+==-. .#. .#.--=- *@+=%@+====+*@*:#@-:- ::*. +#. .-+====++=.
-*=:...:=*- =+ +=::-+-:+**#= .. .+***--+=..:+- :%: :*+: .=#:
.#- :*= += .==:-*#*=----+***+=-:::=**+*-:=- .#= -*-. *=
.*= .*+ .+=. :==---=%++###*##**++++#::-++-.. -#= .-#: =*.
.#- .*=:.. -+:..:-++=-==#%@@%@@%%+----=++*****##%*--====*-.... .+*:::-::.
:----==%+: .:-------==-:.=#**+=++**#+%@#=--+@@#@@@@@@@@@@%%%#*+--:..:=**++=-: .-*=----=+*=.
.*+-:::::-=-.+#*%##+==. .:-+*%@@@@@@@@@*=%@*:=#@*#@@@@@@@@@@#+-:. =#+#####*+= . .**.
++ +%#%@#@##++. :=#@@@@@@@@@#**+*+++*@@@@@@@@@*-. :%##%#%%%%%#: .#-
+- .%##%%%%%@**+. .-*#%%%%%####+---======+++*- -*%#*%#####%##- .#:
:*: .***#=**#%####=. .-=-:.. +@%:. :--*@%#%*+**++. .. .:*=
:+=-----: .++++++**#=... :+**#%*. -*##**++=. .-++--==:
.:=%+:. .:===+=-. :. :=@@- .... .:--.. :**..
:#: --::...........::-+- =%#*=-. :#=====++++++===-==+. .#+
:%. :+=:--====---------::*- +@+:... .+*-::::::::-#*+=: -++:. .-#-
-*=:..:-=+: .+=. .+***%#. :+#=:. =%%##+. :=++===++:
.-====-: .-++:.. .=@%. ..:-==:.:=+=: .*@%%%@: ..::..
:*=-----*#+-+=---:. .-*+. .+***%@@:
=*: ......#:. ..:::-+#::**+=+@%=
.+=. == :=*#****+*%***#**=.
++::-::.. +#: :*##%#*=+++++#- ..
=#***#%##++-: .*:*::#%#####++=+++=#.
:%+=++%#%##%#%*:=- :+#@%*%#####++===+=
=#=+=+#*%#%##%=%+ .=#*#%%@#@%##+*-++
=*=+=+**##@%%=*+. :=+*%%#@%@%%*+@-
:#*=+++#*#@%#+: .-++%#+@%@#%+.
-@%#*#@#*%#-. .:-=*#*+:
.+##%#+-.
..
</pre>
`
}
} }
const Const = new ConstData(); 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 { Buffer } from 'buffer';
import { ref } from 'vue';
import emitter from './emitter.ts'
import Const from './const.ts'
interface MyCardSSOUser { interface MyCardSSOUser {
token : string; token : string;
...@@ -49,9 +52,16 @@ class MyCard { ...@@ -49,9 +52,16 @@ class MyCard {
logout() { logout() {
window.localStorage.removeItem('mycardLogin'); 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; export default Mycard;
\ No newline at end of file
...@@ -12,7 +12,7 @@ class Participant { ...@@ -12,7 +12,7 @@ class Participant {
deck : YGOProDeck | undefined; deck : YGOProDeck | undefined;
constructor(obj: ParticipantObject) { 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.tournamentId = obj.tournamentId;
this.id = obj.id; this.id = obj.id;
this.score = obj.score; this.score = obj.score;
...@@ -35,6 +35,11 @@ class Participant { ...@@ -35,6 +35,11 @@ class Participant {
side : this.deck?.side.slice() ?? [], side : this.deck?.side.slice() ?? [],
}; };
} }
Blob = () : Blob => {
const data = this.deck?.toYdkString() ?? '';
return new Blob([data], { type: 'text/plain' });
}
} }
export default Participant; export default Participant;
\ No newline at end of file
...@@ -20,8 +20,9 @@ import { ...@@ -20,8 +20,9 @@ import {
AllTournament, AllTournament,
AllParticipant, AllParticipant,
AllMatch, AllMatch,
AllAPI,
UserObject, UserObject,
TournamentAParticipant TournamentGet
} from './type.ts' } from './type.ts'
import UniApp from './uniapp.ts'; import UniApp from './uniapp.ts';
...@@ -78,7 +79,7 @@ class TabulatorAPI { ...@@ -78,7 +79,7 @@ class TabulatorAPI {
return false; return false;
} }
}, },
Find : async (token : string, id : number) : Promise<TournamentAParticipant> => { Find : async (token : string, id : number) : Promise<TournamentGet> => {
let response : { let response : {
data : { data : {
data : TournamentObject; data : TournamentObject;
...@@ -91,14 +92,22 @@ class TabulatorAPI { ...@@ -91,14 +92,22 @@ class TabulatorAPI {
} }
}); });
let participants : Array<Participant> = []; let participants : Array<Participant> = [];
let matches : Array<Match> = [];
response.data.data.participants.forEach((i : ParticipantObject) => { response.data.data.participants.forEach((i : ParticipantObject) => {
participants.push(new Participant(i)); participants.push(new Participant(i));
}); });
response.data.data.matches.forEach((i : MatchObject) => {
matches.push(new Match(i));
});
return { return {
tournament : new Tournament(response.data.data), tournament : new Tournament(response.data.data),
participant : { participant : {
participants : participants, participants : participants,
total : participants.length total : participants.length
},
match : {
matches : matches,
total : matches.length
} }
}; };
} }
...@@ -109,6 +118,10 @@ class TabulatorAPI { ...@@ -109,6 +118,10 @@ class TabulatorAPI {
participant : { participant : {
participants : [], participants : [],
total : 0 total : 0
},
match : {
matches : [],
total : 0
} }
}; };
} }
...@@ -256,6 +269,47 @@ class TabulatorAPI { ...@@ -256,6 +269,47 @@ class TabulatorAPI {
console.error(error); console.error(error);
return false; 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 = { Participant = {
...@@ -454,14 +508,14 @@ class TabulatorAPI { ...@@ -454,14 +508,14 @@ class TabulatorAPI {
}) })
return { return {
total : response.data.total, total : response.data.total,
matchs : matchs matches : matchs
}; };
} }
catch(error) { catch(error) {
console.error(error); console.error(error);
return { return {
total : 0, total : 0,
matchs : [] matches : []
}; };
} }
}, },
...@@ -485,7 +539,7 @@ class TabulatorAPI { ...@@ -485,7 +539,7 @@ class TabulatorAPI {
} }
} }
} }
ApiKeyObject = { ApiKey = {
Create : async (token : string, Data : ApiKeyCreateObject) : Promise<boolean> => { Create : async (token : string, Data : ApiKeyCreateObject) : Promise<boolean> => {
let response : { let response : {
data : { data : {
...@@ -493,7 +547,11 @@ class TabulatorAPI { ...@@ -493,7 +547,11 @@ class TabulatorAPI {
} }
}; };
try { 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 : { headers : {
'x-user-token' : token 'x-user-token' : token
} }
...@@ -501,6 +559,11 @@ class TabulatorAPI { ...@@ -501,6 +559,11 @@ class TabulatorAPI {
return response.data.success; return response.data.success;
} }
catch(error) { catch(error) {
uni.showModal({
title : '创建失败',
content : error.message,
showCancel : false
});
console.error(error); console.error(error);
return false; return false;
} }
...@@ -524,14 +587,17 @@ class TabulatorAPI { ...@@ -524,14 +587,17 @@ class TabulatorAPI {
return undefined; return undefined;
} }
}, },
FindALL : async (token : string, obj : ApiKeyFindObject = {}) : Promise<Array<ApiKey>> => { FindALL : async (token : string, obj : ApiKeyFindObject = {}) : Promise<AllAPI> => {
let response : { let response : {
data : { data : {
data : Array<ApiKeyObject>; data : Array<ApiKeyObject>;
total : number;
} }
}; };
try { 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 : { params : {
recordsPerPage : 20, recordsPerPage : 20,
pageCount : obj.pageCount ?? 1, pageCount : obj.pageCount ?? 1,
...@@ -547,11 +613,17 @@ class TabulatorAPI { ...@@ -547,11 +613,17 @@ class TabulatorAPI {
response.data.data.forEach((i : ApiKeyObject) => { response.data.data.forEach((i : ApiKeyObject) => {
keys.push(new ApiKey(i)); keys.push(new ApiKey(i));
}) })
return keys; return {
total : response.data.total,
api : keys
};
} }
catch(error) { catch(error) {
console.error(error); console.error(error);
return []; return {
total : 0,
api : []
};
} }
}, },
Update : async (token : string, id : number, Data : ApiKeyCreateObject) : Promise<boolean> => { Update : async (token : string, id : number, Data : ApiKeyCreateObject) : Promise<boolean> => {
...@@ -561,7 +633,12 @@ class TabulatorAPI { ...@@ -561,7 +633,12 @@ class TabulatorAPI {
} }
}; };
try { 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 : { headers : {
'x-user-token' : token 'x-user-token' : token
} }
......
...@@ -64,6 +64,7 @@ interface TournamentObject extends TournamentCreateObject { ...@@ -64,6 +64,7 @@ interface TournamentObject extends TournamentCreateObject {
creator : number; creator : number;
createdAt : string; createdAt : string;
participants : Array<ParticipantObject>; participants : Array<ParticipantObject>;
matches : Array<MatchObject>
} }
interface TournamentFindObject { interface TournamentFindObject {
...@@ -121,8 +122,8 @@ interface ApiKeyFindObject { ...@@ -121,8 +122,8 @@ interface ApiKeyFindObject {
interface ApiKeyCreateObject { interface ApiKeyCreateObject {
name : string; name : string;
description : string; description ?: string;
expireAt : string; expireAt ?: Date;
} }
interface ApiKeyObject extends ApiKeyCreateObject { interface ApiKeyObject extends ApiKeyCreateObject {
...@@ -144,12 +145,17 @@ interface AllParticipant extends All { ...@@ -144,12 +145,17 @@ interface AllParticipant extends All {
} }
interface AllMatch 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; tournament : Tournament | undefined;
participant : AllParticipant participant : AllParticipant,
match : AllMatch
} }
interface UserObject { interface UserObject {
...@@ -182,8 +188,9 @@ export { ...@@ -182,8 +188,9 @@ export {
AllTournament, AllTournament,
AllParticipant, AllParticipant,
AllMatch, AllMatch,
AllAPI,
ruleSettings, ruleSettings,
UserObject, UserObject,
TournamentAParticipant, TournamentGet,
Deck Deck
} }
\ No newline at end of file
...@@ -69,6 +69,22 @@ class UniappFuncs { ...@@ -69,6 +69,22 @@ class UniappFuncs {
uni.offDeviceOrientationChange(chk) uni.offDeviceOrientationChange(chk)
// #endif // #endif
} }
copy (string) : void {
uni.setClipboardData({
data: string,
success: () => {
uni.showToast({
title: '复制成功',
});
},
fail: () => {
uni.showToast({
title: '复制失败',
});
}
});
}
} }
const UniApp = new UniappFuncs() const UniApp = new UniappFuncs()
......
#page { #page {
#head { .head {
box-shadow: 2px 2px 10px rgba(0, 0, 0, 0.2);
display: grid; display: grid;
grid-template-columns: repeat(5, 1fr); grid-template-columns: repeat(5, 1fr);
grid-template-rows: 1fr; grid-template-rows: 1fr;
align-items: center; align-items: center;
position: relative; z-index: 0;
:deep(.uni-list) { :deep(.uni-list) {
:hover { :hover {
cursor: pointer; 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 { #body {
width: var(--size); width: var(--size);
...@@ -27,11 +30,11 @@ ...@@ -27,11 +30,11 @@
} }
} }
:deep(#user) { :deep(#user) {
position: absolute; position: fixed;
right: 2%; right: 2%;
z-index: 1; z-index: 2;
max-width: var(--size); max-width: var(--size);
width: 20%; min-width: var(--minsize);
.user { .user {
&:hover { &:hover {
cursor: pointer; cursor: pointer;
...@@ -40,8 +43,8 @@ ...@@ -40,8 +43,8 @@
} }
} }
:deep(#drawer) { :deep(#drawer) {
position: absolute; position: fixed;
z-index: 1; z-index: 2;
width: var(--size); width: var(--size);
height: 80%; height: 80%;
overflow-y: auto; overflow-y: auto;
...@@ -68,33 +71,21 @@ ...@@ -68,33 +71,21 @@
} }
} }
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 { .Pics {
position: absolute; position: fixed;
z-index: 1; z-index: 2;
right: 2%; right: 2%;
width: 50%; max-width: var(--maxsize);
:deep(.uni-card) { min-width: var(--minsize);
// display: flex; :deep(#main) {
.deck_cards { overflow-y: auto;
width: 10%; max-height: 90vh;
height: 11.52vh; .deck {
aspect-ratio: 1 / 1.43; .deck_cards {
display: inline-block; width: var(--height);
vertical-align: top; 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 @@ ...@@ -43,13 +43,15 @@
:deep(.uni-list-item) { :deep(.uni-list-item) {
.small { .small {
margin-top: 6rpx; margin-top: 6px;
color: #999; color: #999;
font-size: 12px; font-size: 12px;
overflow: hidden; overflow: hidden;
} }
#header { #header {
min-width: 30%; min-width: 30%;
max-width: 30%;
text-overflow: ellipsis;
} }
#body { #body {
&:hover { &:hover {
...@@ -63,7 +65,7 @@ ...@@ -63,7 +65,7 @@
justify-items: center; justify-items: center;
align-items: center; align-items: center;
column-gap: 10%; column-gap: 10%;
width: 10%; width: 20%;
.button { .button {
border: 0.5px solid var(--color); border: 0.5px solid var(--color);
width: auto; width: auto;
...@@ -79,7 +81,7 @@ ...@@ -79,7 +81,7 @@
.match { .match {
border: 1px solid gray; border: 1px solid gray;
z-index: 1; z-index: 0;
// left : 100%; // left : 100%;
width: 100%; width: 100%;
background-color: white; background-color: white;
......
...@@ -49,6 +49,12 @@ ...@@ -49,6 +49,12 @@
} }
} }
.move_right_slow, .move_left_slow {
&-leave-active {
transition: transform 1s ease;
}
}
.move_up { .move_up {
&-enter-active, &-enter-active,
&-leave-active { &-leave-active {
...@@ -64,4 +70,24 @@ ...@@ -64,4 +70,24 @@
&-leave-from { &-leave-from {
transform: translateY(0%); 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