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
<template> <template>
<view id = 'page'> <view id = 'main_page'>
<view id = 'head' class = 'click'> <transition name = 'page'>
<view> <view v-show = 'loading' class = 'background'>
<button class = 'button' @click = 'page.show.drawer()'> <view class = 'pic' v-html = 'Const.pic.hajimi'></view>
<uni-icons :type = "page.menu ? page.drawer ? 'left' : 'search' : page.drawer ? 'left' : 'info'"></uni-icons>
</button>
</view>
<view>&nbsp;</view>
<view>&nbsp;</view>
<view>&nbsp;</view>
<uni-list>
<uni-list-chat
:avatar-circle = true
:title = 'Mycard.username'
:note = 'Mycard.email'
:clickable = true
:avatar = 'Mycard.avatar'
@click = 'page.show.user()'
>
<uni-icons :type = "page.user ? 'right' : 'left'"></uni-icons>
</uni-list-chat>
</uni-list>
</view>
<Pics></Pics>
<transition name = 'move_right'>
<uni-card
class = 'click'
id = 'user'
v-show = 'page.user'
:title = 'Mycard.username'
:sub-title = 'Mycard.email'
:thumbnail = 'Mycard.avatar'
:style = "{ '--size' : `${size.width / 2}px` }"
>
<view v-show = 'Mycard.id >= 0'><h2>{{ Mycard.id }}</h2></view>
<hr v-show = 'Mycard.id >= 0'>
<view
v-show = 'Mycard.id >= 0'
class = 'user'
><span>获取api_key</span></view>
<hr v-show = 'Mycard.id >= 0'>
<view
v-show = 'Mycard.id >= 0'
class = 'user'
@click = 'page.show.create()'
><span>新建比赛</span></view>
<hr v-show = 'Mycard.id >= 0'>
<view
class = 'user'
@click = '() => {
Mycard.id >= 0 ? Mycard.logout() : Mycard.login();
}'
><span>{{ Mycard.id >= 0 ? '退出登陆' : '登陆' }}</span></view>
</uni-card>
</transition>
<transition name = 'move_left'>
<uni-card
class = 'click'
id = 'drawer'
v-show = 'page.drawer && page.menu && !page.create'
title = '搜索'
:style = "{ '--size' : `${size.width > size.height ? size.width / 4 : size.width / 2}px` }"
>
<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>
</transition>
<transition name = 'move_left'>
<uni-card
class = 'click'
id = 'drawer'
v-show = 'page.drawer && page.tournament && !page.create'
title = '比赛设置'
:style = "{ '--size' : `${size.width > size.height ? size.width / 4 : size.width / 2}px` }"
>
<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>
</transition>
<transition name = 'move_left'>
<Create
class = 'click'
id = 'drawer'
v-show = 'page.drawer && page.create'
title = '比赛创建'
:style = "{ '--size' : `${size.width > size.height ? size.width / 4 : size.width / 2}px` }"
>
</Create>
</transition>
<br>
<transition name = 'switch'>
<view
id = 'body'
:style = "{ '--size' : `${size.width > size.height ? size.width / 2 : size.width / 1.2}px` }"
>
<transition name = 'switch'>
<uni-list
v-show = 'search.result.total > 0 && page.menu'
>
<uni-list-item
id = 'list'
v-for = '(i, v) in search.result.tournaments'
:title = 'i.name'
:note = 'search.rule.note.get(i.rule)'
:rightText = '`${i.createdAt.toLocaleDateString()}\n${i.count}`'
:clickable = true
@click = 'page.show.tournament(v)'
></uni-list-item>
</uni-list>
</transition>
<transition name = 'switch'>
<uni-pagination
:current = 'search.info.pageCount'
v-model = 'search.info.pageCount'
pageSize = 20
:total = 'search.result.total'
v-show = 'page.menu'
@change = 'search.on()'
>
</uni-pagination>
</transition>
<transition name = 'switch'>
<PageTournament v-if = 'page.tournament'/>
</transition>
</view> </view>
</transition> </transition>
<Tabulator/>
</view> </view>
</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, TournamentCreateObject, ruleSettings, UserObject } from '../script/type.ts'; import Tabulator from './tabulator.vue';
import Uniapp from '../script/uniapp.ts'; import emitter from '../script/emitter.ts';
import {Tabulator, User} from '../script/post.ts'; import Const from '../script/const.ts';
import Tournament from '../script/tournament.ts';
import ApiKey from '../script/apikey.ts';
import Mycard from '../script/mycard.ts';
import emitter from '../script/emitter.ts'
import Const from '../script/const.ts'
import PageTournament from './tournament.vue';
import Create from './drawer.vue';
import Pics from './pics.vue';
let page = reactive({
user : false,
drawer : false,
menu : true,
tournament : false,
create : false,
show : {
user : () : void => {
page.user = !page.user;
},
drawer : () : void => {
page.drawer = !page.drawer;
if (!page.drawer && page.create)
page.create = false;
if (!page.drawer && page.tournament)
tournament.init(tournament.this as Tournament);
},
tournament : async(v : number = 0): Promise<void> => {
page.menu = false;
await (new Promise(resolve => setTimeout(resolve, 500)));
page.tournament = true;
const url = window.location.href.split('/?');
window.location.replace(`${url[0].replace(/\/?$/, '')}/tournament/${search.result.tournaments[v].id}${url[1] ? `/?${url[1]}` : ''}`);
},
menu : async(): Promise<void> => {
page.tournament = false;
tournament.clear();
await search.on();
await (new Promise(resolve => setTimeout(resolve, 500)));
page.menu = true;
},
create : async(): Promise<void> => {
if (page.drawer) {
page.show.drawer();
await (new Promise(resolve => setTimeout(resolve, 500)));
}
page.create = true;
page.show.drawer();
},
clear : (e) : void => {
let element = e.target;
let chk = false;
let inPage = false;
let uniElement = false;
while (element) {
if (['head', 'user', 'drawer'].includes(element.id) || element.classList.contains('click'))
chk = true;
if (element.id == 'page')
inPage = true;
if (typeof element.className == 'string' && element.className.includes('uni'))
uniElement = true;
element = element.parentElement;
}
if (chk || (!inPage && uniElement))
return undefined;
if (page.user)
page.show.user();
if (page.drawer)
page.show.drawer();
}
}
});
let search = reactive({
id : '',
creator : '',
date : [] as Array<string>,
rule : {
range : [
{ value: ' ', text: '全部' },
{ value: 'SingleElimination', text: '单淘' },
{ value: 'Swiss', text: '瑞士轮' }
],
note : new Map([
['SingleElimination' , '单淘'],
['Swiss' , '瑞士轮']
]) as Map<string, string>
},
visibility : {
range: [
{ value: ' ', text: '全部' },
{ value: 'Public', text: '公开' },
{ value: 'Internal', text: '仅登陆可见' },
{ value: 'Private', text: '私密' }
]
},
status : {
range: [
{ value: ' ', text: '全部' },
{ value: 'Ready', text: '准备中' },
{ value: 'Running', text: '进行中' },
{ value: 'Finished', text: '已结束' }
]
},
info : {
pageCount : 1,
id : 0,
creator : 0,
name : '',
rule : '',
visibility : '',
status : ''
} as TournamentFindObject,
result : {
total : 0,
tournaments : [] as Array<Tournament>
},
on : async (Data : TournamentFindObject = search.info) : Promise<void> => {
page.drawer = false;
if (search.result.total > 0) {
search.result.total = 0;
}
const result = await Tabulator.Tournament.FindALL(Mycard.token, Data);
search.result.total = result.total;
search.result.tournaments = result.tournaments;
},
mine : () => {
search.creator = Mycard.id >= 0 ? Mycard.id.toString() : '';
}
});
let tournament = reactive({
this : undefined as undefined | Tournament,
name : '',
description : '',
visibility : {
select : '',
range : [
{ value: 'Public', text: '公开' },
{ value: 'Internal', text: '仅登陆可见' },
{ value: 'Private', text: '私密' }
]
},
rule : {
select : '',
range : [
{ value: 'SingleElimination', text: '单淘' },
{ value: 'Swiss', text: '瑞士轮' }
],
settings : {
} as ruleSettings
},
hasThirdPlaceMatch : {
select : (e) => {
tournament.rule.settings.hasThirdPlaceMatch = e.detail.value.length > 0
}
},
collaborator : '',
collaborators : [] as Array<UserObject>,
init : async (t : Tournament) : Promise<void> => {
if (!t) return;
tournament.this = t;
tournament.name = t.name;
tournament.description = t.description;
tournament.visibility.select = t.visibility;
tournament.rule.select = t.rule;
tournament.rule.settings = Object.assign({}, t.ruleSettings);
let collaborators : Array<UserObject> = [];
for (const id of t.collaborators) {
const i = tournament.collaborators.find(i => i.id == id) ?? await User.Find.Id(id);
if (i) collaborators.push(i);
}
tournament.collaborators = collaborators;
},
clear : () : void => {
tournament.this = undefined;
tournament.name = '';
tournament.description = '';
tournament.visibility.select = '';
tournament.rule.select = '';
tournament.rule.settings = {} as ruleSettings;
tournament.collaborators = [];
},
update : () : void => {
if (tournament.visibility.select == '')
// @ts-ignore
tournament.visibility.select = tournament.this.visibility;
const collaborators = tournament.collaborators.map(user => user.id); let loading = ref(true);
emitter.emit(Const.updateTournament, {
name: tournament.name,
description: tournament.description,
visibility: tournament.visibility.select,
collaborators : collaborators,
// PS:这里接口暂时不通
// rule : tournament.rule.select,
// ruleSettings : tournament.rule.settings
} as TournamentCreateObject);
},
remove : (v : number) : void => {
tournament.collaborators.splice(v, 1);
},
add : async() : Promise<void> => {
try {
if (tournament.collaborators.findIndex(i => i.username == tournament.collaborator) >= 0)
throw new Error('协作者已存在');
const i = await User.Find.Name(tournament.collaborator);
if (!i)
throw new Error('未搜索到此用户');
if (tournament.this?.creator == i.id)
throw new Error('协作者不可以是比赛创建者');
tournament.collaborators.push(i);
} catch(error) {
uni.showModal({
title : '添加失败',
content : error.message,
showCancel : false
});
} finally {
tournament.collaborator = '';
}
},
operatorChk : () : boolean => {
if (!tournament.this)
return true
return !(Mycard.id >= 0 && (Mycard.id == tournament.this?.creator || tournament.this?.collaborators.includes(Mycard.id)))
}
});
const creator = { const changeUrl = async (url : string) : Promise<void> => {
off : async () : Promise<void> => { loading.value = !loading.value;
page.show.drawer(); await (new Promise(resolve => setTimeout(resolve, 500)));
if (page.tournament) window.location.replace(url);
page.show.menu(); }
search.mine();
await search.on();
}
};
let size = reactive({ const show = async (chk : boolean | undefined = undefined) : Promise<void> => {
width : 0, await (new Promise(resolve => setTimeout(resolve, 500)));
height : 0, if (typeof chk === 'undefined')
get : () => { loading.value = !loading.value;
// @ts-ignore else loading.value = chk;
size.width = uni.getSystemInfoSync().windowWidth; }
size.height = uni.getSystemInfoSync().windowHeight;
}
});
onBeforeMount(() : void => { onBeforeMount(() : void => {
Uniapp.chkScreen(size.get);
document.addEventListener("click", page.show.clear);
emitter.on(Const.tournamentInfo, page.show.drawer);
// @ts-ignore // @ts-ignore
emitter.on(Const.tournamentReload, tournament.init); emitter.on(Const.changeUrl, changeUrl);
emitter.on(Const.createOff, creator.off);
const url = window.location.pathname.match(/\/tournament\/([^\/]+)(?=\/|$)/);
if (url && !isNaN(parseInt(url[1]))) {
page.menu = false;
page.tournament = true;
} else {
if (window.location.pathname.length > 1)
window.location.replace(window.location.href.replace(window.location.pathname, ''))
else {
search.mine();
search.on();
}
}
});
onUnmounted(() => {
document.removeEventListener("click", page.show.clear);
emitter.off(Const.tournamentInfo, page.show.drawer);
// @ts-ignore // @ts-ignore
emitter.off(Const.tournamentReload, tournament.init); emitter.on(Const.show, show);
emitter.off(Const.createOff, creator.off); })
});
watch(() => { return search.date; }, () => {
search.date.length > 0 ? () => {
search.info.after = new Date(search.date[0]);
search.info.before = new Date(`${search.date[1]}T23:59:59.999`);
} : () => {
search.info.after = undefined;
search.info.before = undefined;
}
});
watch(() => { return search.id; }, () => {
search.info.id = search.id == '' ? 0 : parseInt(search.id);
});
watch(() => { return search.creator; }, () => { onUnmounted(() : void => {
search.info.creator = search.creator == '' ? 0 : parseInt(search.creator); // @ts-ignore
emitter.off(Const.changeUrl, changeUrl);
// @ts-ignore
emitter.off(Const.show, show);
}); });
</script> </script>
<style scoped lang = 'scss'> <style scoped lang = 'scss'>
@import '../style/page.scss';
@import '../style/transition.scss'; @import '../style/transition.scss';
@import '../style/style.scss';
#main_page {
.background {
position: fixed;
z-index: 100;
display: flex;
justify-content: center;
align-items: center;
margin: 0;
.pic {
white-space: pre;
line-height: 1;
letter-spacing: 0;
}
}
.Tabulator {
position: fixed;
z-index: 1;
}
}
</style> </style>
\ No newline at end of file
<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
<template>
<view id = 'page'>
<view id = 'really' class = 'click head'>
<view>
<button
:style = "size.width > size.height ? { width: '30%' } : {}"
class = 'button'
@click = 'page.show.drawer()'
>
<uni-icons :type = "page.menu ? page.drawer ? 'left' : 'search' : page.drawer ? 'left' : 'info'"></uni-icons>
</button>
</view>
<view>&nbsp;</view>
<view>&nbsp;</view>
<view>&nbsp;</view>
<uni-list>
<uni-list-chat
:avatar-circle = true
:title = 'Mycard.username'
:note = 'Mycard.email'
:clickable = true
:avatar = 'Mycard.avatar'
@click = 'page.show.user()'
>
<uni-icons :type = "page.user ? 'right' : 'left'"></uni-icons>
</uni-list-chat>
</uni-list>
</view>
<view class = 'click head'><uni-list><uni-list-chat></uni-list-chat></uni-list></view>
<Pics
:style = "{
'--maxsize' : `${size.width > size.height ? size.width / 2 : size.width / 1.2}px`,
'--minsize' : `${size.width / 2}px`,
'--height' : `${size.width > size.height ? 10 : 25}%`,
}"
></Pics>
<transition name = 'move_right'>
<uni-card
class = 'click'
id = 'user'
v-show = 'page.user'
:title = 'Mycard.username'
:sub-title = 'Mycard.email'
:thumbnail = 'Mycard.avatar'
:style = "{ '--size' : `${size.width > size.height ? size.width / 2 : size.width / 1.2}px`, '--minsize' : `${size.width > size.height ? size.width / 4 : size.width / 2.4}px` }"
>
<view v-show = 'Mycard.id >= 0'><h2>{{ Mycard.id }}</h2></view>
<hr v-show = 'Mycard.id >= 0'>
<view
v-show = 'Mycard.id >= 0'
class = 'user'
@click = 'page.show.api()'
><span>api_key</span></view>
<hr v-show = 'Mycard.id >= 0'>
<view
v-show = 'Mycard.id >= 0'
class = 'user'
@click = 'page.show.create()'
><span>新建比赛</span></view>
<hr v-show = 'Mycard.id >= 0'>
<view
class = 'user'
@click = '() => {
Mycard.id >= 0 ? Mycard.logout() : Mycard.login();
}'
><span>{{ Mycard.id >= 0 ? '退出登陆' : '登陆' }}</span></view>
</uni-card>
</transition>
<transition name = 'move_left'>
<Searcher
class = 'click'
id = 'drawer'
v-show = 'page.drawer && page.menu && !(page.create || page.apikey)'
title = '搜索'
:style = "{ '--size' : `${size.width > size.height ? size.width / 4 : size.width / 2}px` }"
></Searcher>
</transition>
<transition name = 'move_left'>
<Setting
class = 'click'
id = 'drawer'
v-show = 'page.drawer && page.tournament && !(page.create || page.apikey)'
title = '比赛设置'
:style = "{ '--size' : `${size.width > size.height ? size.width / 4 : size.width / 2}px` }"
></Setting>
</transition>
<transition name = 'move_left'>
<Create
class = 'click'
id = 'drawer'
v-show = 'page.drawer && page.create'
title = '比赛创建'
:style = "{ '--size' : `${size.width > size.height ? size.width / 4 : size.width / 2}px` }"
>
</Create>
</transition>
<transition name = 'move_left'>
<API
class = 'click'
id = 'drawer'
v-show = 'page.drawer && page.apikey'
title = '我的api密钥'
:style = "{ '--size' : `${size.width > size.height ? size.width / 4 : size.width / 2}px` }"
>
</API>
</transition>
<br>
<transition name = 'switch'>
<view
id = 'body'
:style = "{ '--size' : `${size.width > size.height ? size.width / 2 : size.width}px` }"
>
<transition name = 'switch'>
<uni-list
v-show = 'search.result.total > 0 && page.menu'
>
<uni-list-item
id = 'list'
v-for = '(i, v) in search.result.tournaments'
:title = 'i.name'
:note = 'search.rule.note.get(i.rule)'
:rightText = '`${i.createdAt.toLocaleDateString()}\n${i.count}`'
:clickable = true
@click = 'page.show.tournament(v)'
></uni-list-item>
</uni-list>
</transition>
<transition name = 'switch'>
<uni-pagination
:current = 'search.info.pageCount'
v-model = 'search.info.pageCount'
pageSize = 20
:total = 'search.result.total'
v-show = 'page.menu'
@change = 'search.on()'
>
</uni-pagination>
</transition>
<transition name = 'switch'>
<PageTournament v-if = 'page.tournament'/>
</transition>
</view>
</transition>
</view>
</template>
<script setup lang = 'ts'>
import { ref, reactive, onMounted, onUnmounted, onBeforeMount, watch} from 'vue';
import { TournamentFindObject, TournamentCreateObject, ruleSettings, UserObject } from '../script/type.ts';
import Uniapp from '../script/uniapp.ts';
import { Tabulator, User} from '../script/post.ts';
import Tournament from '../script/tournament.ts';
import Mycard from '../script/mycard.ts';
import emitter from '../script/emitter.ts'
import Const from '../script/const.ts'
import PageTournament from './tournament.vue';
import Create from './drawer/creator.vue';
import Pics from './pics.vue';
import Setting from './drawer/setting.vue';
import Searcher from './drawer/searcher.vue';
import API from './drawer/api.vue';
let page = reactive({
user : false,
drawer : false,
menu : true,
tournament : false,
create : false,
apikey : false,
show : {
user : () : void => {
page.user = !page.user;
},
drawer : () : void => {
page.drawer = !page.drawer;
if (!page.drawer && page.create)
page.create = false;
if (!page.drawer && page.apikey)
page.apikey = false;
if (!page.drawer && page.tournament)
tournament.init(tournament.this as Tournament);
},
tournament : (v : number = 0): void => {
const url = window.location.href.split('/?');
emitter.emit(Const.changeUrl, `${url[0].endsWith('/') ? url[0] : `${url[0]}/`}${search.result.tournaments[v].id}${url[1] ? `/?${url[1]}` : ''}`);
},
menu : async(): Promise<void> => {
page.tournament = false;
tournament.clear();
await search.on();
await (new Promise(resolve => setTimeout(resolve, 500)));
page.menu = true;
},
create : async(): Promise<void> => {
if (page.drawer) {
page.show.drawer();
await (new Promise(resolve => setTimeout(resolve, 500)));
}
page.create = true;
page.show.drawer();
},
api : async(): Promise<void> => {
if (page.drawer) {
page.show.drawer();
await (new Promise(resolve => setTimeout(resolve, 500)));
}
page.apikey = true;
page.show.drawer();
},
clear : (e) : void => {
let element = e.target;
let chk = false;
let inPage = false;
let uniElement = false;
while (element) {
if (['head', 'user', 'drawer'].includes(element.id) || element.classList.contains('click'))
chk = true;
if (element.id == 'page')
inPage = true;
if (typeof element.className == 'string' && element.className.includes('uni'))
uniElement = true;
element = element.parentElement;
}
if (chk || (!inPage && uniElement))
return undefined;
if (page.user)
page.show.user();
if (page.drawer)
page.show.drawer();
}
}
});
let search = reactive({
id : '',
creator : '',
date : [] as Array<string>,
rule : {
range : [
{ value: ' ', text: '全部' },
{ value: 'SingleElimination', text: '单淘' },
{ value: 'Swiss', text: '瑞士轮' }
],
note : new Map([
['SingleElimination' , '单淘'],
['Swiss' , '瑞士轮']
]) as Map<string, string>
},
visibility : {
range: [
{ value: ' ', text: '全部' },
{ value: 'Public', text: '公开' },
{ value: 'Internal', text: '仅登陆可见' },
{ value: 'Private', text: '私密' }
]
},
status : {
range: [
{ value: ' ', text: '全部' },
{ value: 'Ready', text: '准备中' },
{ value: 'Running', text: '进行中' },
{ value: 'Finished', text: '已结束' }
]
},
info : {
pageCount : 1,
id : 0,
creator : 0,
before : undefined,
after : undefined,
name : '',
rule : '',
visibility : '',
status : ''
} as TournamentFindObject,
result : {
total : 0,
tournaments : [] as Array<Tournament>
},
on : async (Data : TournamentFindObject = search.info) : Promise<void> => {
page.drawer = false;
if (search.result.total > 0) {
search.result.total = 0;
}
const result = await Tabulator.Tournament.FindALL(Mycard.token, Data);
search.result.total = result.total;
search.result.tournaments = result.tournaments;
},
mine : () => {
search.creator = Mycard.id >= 0 ? Mycard.id.toString() : '';
}
});
let tournament = reactive({
this : undefined as undefined | Tournament,
name : '',
description : '',
visibility : {
select : '',
range : [
{ value: 'Public', text: '公开' },
{ value: 'Internal', text: '仅登陆可见' },
{ value: 'Private', text: '私密' }
]
},
rule : {
select : '',
range : [
{ value: 'SingleElimination', text: '单淘' },
{ value: 'Swiss', text: '瑞士轮' }
],
settings : {
} as ruleSettings
},
hasThirdPlaceMatch : {
select : (e) => {
tournament.rule.settings.hasThirdPlaceMatch = e.detail.value.length > 0
}
},
collaborator : '',
collaborators : [] as Array<UserObject>,
init : async (t : Tournament) : Promise<void> => {
if (!t) return;
tournament.this = t;
tournament.name = t.name;
tournament.description = t.description;
tournament.visibility.select = t.visibility;
tournament.rule.select = t.rule;
tournament.rule.settings = Object.assign({}, t.ruleSettings);
let collaborators : Array<UserObject> = [];
for (const id of t.collaborators) {
const i = tournament.collaborators.find(i => i.id == id) ?? await User.Find.Id(id);
if (i) collaborators.push(i);
}
tournament.collaborators = collaborators;
},
clear : () : void => {
tournament.this = undefined;
tournament.name = '';
tournament.description = '';
tournament.visibility.select = '';
tournament.rule.select = '';
tournament.rule.settings = {} as ruleSettings;
tournament.collaborators = [];
},
update : () : void => {
if (!tournament.visibility.select)
// @ts-ignore
tournament.visibility.select = tournament.this.visibility;
const collaborators = tournament.collaborators.map(user => user.id);
emitter.emit(Const.updateTournament, {
name: tournament.name,
description: tournament.description,
visibility: tournament.visibility.select,
collaborators : collaborators,
// PS:这里接口暂时不通
// rule : tournament.rule.select,
// ruleSettings : tournament.rule.settings
} as TournamentCreateObject);
},
remove : (v : number) : void => {
tournament.collaborators.splice(v, 1);
},
add : async() : Promise<void> => {
try {
if (tournament.collaborators.findIndex(i => i.username == tournament.collaborator) >= 0)
throw new Error('协作者已存在');
const i = await User.Find.Name(tournament.collaborator);
if (!i)
throw new Error('未搜索到此用户');
if (tournament.this?.creator == i.id)
throw new Error('协作者不可以是比赛创建者');
tournament.collaborators.push(i);
} catch(error) {
uni.showModal({
title : '添加失败',
content : error.message,
showCancel : false
});
} finally {
tournament.collaborator = '';
}
},
operatorChk : () : boolean => {
if (!tournament.this)
return true
return !(Mycard.id >= 0 && (Mycard.id == tournament.this?.creator || tournament.this?.collaborators.includes(Mycard.id)))
}
});
const creator = {
off : async () : Promise<void> => {
page.show.drawer();
if (page.tournament)
page.show.menu();
search.mine();
await search.on();
}
};
let size = reactive({
width : 0,
height : 0,
get : () => {
// @ts-ignore
size.width = uni.getSystemInfoSync().windowWidth;
size.height = uni.getSystemInfoSync().windowHeight;
}
});
const loading = async () : Promise<void> => {
const url = window.location.hash.match(/#\/(.*?)(?:\?|$)/);
if (url) {
if (!isNaN(parseInt(url[1]))) {
page.menu = false;
page.tournament = true;
emitter.emit(Const.showTournament);
} else if (url[1].length == 0) {
if (window.location.href.includes('/?') && window.location.hash.endsWith('#/')) {
const i = window.location.href.split('/?');
window.location.replace(`${i[0].replace('#/', '')}/#${i[1] ? `/?${i[1].replace('#/', '')}` : '/'}`);
return;
}
page.tournament = false;
page.menu = true;
search.mine();
await search.on();
emitter.emit(Const.show, false);
}
} else {
const i = window.location.href.split('/?');
emitter.emit(Const.changeUrl, `${window.location.origin}/#${i[1] ? `/?${i[1]}` : '/'}`);
}
};
onBeforeMount(() : void => {
loading();
Uniapp.chkScreen(size.get);
document.addEventListener("click", page.show.clear);
emitter.on(Const.tournamentInfo, page.show.drawer);
// @ts-ignore
emitter.on(Const.tournamentReload, tournament.init);
emitter.on(Const.createOff, creator.off);
window.addEventListener('hashchange', loading);
});
onMounted(() => {
emitter.emit(Const.settingInit, tournament);
emitter.emit(Const.searcherInit, search);
});
onUnmounted(() => {
document.removeEventListener("click", page.show.clear);
emitter.off(Const.tournamentInfo, page.show.drawer);
// @ts-ignore
emitter.off(Const.tournamentReload, tournament.init);
emitter.off(Const.createOff, creator.off);
window.removeEventListener('hashchange', loading);
});
watch(() => { return search.date; }, () => {
const toDate = () => {
search.info.after = new Date(search.date[0]);
search.info.before = new Date(`${search.date[1]}T23:59:59.999`);
};
const toUndefined = () => {
search.info.after = undefined;
search.info.before = undefined;
};
search.date.length > 0 ? toDate() : toUndefined();
});
watch(() => { return search.id; }, () => {
search.info.id = !search.id ? 0 : parseInt(search.id);
});
watch(() => { return search.creator; }, () => {
search.info.creator = !search.creator ? 0 : parseInt(search.creator);
});
</script>
<style scoped lang = 'scss'>
@import '../style/page.scss';
@import '../style/transition.scss';
@import '../style/style.scss';
</style>
\ No newline at end of file
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
</view> </view>
<view class = 'button' @click = 'page.copyUrl()'> <view class = 'button' @click = 'page.copyUrl()'>
<span>分享</span> <span>分享</span>
<uni-icons type = 'redo'></uni-icons> <uni-icons type = 'paperclip'></uni-icons>
</view> </view>
<view class = 'button' @click = 'page.clear()'> <view class = 'button' @click = 'page.clear()'>
<span>关闭</span> <span>关闭</span>
...@@ -39,6 +39,10 @@ ...@@ -39,6 +39,10 @@
<span>重置</span> <span>重置</span>
<uni-icons type = 'loop'></uni-icons> <uni-icons type = 'loop'></uni-icons>
</view> </view>
<view class = 'button click' @click = 'tournament.operatorChk(tournament.shuffle)'>
<span>打乱</span>
<uni-icons type = 'auth'></uni-icons>
</view>
<view class = 'button click' @click = 'tournament.operatorChk(tournament.upload)'> <view class = 'button click' @click = 'tournament.operatorChk(tournament.upload)'>
<span>上传</span> <span>上传</span>
<uni-icons type = 'cloud-upload'></uni-icons> <uni-icons type = 'cloud-upload'></uni-icons>
...@@ -65,20 +69,37 @@ ...@@ -65,20 +69,37 @@
</uni-list-item> </uni-list-item>
<uni-list-item <uni-list-item
v-for = '(i, v) in participant.array.slice((participant.page - 1) * 20, participant.page * 20)' v-for = '(i, v) in participant.array.slice((participant.page - 1) * 20, participant.page * 20)'
:title = "i.score ? `胜平负:${i.score.win + i.score.bye}-${i.score.draw}-${i.score.lose}` : ''" :title = "i.score && match.array.findIndex(m => (m.status == 'Finished' || m.status == 'Abandoned') && (m.player1Id == i.id || m.player2Id == i.id)) > -1 ? `胜平负:${i.score.win + i.score.bye}-${i.score.draw}-${i.score.lose}` : ''"
:note = "i.score ? `小分:${i.score.score}` : ''" :note = "i.score && match.array.findIndex(m => (m.status == 'Finished' || m.status == 'Abandoned') && (m.player1Id == i.id || m.player2Id == i.id)) > -1 ? `小分:${i.score.score}` : ''"
:clickable = true :clickable = true
@click = 'participant.pics.on(i)'
> >
<template v-slot:header> <template v-slot:header>
<view id = 'header'> <view id = 'header'>
<span>{{ i.name }}</span> <span>{{ i.name }}</span>
<br> <br>
<span class = 'small'>{{ i.score ? i.score.rank : '' }}</span> <span class = 'small'>{{ tournament.this.status == 'Ready' ? `${v + 1}` : (i.score ? i.score.rank : '') }}</span>
</view> </view>
</template> </template>
<template v-slot:footer> <template v-slot:footer>
<view id = 'footer'> <view id = 'footer'>
<view
class = 'button'
id = 'deckbutton'
:style = "{ '--color' : participant.move.this === i ? '#409eff' : 'gray', 'background-color' : participant.move.this === i ? '#e6e6e6' : 'white'}"
v-show = '!i.quit'
@click = 'tournament.operatorChk(participant.move.start, [i, $event])'
>
<uni-icons :type = "participant.move.this && participant.move.this !== i ? 'pulldown' : 'list'"></uni-icons>
</view>
<view
class = 'button'
id = 'deckbutton'
:style = "{ '--color' : 'gray' }"
v-show = '!i.quit'
@click = 'participant.pics.on(i)'
>
<uni-icons type = 'wallet'></uni-icons>
</view>
<view <view
class = 'button' class = 'button'
:style = "{ '--color' : 'gray' }" :style = "{ '--color' : 'gray' }"
...@@ -90,7 +111,7 @@ ...@@ -90,7 +111,7 @@
<view <view
class = 'button' class = 'button'
:style = "{ '--color' : 'red' }" :style = "{ '--color' : 'red' }"
@click = 'tournament.operatorChk(participant.del, [v])' @click = 'tournament.operatorChk(participant.del, [i])'
v-show = '!i.quit' v-show = '!i.quit'
> >
<uni-icons type = 'trash' color = 'red'></uni-icons> <uni-icons type = 'trash' color = 'red'></uni-icons>
...@@ -141,13 +162,13 @@ ...@@ -141,13 +162,13 @@
<uni-number-box <uni-number-box
v-model = 'match.round' v-model = 'match.round'
:min = '1' :min = '0'
:max = 'match.maxRound' :max = 'match.maxRound'
:disabled = "tournament.this.status == 'Ready'" :disabled = "tournament.this.status == 'Ready'"
></uni-number-box> ></uni-number-box>
</div> </div>
<view class = 'button' @click = '() => { match.chk = true; match.page = 1; match.search(true); }'>全部轮次</view> <view class = 'button' @click = '() => { match.round = 0; }'>全部轮次</view>
</view> </view>
<transition name = 'switch'> <transition name = 'switch'>
<uni-list> <uni-list>
...@@ -156,7 +177,9 @@ ...@@ -156,7 +177,9 @@
title = '暂无比赛' title = '暂无比赛'
> >
</uni-list-item> </uni-list-item>
<view v-for = '(i, v) in match.array'> <view
v-for = 'i in (match.round == 0) ? match.array.slice((match.page - 1) * 20, match.page * 20) : match.array.filter(m => m.round == match.round).slice((match.page - 1) * 20, match.page * 20)'
>
<view <view
class = 'match' class = 'match'
v-show = "match.submit.page === i && (i.status == 'Running' || i.status == 'Finished')" v-show = "match.submit.page === i && (i.status == 'Running' || i.status == 'Finished')"
...@@ -166,7 +189,7 @@ ...@@ -166,7 +189,7 @@
:clearable = 'false' :clearable = 'false'
type = 'number' type = 'number'
:placeholder = 'participant.array.find(p => p.id == i.player1Id)?.name' :placeholder = 'participant.array.find(p => p.id == i.player1Id)?.name'
v-model = 'match.submit.chk[v][0]' v-model = 'match.submit.chk[match.array.findIndex(m => m === i)][0]'
:disabled = "i.status != 'Running'" :disabled = "i.status != 'Running'"
></uni-easyinput> ></uni-easyinput>
<view> <view>
...@@ -176,15 +199,15 @@ ...@@ -176,15 +199,15 @@
:clearable = 'false' :clearable = 'false'
type = 'number' type = 'number'
:placeholder = 'participant.array.find(p => p.id == i.player2Id)?.name' :placeholder = 'participant.array.find(p => p.id == i.player2Id)?.name'
v-model = 'match.submit.chk[v][1]' v-model = 'match.submit.chk[match.array.findIndex(m => m === i)][1]'
:disabled = "i.status != 'Running'" :disabled = "i.status != 'Running'"
></uni-easyinput> ></uni-easyinput>
</view> </view>
<view class = 'button' @click = 'match.submit.on(v)'>{{ i.status == 'Running' ? '提交比分' : i.status == 'Finished' ? '重赛' : '' }}</view> <view class = 'button' @click = 'match.submit.on(i)'>{{ i.status == 'Running' ? '提交比分' : i.status == 'Finished' ? '重赛' : '' }}</view>
</view> </view>
<uni-list-item <uni-list-item
:clickable = true :clickable = true
@click = 'match.submit.show(v)' @click = 'match.submit.show(i)'
id = 'matchList' id = 'matchList'
> >
<template v-slot:body> <template v-slot:body>
...@@ -249,19 +272,25 @@ ...@@ -249,19 +272,25 @@
:current = 'match.page' :current = 'match.page'
v-model = 'match.page' v-model = 'match.page'
pageSize = 20 pageSize = 20
:total = 'match.total' :total = '(match.round == 0) ? match.total : match.array.filter(m => m.round == match.round).length'
@change = 'match.search()'
> >
</uni-pagination> </uni-pagination>
</uni-card> </uni-card>
</transition> </transition>
<transition name = 'switch'>
<MatchTree
v-if = "!page.loading && tournament.this.rule == 'SingleElimination'"
v-show = "match.array.length > 0"
:matches = 'match.array'
:participants = 'participant.array'
></MatchTree>
</transition>
</uni-card> </uni-card>
</transition> </transition>
</view> </view>
</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 YGOProDeck from 'ygopro-deck-encode';
import emitter from '../script/emitter.ts' import emitter from '../script/emitter.ts'
import UniApp from '../script/uniapp.ts'; import UniApp from '../script/uniapp.ts';
import {Tabulator, User} from '../script/post.ts'; import {Tabulator, User} from '../script/post.ts';
...@@ -270,7 +299,8 @@ ...@@ -270,7 +299,8 @@
import Match from '../script/match.ts'; import Match from '../script/match.ts';
import Const from '../script/const.ts' import Const from '../script/const.ts'
import Mycard from '../script/mycard.ts'; import Mycard from '../script/mycard.ts';
import {TournamentCreateObject, MatchUpdateObject, TournamentAParticipant, ParticipantUpdateObject} from '../script/type.ts' import {TournamentCreateObject, MatchUpdateObject, TournamentGet, ParticipantUpdateObject} from '../script/type.ts'
import MatchTree from './matchTree.vue';
let tournament = reactive({ let tournament = reactive({
this : undefined as undefined | Tournament, this : undefined as undefined | Tournament,
...@@ -286,6 +316,25 @@ ...@@ -286,6 +316,25 @@
['Finished', 'darkgray'] ['Finished', 'darkgray']
]) as Map<string, string>, ]) as Map<string, string>,
}, },
search : async () : Promise<boolean> => {
const url = window.location.hash.match(/#\/(.*?)(?:\?|$)/);
// @ts-ignore
const id = url[1];
// @ts-ignore
const t : TournamentGet = await Tabulator.Tournament.Find(Mycard.token, id);
if (t.tournament) {
tournament.this = t.tournament;
emitter.emit(Const.tournamentReload, tournament.this)
const participants = t.participant;
participant.array = participants.participants;
participant.total = participants.total;
const matches = t.match;
match.array = matches.matches;
match.total = matches.total;
return true;
}
return false;
},
on : () : void => { on : () : void => {
switch(tournament.this?.status) { switch(tournament.this?.status) {
case 'Ready': case 'Ready':
...@@ -339,6 +388,18 @@ ...@@ -339,6 +388,18 @@
} }
}); });
}, },
shuffle : () : void => {
if (tournament.this?.status != 'Ready') return;
uni.showModal({
title : '确认要打乱次序吗?',
success : async (res : UniApp.ShowModalRes) : Promise<void> => {
if (!res.confirm) return;
// @ts-ignore
if (await Tabulator.Tournament.Shuffle(Mycard.token, tournament.this.id))
tournament.search();
}
});
},
operatorChk : (f : Function, para : Array<any> = []) : void => { operatorChk : (f : Function, para : Array<any> = []) : void => {
if (Mycard.id >= 0 && (Mycard.id == tournament.this?.creator || tournament.this?.collaborators.includes(Mycard.id))) if (Mycard.id >= 0 && (Mycard.id == tournament.this?.creator || tournament.this?.collaborators.includes(Mycard.id)))
f(...para); f(...para);
...@@ -351,9 +412,19 @@ ...@@ -351,9 +412,19 @@
}, },
upload : async () : Promise<void> => { upload : async () : Promise<void> => {
const f = async (res : UniApp.ChooseFileSuccessCallbackResult) : Promise<void> => { const f = async (res : UniApp.ChooseFileSuccessCallbackResult) : Promise<void> => {
let del_list : Array<Participant> = [];
// @ts-ignore // @ts-ignore
if (await Tabulator.Tournament.UpdateYdk(Mycard.token, tournament.this.id, res)) res.tempFiles.forEach(i => {
await participant.search(); const p = participant.array.filter(p => p.name == i.name.replace(/\.[^/.]+$/, ""));
del_list.push(...p);
});
// @ts-ignore
if (await Tabulator.Tournament.UpdateYdk(Mycard.token, tournament.this.id, res)) {
for (const i of del_list)
await participant.del(i);
await tournament.search();
}
}; };
await UniApp.selectFile(['.ydk', '.txt'], f); await UniApp.selectFile(['.ydk', '.txt'], f);
} }
...@@ -363,9 +434,8 @@ ...@@ -363,9 +434,8 @@
array : [] as Array<Match>, array : [] as Array<Match>,
total : 0, total : 0,
page : 1, page : 1,
round : 1, round : 0,
maxRound : 2, maxRound : 2,
chk : false,
status : { status : {
color : new Map([ color : new Map([
['Running', 'rgb(84, 200, 17)'], ['Running', 'rgb(84, 200, 17)'],
...@@ -375,19 +445,20 @@ ...@@ -375,19 +445,20 @@
}, },
submit : { submit : {
page : undefined as Match | undefined, page : undefined as Match | undefined,
show : (v : number) : void => { show : (i : Match) : void => {
match.submit.page = match.submit.page == match.array[v] ? undefined : match.array[v]; match.submit.page = match.submit.page === i ? undefined : i;
if (!match.submit.page) if (!match.submit.page)
match.submit.chk = match.array.map(i => [i.player1Score ?? 0, i.player2Score ?? 0]); match.submit.chk = match.array.map(i => [i.player1Score ?? 0, i.player2Score ?? 0]);
}, },
on : async (v : number) : Promise<void> => { on : async (i : Match) : Promise<void> => {
switch (match.array[v].status) { const v = match.array.findIndex(m => m === i);
switch (i.status) {
case 'Running': case 'Running':
const player1 : number = match.submit.chk[v][0] ?? 0; const player1 : number = match.submit.chk[v][0] ?? 0;
const player2 : number = match.submit.chk[v][1] ?? 0; const player2 : number = match.submit.chk[v][1] ?? 0;
const id : null | number = player1 > player2 ? match.array[v].player1Id : player1 == player2 ? null : match.array[v].player2Id const id : null | number = player1 > player2 ? i.player1Id : player1 == player2 ? null : i.player2Id
// @ts-ignore // @ts-ignore
if (await Tabulator.Match.Update(Mycard.token, match.array[v].id, { if (await Tabulator.Match.Update(Mycard.token, i.id, {
player1Score : player1, player1Score : player1,
player2Score : player2, player2Score : player2,
winnerId : id winnerId : id
...@@ -396,7 +467,7 @@ ...@@ -396,7 +467,7 @@
break; break;
case 'Finished': case 'Finished':
// @ts-ignore // @ts-ignore
if (await Tabulator.Match.Update(Mycard.token, match.array[v].id, { if (await Tabulator.Match.Update(Mycard.token, i.id, {
player1Score : 0, player1Score : 0,
player2Score : 0, player2Score : 0,
winnerId : undefined winnerId : undefined
...@@ -406,12 +477,6 @@ ...@@ -406,12 +477,6 @@
} }
}, },
chk : [] as Array<Array<number>> chk : [] as Array<Array<number>>
},
search : async (chk : boolean = match.chk) : Promise<void> => {
// @ts-ignore
const matchs = await Tabulator.Match.FindALL(Mycard.token, {tournamentId : tournament.this.id, statusIn : 'Running,Finished', pageCount : match.page, round : chk ? undefined : match.round});
match.array = matchs.matchs;
match.total = matchs.total;
} }
}); });
...@@ -424,45 +489,29 @@ ...@@ -424,45 +489,29 @@
// @ts-ignore // @ts-ignore
if (await Tabulator.Participant.Create(Mycard.token, { name : participant.name, tournamentId : tournament.this.id}, participant.array)) { if (await Tabulator.Participant.Create(Mycard.token, { name : participant.name, tournamentId : tournament.this.id}, participant.array)) {
participant.name = ''; participant.name = '';
await participant.search(); await tournament.search();
} }
}, },
del : async(v : number) : Promise<void> => { del : async(i : Participant) : Promise<void> => {
const del = async() : Promise<boolean> => { const del = async() : Promise<boolean> => {
// @ts-ignore // @ts-ignore
return await Tabulator.Participant.Delete(Mycard.token, participant.array[v].id); return await Tabulator.Participant.Delete(Mycard.token, i.id);
} }
const update = async() : Promise<boolean> => { const update = async() : Promise<boolean> => {
return await Tabulator.Participant.Update(Mycard.token, participant.array[v].id, { return await Tabulator.Participant.Update(Mycard.token, i.id, {
name : participant.array[v].name, name : i.name,
quit : true quit : true
}); });
} }
// @ts-ignore // @ts-ignore
if (tournament.this.status == 'Ready' ? await del() : await update()) if (tournament.this.status == 'Ready' ? await del() : await update())
await participant.search(); await tournament.search();
}, },
update : async (Data : TournamentCreateObject) : Promise<void> => { update : async (Data : TournamentCreateObject) : Promise<void> => {
// @ts-ignore // @ts-ignore
if (await Tabulator.Tournament.Update(Mycard.token, tournament.this.id, Data)) if (await Tabulator.Tournament.Update(Mycard.token, tournament.this.id, Data))
page.reload(); page.reload();
}, },
search : async () : Promise<boolean> => {
const url = window.location.pathname.match(/\/tournament\/([^\/]+)(?=\/|$)/);
// @ts-ignore
const id = url[1];
// @ts-ignore
const t : TournamentAParticipant = await Tabulator.Tournament.Find(Mycard.token, id);
if (t.tournament) {
tournament.this = t.tournament;
emitter.emit(Const.tournamentReload, tournament.this)
const participants = t.participant;
participant.array = participants.participants;
participant.total = participants.total;
return true;
}
return false;
},
upload : async (i : Participant) : Promise<void> => { upload : async (i : Participant) : Promise<void> => {
const f = async (res : UniApp.ChooseFileSuccessCallbackResult) : Promise<void> => { const f = async (res : UniApp.ChooseFileSuccessCallbackResult) : Promise<void> => {
if (res.tempFiles[0].size < Const.maxSize) { if (res.tempFiles[0].size < Const.maxSize) {
...@@ -471,7 +520,7 @@ ...@@ -471,7 +520,7 @@
name : i.name, name : i.name,
deckbuf : i.deckbuf deckbuf : i.deckbuf
} as ParticipantUpdateObject)) } as ParticipantUpdateObject))
await participant.search(); await tournament.search();
} }
}; };
await UniApp.selectFile(['.ydk', '.txt'], f, 1); await UniApp.selectFile(['.ydk', '.txt'], f, 1);
...@@ -482,24 +531,65 @@ ...@@ -482,24 +531,65 @@
participant : i, participant : i,
main : i.getDeck().main, main : i.getDeck().main,
side : i.getDeck().side, side : i.getDeck().side,
blob : i.Blob()
}) })
} }
},
move : {
this : undefined as Participant | undefined,
start : (i : Participant, e : TouchEvent) : void => {
const on = () : void => {
participant.move.this = i;
};
const end = async () : Promise<void> => {
if (participant.move.this === i) {
participant.move.this = undefined;
return;
}
const v1 = participant.array.findIndex(p => { return p === i; });
const v2 = participant.array.findIndex(p => { return p === participant.move.this; });
participant.move.this = undefined;
if (v1 - v2 != 1) {
// @ts-ignore
if (await participant.move.change(participant.array[v1].id, v2 >= 1 ? participant.array[v2 - 1].id : 0) && await participant.move.change(participant.array[v2].id, v1 >= 1 ? participant.array[v1 - 1].id : 0))
await tournament.search();
} else {
const v = v1 > v2 ? v1 : v2;
await participant.move.down(participant.array[v - 1].id, participant.array[v].id);
}
};
participant.move.this ? end() : on();
},
change : async (from : number, to : number) : Promise<void> => {
// @ts-ignore
return await Tabulator.Tournament.Drag(Mycard.token, tournament.this?.id, from, to);
},
down : async (from : number, to : number) : Promise<void> => {
// @ts-ignore
if (await Tabulator.Tournament.Drag(Mycard.token, tournament.this?.id, from, to))
await tournament.search();
}
} }
}); });
let page = reactive({ let page = reactive({
height : 0, height : 0,
loading : false, loading : false,
clear : async () : Promise<void>=> { clear : (chk = false) : void => {
tournament.this = undefined; const i = window.location.href.split('/?');
await (new Promise(resolve => setTimeout(resolve, 450))); if (chk)
window.location.replace(window.location.href.replace(window.location.pathname, '')) window.location.replace(`${window.location.origin}/#${i[1] ? `/?${i[1]}` : '/'}`);
else
emitter.emit(Const.changeUrl, `${window.location.origin}/#${i[1] ? `/?${i[1]}` : '/'}`);
},
show : () : void => {
emitter.emit(Const.show);
}, },
get : async () : Promise<void> => { get : async () : Promise<void> => {
if (await participant.search()) if (await tournament.search())
await match.search(); page.show();
else else
page.clear(); page.clear(true);
}, },
reload : async () : Promise<void> => { reload : async () : Promise<void> => {
const query = uni.createSelectorQuery().in(this); const query = uni.createSelectorQuery().in(this);
...@@ -509,8 +599,8 @@ ...@@ -509,8 +599,8 @@
}).exec(); }).exec();
page.loading = true; page.loading = true;
participant.name = ''; participant.name = '';
if (await participant.search()) { if (await tournament.search()) {
await match.search(); match.round = 0;
await (new Promise(resolve => setTimeout(resolve, 500))); await (new Promise(resolve => setTimeout(resolve, 500)));
page.loading = false; page.loading = false;
page.height = 0; page.height = 0;
...@@ -533,35 +623,32 @@ ...@@ -533,35 +623,32 @@
match.submit.chk = match.array.map(i => [i.player1Score ?? 0, i.player2Score ?? 0]); match.submit.chk = match.array.map(i => [i.player1Score ?? 0, i.player2Score ?? 0]);
}, },
copyUrl : () => { copyUrl : () => {
uni.setClipboardData({ UniApp.copy(`${window.location.href.split('/?')[0]}`);
data : `${window.location.href.split('/?')[0]}`,
success : () => {
uni.showToast({
title : '复制成功'
})
}
})
} }
}); });
onBeforeMount(() => { const loading = () : void => {
const url = window.location.pathname.match(/\/tournament\/([^\/]+)(?=\/|$)/); const url = window.location.hash.match(/#\/(.*?)(?:\?|$)/);
url && !isNaN(parseInt(url[1])) ? page.get() : page.clear(); url && !isNaN(parseInt(url[1])) ? page.get() : page.clear();
};
onBeforeMount(() => {
loading();
document.addEventListener("click", page.clickClear); document.addEventListener("click", page.clickClear);
// @ts-ignore // @ts-ignore
emitter.on(Const.updateTournament, participant.update); emitter.on(Const.updateTournament, participant.update);
emitter.on(Const.showTournament, loading);
}); });
onUnmounted(() => { onUnmounted(() => {
document.removeEventListener("click", page.clickClear); document.removeEventListener("click", page.clickClear);
// @ts-ignore // @ts-ignore
emitter.off(Const.updateTournament, participant.update); emitter.off(Const.updateTournament, participant.update);
emitter.off(Const.showTournament, loading);
}); });
watch(() => { return match.round; }, async () : Promise<void> => { watch(() => { return match.round; }, () : void => {
match.page = 1; match.page = 1;
match.chk = false;
await match.search();
}); });
watch(() => { return match.array; }, async () : Promise<void> => { watch(() => { return match.array; }, async () : Promise<void> => {
......
...@@ -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