This repository has been archived by the owner on Apr 4, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(*): optimize code and add tetr.io personal account bind and hist…
…ory query
- Loading branch information
1 parent
4fbfe0e
commit 6c65f22
Showing
12 changed files
with
455 additions
and
115 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
<script lang="ts" setup> | ||
import { useMessage } from 'naive-ui' | ||
import { isEmpty } from 'remeda/dist/es' | ||
import type { Database } from '~/types/supabase' | ||
const props = defineProps<{ | ||
readonly id?: string | ||
readonly name?: string | ||
}>() | ||
const name = ref<string>(props.name ?? '') | ||
const waiting = ref(false) | ||
const available = computed(() => { | ||
return isEmpty(props.name) && !isEmpty(name.value) && !waiting.value | ||
}) | ||
const supabase = useSupabaseClient<Database>() | ||
const user = useSupabaseUser() | ||
const $message = useMessage() | ||
const save = async () => { | ||
waiting.value = true | ||
if (user.value === null) { | ||
$message.error('请先登录') | ||
return | ||
} | ||
const already_used = await supabase.from('tetrio_bindings') | ||
.select() | ||
.eq('tetrio_name', name.value) | ||
.then(result => { | ||
if (result.error !== null) { | ||
$message.error('发生了一个错误, 请到控制台查看') | ||
console.error(result.error) | ||
} | ||
if (result.data === null) { | ||
return false | ||
} | ||
return result.data.length > 0 | ||
}) | ||
if (already_used) { | ||
$message.error('这个用户名已经被其他人绑定了') | ||
return | ||
} | ||
const result = await supabase.functions.invoke('tetrio_profile', { | ||
method: 'POST', | ||
body: JSON.stringify({ | ||
name: name.value | ||
}) | ||
}) | ||
if (result.error !== null) { | ||
$message.error('发生了一个错误, 请到控制台查看') | ||
console.error(result.error) | ||
} | ||
if (result.data !== null && 'data' in result.data) { | ||
await supabase.from('tetrio_bindings') | ||
.upsert({ | ||
id: user.value.id, | ||
tetrio_id: result.data.data.id, | ||
tetrio_name: name.value | ||
}) | ||
.then(result => { | ||
if (result.error !== null) { | ||
$message.error('发生了一个错误, 请到控制台查看') | ||
console.error(result.error) | ||
} | ||
}) | ||
} | ||
waiting.value = false | ||
} | ||
const bound = computed(() => { | ||
return !isEmpty(props.id) && !isEmpty(props.name) | ||
}) | ||
</script> | ||
|
||
<template> | ||
<n-card class="sm:w-1/2" size="small" title="账号绑定"> | ||
<template v-if="!bound"> | ||
<n-flex :size="0" class="leading-tight" vertical> | ||
<n-input v-model:value="name" :disabled="props.name !== undefined" placeholder="tetr.io 用户名"/> | ||
<n-text :depth="3" class="text-sm">{{ id }}</n-text> | ||
</n-flex> | ||
|
||
</template> | ||
|
||
<template v-else> | ||
<n-flex :size="0" class="text-center leading-tight" vertical> | ||
<n-text class="text-2xl fw-bold">{{ name }}</n-text> | ||
<n-text :depth="3" class="text-xl fw-bold">{{ id }}</n-text> | ||
</n-flex> | ||
</template> | ||
|
||
<template #action> | ||
<n-flex justify="end"> | ||
<template v-if="!bound"> | ||
<n-button :disabled="!available" :loading="waiting" @click="save">保存</n-button> | ||
</template> | ||
|
||
<template v-else> | ||
<n-flex :size="5" justify="end"> | ||
<n-text>如需更改绑定请联系</n-text> | ||
<n-button href="https://zhazha120.cn" tag="a" text type="primary">渣渣120</n-button> | ||
</n-flex> | ||
</template> | ||
</n-flex> | ||
</template> | ||
</n-card> | ||
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
<script lang="ts" setup> | ||
import { asyncComputed } from '@vueuse/core' | ||
import { LineChart, type LineSeriesOption } from 'echarts/charts' | ||
import { | ||
GridComponent, | ||
type GridComponentOption, | ||
LegendComponent, | ||
type LegendComponentOption, | ||
TitleComponent, | ||
type TitleComponentOption, | ||
ToolboxComponent, | ||
type ToolboxComponentOption, | ||
TooltipComponent, | ||
type TooltipComponentOption | ||
} from 'echarts/components' | ||
import { type ComposeOption, use } from 'echarts/core' | ||
import { CanvasRenderer } from 'echarts/renderers' | ||
import { useOsTheme } from 'naive-ui' | ||
import { map, pipe, prop, uniq } from 'remeda' | ||
import VChart, { THEME_KEY } from 'vue-echarts' | ||
import type { Database } from '~/types/supabase' | ||
const props = defineProps<{ | ||
readonly name: string | ||
readonly records: Database['public']['Tables']['tetrio_players']['Row'][] | ||
readonly type: 'tr' | 'apm' | 'pps' | 'vs' | ||
readonly title: string | ||
}>() | ||
use([ | ||
TitleComponent, | ||
TooltipComponent, | ||
LegendComponent, | ||
ToolboxComponent, | ||
GridComponent, | ||
LineChart, | ||
CanvasRenderer | ||
]) | ||
if (useOsTheme().value === 'dark') { | ||
provide(THEME_KEY, 'dark') | ||
} | ||
const chart = asyncComputed(async () => { | ||
return { | ||
title: { | ||
text: props.title | ||
}, | ||
tooltip: { | ||
trigger: 'axis' | ||
}, | ||
legend: { | ||
data: pipe( | ||
props.records, | ||
map(prop('name')), | ||
uniq() | ||
) | ||
}, | ||
toolbox: { | ||
feature: { | ||
saveAsImage: {} | ||
} | ||
}, | ||
xAxis: { | ||
type: 'category', | ||
data: pipe( | ||
props.records, | ||
map(record => { | ||
const date = new Date(record.record_at) | ||
date.setMinutes(0) | ||
date.setSeconds(0) | ||
return date.toLocaleString() | ||
}), | ||
uniq() | ||
) | ||
}, | ||
yAxis: { | ||
type: 'value', | ||
min: 'dataMin', | ||
max: 'dataMax' | ||
}, | ||
series: { | ||
type: 'line', | ||
name: props.name, | ||
data: map(props.records, record => { | ||
return record[props.type].toFixed(2) | ||
}), | ||
endLabel: { | ||
show: true | ||
} | ||
} as LineSeriesOption | ||
} as ComposeOption<TitleComponentOption | TooltipComponentOption | LegendComponentOption | ToolboxComponentOption | GridComponentOption | LineSeriesOption> | ||
}) | ||
</script> | ||
|
||
<template> | ||
<div class="overflow-auto"> | ||
<div class="mx-auto w-300 h-250"> | ||
<v-chart :option="chart"/> | ||
</div> | ||
</div> | ||
</template> |
Oops, something went wrong.