🎈 perf: 改进路由
This commit is contained in:
parent
4809a1eb85
commit
85300fa2d2
@ -5,5 +5,5 @@
|
||||
"singleQuote": true,
|
||||
"printWidth": 100,
|
||||
"htmlWhitespaceSensitivity": "ignore",
|
||||
"trailingComma": "none"
|
||||
"trailingComma": "all"
|
||||
}
|
||||
|
150
src/App.vue
150
src/App.vue
@ -6,10 +6,10 @@
|
||||
<div class="header-title" @click="navigate()">社团展示系统</div>
|
||||
</router-link>
|
||||
<transition name="header-content-container">
|
||||
<template v-if="arrayIncludes(['initialized', 'failed'], userStore.userInfoStatus)">
|
||||
<template v-if="!userStore.isInitializing">
|
||||
<nav class="header-content-container">
|
||||
<el-menu
|
||||
:default-active="route.fullPath"
|
||||
:default-active="$route.fullPath"
|
||||
:ellipsis="false"
|
||||
:router="true"
|
||||
mode="horizontal"
|
||||
@ -64,28 +64,6 @@
|
||||
</transition>
|
||||
</router-view>
|
||||
<transition name="page-root"></transition>
|
||||
<!-- <div
|
||||
:style="
|
||||
pageStore.pageLoadingCount
|
||||
? {
|
||||
opacity: 1,
|
||||
transition: 'opacity 0.75s 0.15s',
|
||||
zIndex: 99
|
||||
}
|
||||
: {
|
||||
opacity: 0,
|
||||
transition: 'opacity 0.3s',
|
||||
zIndex: -1
|
||||
}
|
||||
"
|
||||
class="page-loading-mask"
|
||||
>
|
||||
<div class="page-loading-icon-container">
|
||||
<el-icon class="loading-icon is-loading" color="white" size="60">
|
||||
<icon-cs-loading />
|
||||
</el-icon>
|
||||
</div>
|
||||
</div> -->
|
||||
</el-main>
|
||||
</el-container>
|
||||
<el-dialog
|
||||
@ -363,27 +341,28 @@ import { type VerifyImagePath } from '@/components/VerifyInput.vue';
|
||||
import { loginResponseSchema, registerResponseSchema } from '@/schemas';
|
||||
import { useUserStore } from '@/stores';
|
||||
import { usePageStore } from '@/stores/page';
|
||||
import { arrayIncludes, errorMessage } from '@/utils';
|
||||
import { errorMessage } from '@/utils';
|
||||
import { Avatar, CloseBold } from '@element-plus/icons-vue';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ElMessage, type FormInstance, type FormRules } from 'element-plus';
|
||||
import { partial } from 'lodash-es';
|
||||
import { reactive, ref, watch } from 'vue';
|
||||
import { RouterView, useRoute, useRouter } from 'vue-router';
|
||||
import { RouterView, useRoute } from 'vue-router';
|
||||
import router from './router';
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const userStore = useUserStore();
|
||||
const pageStore = usePageStore();
|
||||
const showLoginRegisterDialog = ref(false);
|
||||
watch(
|
||||
() => userStore.userInfoStatus,
|
||||
() => userStore.isInitializing,
|
||||
(value) => {
|
||||
if (value === 'initialized' && userStore.token === null) {
|
||||
console.log(1);
|
||||
if (!value && userStore.token === null) {
|
||||
showLoginRegisterDialog.value = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
const showLoginRegisterDialog = ref(false);
|
||||
watch(showLoginRegisterDialog, (value) => {
|
||||
if (value) {
|
||||
logining.value = false;
|
||||
@ -400,21 +379,21 @@ let loginFormData = reactive({
|
||||
username: 'wubaopu2',
|
||||
password: '123456',
|
||||
verifyImage: 'none' as VerifyImagePath,
|
||||
verifyCode: ''
|
||||
verifyCode: '',
|
||||
});
|
||||
const loginFormRules = reactive<FormRules<typeof loginFormData>>({
|
||||
username: [
|
||||
{ required: true, message: '请输入用户名' },
|
||||
{ min: 6, message: '用户名长度不能小于3位' }
|
||||
{ min: 6, message: '用户名长度不能小于6位' },
|
||||
],
|
||||
password: [
|
||||
{ required: true, message: '请输入密码' },
|
||||
{ min: 6, message: '密码长度不能小于6位' }
|
||||
{ min: 6, message: '密码长度不能小于6位' },
|
||||
],
|
||||
verifyCode: [
|
||||
{ required: true, message: '请输入验证码' },
|
||||
{ pattern: /[0-9A-Za-z]{4}/, message: '验证码不符合格式' }
|
||||
]
|
||||
{ pattern: /[0-9A-Za-z]{4}/, message: '验证码不符合格式' },
|
||||
],
|
||||
});
|
||||
const logining = ref(false);
|
||||
const registerFormRef = ref<FormInstance>();
|
||||
@ -423,7 +402,7 @@ let registerFormData = reactive({
|
||||
password: '',
|
||||
confirmPassword: '',
|
||||
verifyImage: 'none' as VerifyImagePath,
|
||||
verifyCode: ''
|
||||
verifyCode: '',
|
||||
});
|
||||
const registerFormRules = reactive<FormRules<typeof registerFormData>>({
|
||||
...loginFormRules,
|
||||
@ -439,9 +418,9 @@ const registerFormRules = reactive<FormRules<typeof registerFormData>>({
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
const registering = ref(false);
|
||||
const loginErrorMessage = partial(errorMessage, '登录');
|
||||
@ -452,6 +431,12 @@ interface LoginParams {
|
||||
key: string;
|
||||
code: string;
|
||||
}
|
||||
watch(
|
||||
() => userStore.userInfo,
|
||||
() => {
|
||||
router.push({ path: router.currentRoute.value.fullPath, force: true });
|
||||
},
|
||||
);
|
||||
|
||||
async function login(params: LoginParams): Promise<boolean> {
|
||||
const loginRespRaw = await axiosInstance
|
||||
@ -466,12 +451,10 @@ async function login(params: LoginParams): Promise<boolean> {
|
||||
ElMessage.error(loginErrorMessage(loginResp.code, loginResp.msg));
|
||||
return false;
|
||||
}
|
||||
await userStore.updateSelfUserInfo(true);
|
||||
return true;
|
||||
return userStore.updateSelfUserInfo(true);
|
||||
}
|
||||
|
||||
async function submitLoginForm() {
|
||||
let succeed = false;
|
||||
logining.value = true;
|
||||
try {
|
||||
try {
|
||||
@ -487,22 +470,22 @@ async function submitLoginForm() {
|
||||
username,
|
||||
password,
|
||||
verifyImage: { key },
|
||||
verifyCode: code
|
||||
verifyCode: code,
|
||||
} = loginFormData;
|
||||
succeed = await login({
|
||||
username,
|
||||
password,
|
||||
key,
|
||||
code
|
||||
});
|
||||
if (
|
||||
!(await login({
|
||||
username,
|
||||
password,
|
||||
key,
|
||||
code,
|
||||
}))
|
||||
)
|
||||
return;
|
||||
showLoginRegisterDialog.value = false;
|
||||
} finally {
|
||||
if (succeed) {
|
||||
showLoginRegisterDialog.value = false;
|
||||
} else {
|
||||
loginFormData.verifyImage = 'none';
|
||||
loginFormRef.value?.resetFields('verifyCode');
|
||||
logining.value = false;
|
||||
}
|
||||
loginFormData.verifyImage = 'none';
|
||||
loginFormRef.value?.resetFields('verifyCode');
|
||||
logining.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -512,30 +495,25 @@ interface RegisterParams extends LoginParams {
|
||||
auth: number;
|
||||
}
|
||||
|
||||
async function register(params: RegisterParams) {
|
||||
try {
|
||||
const registerResp = registerResponseSchema.parse(
|
||||
(await axiosInstance.put('/api/user/create', params)).data
|
||||
);
|
||||
if (registerResp.type === 'error') {
|
||||
ElMessage.error({
|
||||
message: registerErrorMessage(registerResp.code, registerResp.msg)
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} catch (e) {
|
||||
if (e instanceof AxiosError) {
|
||||
ElMessage.error(registerErrorMessage(e.code, e.message));
|
||||
} else {
|
||||
ElMessage.error('error');
|
||||
}
|
||||
async function register(params: RegisterParams): Promise<boolean> {
|
||||
const raw = await axiosInstance
|
||||
.put<RawResp>('/api/user/create', params)
|
||||
.then((r) => r.data)
|
||||
.catch((err: AxiosError) => {
|
||||
ElMessage.error(registerErrorMessage(err.code, err.message));
|
||||
});
|
||||
if (!raw) return false;
|
||||
const resp = registerResponseSchema.parse(raw);
|
||||
if (resp.type === 'error') {
|
||||
ElMessage.error({
|
||||
message: registerErrorMessage(resp.code, resp.msg),
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return userStore.updateSelfUserInfo(true);
|
||||
}
|
||||
|
||||
async function submitRegisterForm() {
|
||||
let succeed = false;
|
||||
registering.value = true;
|
||||
try {
|
||||
try {
|
||||
@ -551,23 +529,19 @@ async function submitRegisterForm() {
|
||||
username,
|
||||
password,
|
||||
verifyImage: { key },
|
||||
verifyCode: code
|
||||
verifyCode: code,
|
||||
} = registerFormData;
|
||||
succeed = await register({ username, password, key, code, auth: 1 });
|
||||
if (!(await register({ username, password, key, code, auth: 1 }))) return;
|
||||
showLoginRegisterDialog.value = false;
|
||||
} finally {
|
||||
if (succeed) {
|
||||
userStore.userInfoStatus = 'uninitialized';
|
||||
await router.push({ path: route.fullPath, force: true });
|
||||
} else {
|
||||
registerFormData.verifyImage = 'none';
|
||||
registerFormRef.value?.resetFields('verifyCode');
|
||||
registering.value = false;
|
||||
}
|
||||
registerFormData.verifyImage = 'none';
|
||||
registerFormRef.value?.resetFields('verifyCode');
|
||||
registering.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function logout() {
|
||||
userStore.$reset();
|
||||
userStore.token = null;
|
||||
await userStore.updateSelfUserInfo(true);
|
||||
}
|
||||
</script>
|
||||
|
@ -3,7 +3,7 @@ import axios from 'axios';
|
||||
|
||||
const baseURL = 'https://wzpmc.cn:18080/';
|
||||
const axiosInstance = axios.create({
|
||||
baseURL
|
||||
baseURL,
|
||||
});
|
||||
// 自动添加token到请求中.
|
||||
axiosInstance.interceptors.request.use((config) => {
|
||||
@ -16,6 +16,7 @@ axiosInstance.interceptors.response.use((response) => {
|
||||
const userStore = useUserStore();
|
||||
const authorization = response.headers['set-authorization'] as string | undefined;
|
||||
if (authorization) {
|
||||
console.log(123);
|
||||
userStore.token = authorization;
|
||||
}
|
||||
return response;
|
||||
|
@ -41,7 +41,7 @@ import {
|
||||
type BackgroundTask,
|
||||
type BackgroundURL,
|
||||
CancelledError,
|
||||
useBackgroundStore
|
||||
useBackgroundStore,
|
||||
} from '@/stores';
|
||||
import 'element-plus/theme-chalk/index.css';
|
||||
import { onMounted, onUnmounted, ref } from 'vue';
|
||||
|
@ -23,8 +23,8 @@
|
||||
:style="[
|
||||
getTilePosition(y - 1, x - 1),
|
||||
{
|
||||
animationDelay: `${0.1 * (x + y - 2)}s`
|
||||
}
|
||||
animationDelay: `${0.1 * (x + y - 2)}s`,
|
||||
},
|
||||
]"
|
||||
class="background-tile"
|
||||
></div>
|
||||
@ -41,8 +41,8 @@
|
||||
'tile',
|
||||
{
|
||||
'tile-appear-from-others': tile.fromOthers,
|
||||
'tile-transition-cancelled': transitionCancelled
|
||||
}
|
||||
'tile-transition-cancelled': transitionCancelled,
|
||||
},
|
||||
]"
|
||||
:data-tile-id="tile.id"
|
||||
:style="[getTilePosition(y, x), getNumberTileStyle(tile)]"
|
||||
@ -204,7 +204,7 @@ enum Directions {
|
||||
UP = 'ArrowUp',
|
||||
DOWN = 'ArrowDown',
|
||||
LEFT = 'ArrowLeft',
|
||||
RIGHT = 'ArrowRight'
|
||||
RIGHT = 'ArrowRight',
|
||||
}
|
||||
|
||||
type SingleTileLine = (Tile | undefined)[];
|
||||
@ -265,7 +265,7 @@ const borderWidth = 15;
|
||||
const { width, height } = storeToRefs(game2048Store);
|
||||
const containerSizeStyle = computed(() => ({
|
||||
minWidth: `${width.value * tileWidth + (width.value + 1) * borderWidth}px`,
|
||||
minHeight: `${height.value * tileWidth + (height.value + 1) * borderWidth}px`
|
||||
minHeight: `${height.value * tileWidth + (height.value + 1) * borderWidth}px`,
|
||||
}));
|
||||
/**
|
||||
* 是否显示容器.
|
||||
@ -288,7 +288,7 @@ watch(gameStatus, (status) => {
|
||||
message: `单个块分数达到${game2048Store.successNumber}`,
|
||||
type: 'success',
|
||||
duration: 3500,
|
||||
offset: 50
|
||||
offset: 50,
|
||||
});
|
||||
break;
|
||||
case 'failed':
|
||||
@ -297,7 +297,7 @@ watch(gameStatus, (status) => {
|
||||
message: '你无路可走',
|
||||
type: 'error',
|
||||
duration: 3500,
|
||||
offset: 50
|
||||
offset: 50,
|
||||
});
|
||||
break;
|
||||
}
|
||||
@ -327,7 +327,7 @@ const tileBackgroundColorMapping: Record<number, string | undefined> = {
|
||||
256: 'rgb(237, 204, 97)',
|
||||
512: 'rgb(237, 200, 80)',
|
||||
1024: 'rgb(237, 197, 63)',
|
||||
2048: 'rgb(237, 194, 46)'
|
||||
2048: 'rgb(237, 194, 46)',
|
||||
};
|
||||
|
||||
/**
|
||||
@ -335,7 +335,7 @@ const tileBackgroundColorMapping: Record<number, string | undefined> = {
|
||||
*/
|
||||
function storeTileGrid() {
|
||||
game2048Store.rawTileGrid = tileGrid.map((row) =>
|
||||
row.map((tiles) => tiles.map((tile) => pick(tile, 'number', 'removed')))
|
||||
row.map((tiles) => tiles.map((tile) => pick(tile, 'number', 'removed'))),
|
||||
);
|
||||
}
|
||||
|
||||
@ -345,7 +345,7 @@ function storeTileGrid() {
|
||||
function getTilePosition(y: number, x: number) {
|
||||
return {
|
||||
left: `${borderWidth + x * (tileWidth + borderWidth)}px`,
|
||||
top: `${borderWidth + y * (tileWidth + borderWidth)}px`
|
||||
top: `${borderWidth + y * (tileWidth + borderWidth)}px`,
|
||||
};
|
||||
}
|
||||
|
||||
@ -357,7 +357,7 @@ function getNumberTileStyle(tile: Tile) {
|
||||
return {
|
||||
fontSize: `${number < 128 ? 55 : number < 1024 ? 45 : 35}px`,
|
||||
color: number < 8 ? 'rgb(119, 110, 101)' : 'white',
|
||||
backgroundColor: tileBackgroundColorMapping[number] ?? 'rgb(56, 56, 56)'
|
||||
backgroundColor: tileBackgroundColorMapping[number] ?? 'rgb(56, 56, 56)',
|
||||
};
|
||||
}
|
||||
|
||||
@ -386,7 +386,7 @@ function addRandomTiles(randomCount: number, randomNumbers: number[] = [2, 4]) {
|
||||
id: maxId++,
|
||||
number: sample(randomNumbers) ?? 2,
|
||||
removed: false,
|
||||
fromOthers: false
|
||||
fromOthers: false,
|
||||
};
|
||||
tiles.push(tile);
|
||||
tileGrid[y][x].push(tile);
|
||||
@ -394,7 +394,7 @@ function addRandomTiles(randomCount: number, randomNumbers: number[] = [2, 4]) {
|
||||
}
|
||||
return {
|
||||
tiles,
|
||||
promise: Promise.all(tileAddPromises).then<void>(() => undefined)
|
||||
promise: Promise.all(tileAddPromises).then<void>(() => undefined),
|
||||
};
|
||||
}
|
||||
|
||||
@ -433,13 +433,13 @@ function mergeLineImpl(tileRelationMap: TileRelationMap, line: SingleTileLine, o
|
||||
id: maxId++,
|
||||
number: tile.number * 2,
|
||||
removed: false,
|
||||
fromOthers: true
|
||||
fromOthers: true,
|
||||
};
|
||||
tileRelationMap.set(
|
||||
newTile,
|
||||
(tileRelationMap.get(prevTile) ?? [prevTile]).concat(
|
||||
tileRelationMap.get(tile) ?? [tile]
|
||||
)
|
||||
tileRelationMap.get(tile) ?? [tile],
|
||||
),
|
||||
);
|
||||
prev = once ? undefined : [i, newTile];
|
||||
line[i] = newTile;
|
||||
@ -493,9 +493,9 @@ function mergeLine(line: SingleTileLine): [lineMergeREsult: LineMergeResult, cha
|
||||
originalLine,
|
||||
transitionLine,
|
||||
resultLine: line.map((tile) => (tile !== undefined ? [tile] : [])),
|
||||
tileRelationMap
|
||||
tileRelationMap,
|
||||
},
|
||||
changed
|
||||
changed,
|
||||
];
|
||||
}
|
||||
|
||||
@ -506,14 +506,14 @@ function mergeLine(line: SingleTileLine): [lineMergeREsult: LineMergeResult, cha
|
||||
*/
|
||||
async function mergeTiles(direction: Directions) {
|
||||
function getMergedGrid(
|
||||
d: Directions = direction
|
||||
d: Directions = direction,
|
||||
): [MergedGrid, changed: boolean, maxNumber: number] {
|
||||
let changed = false;
|
||||
let maxNumber = 0;
|
||||
const mergedGrid: MergedGrid = Array(firstIndices.length).fill(undefined);
|
||||
for (const first of firstIndices(d)) {
|
||||
const line = secondIndices(d).map((second) =>
|
||||
get2DArrayItem(tileGrid, first, second, isColFirst(d)).at(0)
|
||||
get2DArrayItem(tileGrid, first, second, isColFirst(d)).at(0),
|
||||
);
|
||||
|
||||
const [lineMergeResult, lineChanged] = mergeLine(line);
|
||||
@ -547,8 +547,8 @@ async function mergeTiles(direction: Directions) {
|
||||
lineIndex: number;
|
||||
},
|
||||
first: number,
|
||||
second: number
|
||||
) => void
|
||||
second: number,
|
||||
) => void,
|
||||
) {
|
||||
forEachMergedLine((lineMergeResult, first) => {
|
||||
for (const [lineIndex, second] of secondIndices().entries()) {
|
||||
@ -557,10 +557,10 @@ async function mergeTiles(direction: Directions) {
|
||||
{
|
||||
tiles,
|
||||
lineIndex,
|
||||
...lineMergeResult
|
||||
...lineMergeResult,
|
||||
},
|
||||
first,
|
||||
second
|
||||
second,
|
||||
);
|
||||
}
|
||||
});
|
||||
@ -583,7 +583,7 @@ async function mergeTiles(direction: Directions) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
})()
|
||||
})(),
|
||||
);
|
||||
}
|
||||
|
||||
@ -613,7 +613,7 @@ async function mergeTiles(direction: Directions) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
})()
|
||||
})(),
|
||||
);
|
||||
}
|
||||
|
||||
@ -632,7 +632,7 @@ async function mergeTiles(direction: Directions) {
|
||||
tileTransitionPromises.push(
|
||||
...transitionTiles
|
||||
.filter((tile) => tile !== originalLine[lineIndex]) // 过滤没有移动位置的过渡块.
|
||||
.map((tile) => tileTransitionFutureMap.add(tile.id))
|
||||
.map((tile) => tileTransitionFutureMap.add(tile.id)),
|
||||
);
|
||||
}
|
||||
tiles.splice(0, tiles.length, ...transitionTiles);
|
||||
@ -644,7 +644,7 @@ async function mergeTiles(direction: Directions) {
|
||||
tileAddPromises.push(
|
||||
...resultTiles
|
||||
.filter((tile) => !originalLine.includes(tile)) // 过滤仅移动位置的结果块.
|
||||
.map((tile) => tileAddFutureMap.add(tile.id))
|
||||
.map((tile) => tileAddFutureMap.add(tile.id)),
|
||||
);
|
||||
tiles.splice(0, tiles.length, ...resultTiles);
|
||||
});
|
||||
@ -738,11 +738,11 @@ onMounted(async () => {
|
||||
return {
|
||||
...rawTile,
|
||||
id,
|
||||
fromOthers: false
|
||||
fromOthers: false,
|
||||
};
|
||||
})
|
||||
)
|
||||
)
|
||||
}),
|
||||
),
|
||||
),
|
||||
);
|
||||
if (game2048Store.isInitial) {
|
||||
const { tiles, promise: newTilePromise } = addRandomTiles(2);
|
||||
@ -765,7 +765,7 @@ onUnmounted(() => {
|
||||
isUnmounted = true;
|
||||
for (const future of chainIterables([
|
||||
tileAddFutureMap.values(),
|
||||
tileTransitionFutureMap.values()
|
||||
tileTransitionFutureMap.values(),
|
||||
])) {
|
||||
future.resolve();
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ watch(
|
||||
() => props.score,
|
||||
(score) => {
|
||||
scoreQueue.push(score);
|
||||
}
|
||||
},
|
||||
);
|
||||
onMounted(async () => {
|
||||
let continuouslyRolling = false;
|
||||
@ -61,7 +61,7 @@ onMounted(async () => {
|
||||
await scoreDiv.value?.animate([{ transform: 'none' }, { transform: 'translateY(-100%)' }], {
|
||||
easing: continuouslyRolling ? 'linear' : 'ease-in',
|
||||
fill: 'forwards',
|
||||
duration: 100 / (scoreQueue.size + 1 + Number(continuouslyRolling))
|
||||
duration: 100 / (scoreQueue.size + 1 + Number(continuouslyRolling)),
|
||||
}).finished;
|
||||
displayedScore.value = score;
|
||||
await nextTick();
|
||||
@ -69,7 +69,7 @@ onMounted(async () => {
|
||||
await scoreDiv.value?.animate([{ transform: 'translateY(100%)' }, { transform: 'none' }], {
|
||||
easing: continuouslyRolling ? 'linear' : 'ease-out',
|
||||
fill: 'forwards',
|
||||
duration: 100 / (scoreQueue.size + 1)
|
||||
duration: 100 / (scoreQueue.size + 1),
|
||||
}).finished;
|
||||
} catch (e) {
|
||||
if (
|
||||
|
@ -6,7 +6,9 @@
|
||||
</el-icon>
|
||||
</template>
|
||||
<template #append>
|
||||
<el-button v-if="model.verifyImage === 'none'" @click="updateVerifyImage">获取验证码</el-button>
|
||||
<el-button v-if="model.verifyImage === 'none'" @click="updateVerifyImage">
|
||||
获取验证码
|
||||
</el-button>
|
||||
<el-icon v-else-if="model.verifyImage === 'fetching'" class="is-loading">
|
||||
<icon-ep-loading />
|
||||
</el-icon>
|
||||
@ -66,7 +68,7 @@ watch(
|
||||
} else {
|
||||
popOverMessage.value = '看不清? 点击换一张';
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
watch(
|
||||
() => model.value.verifyImage,
|
||||
@ -74,7 +76,7 @@ watch(
|
||||
if (value === 'none') {
|
||||
refreshCoolDown.value = 0;
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
async function cooldown() {
|
||||
@ -94,7 +96,7 @@ async function updateVerifyImage() {
|
||||
try {
|
||||
try {
|
||||
const verifyResponse = verifyResponseSchema.parse(
|
||||
(await axiosInstance.get('/api/user/verify')).data
|
||||
(await axiosInstance.get('/api/user/verify')).data,
|
||||
);
|
||||
console.log(verifyResponse);
|
||||
if (verifyResponse.type === 'error') {
|
||||
@ -104,7 +106,7 @@ async function updateVerifyImage() {
|
||||
const { img, key } = verifyResponse.data;
|
||||
model.value.verifyImage = {
|
||||
key: key,
|
||||
img
|
||||
img,
|
||||
};
|
||||
cooldown().then();
|
||||
succeed = true;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useUserStore } from '@/stores';
|
||||
import { type PageErrorReason, usePageStore } from '@/stores/page';
|
||||
import { IdPool } from '@/utils';
|
||||
import { usePageStore } from '@/stores/page';
|
||||
import { PageErrorType, type PageErrorReason } from '@/views/ErrorPage.vue';
|
||||
import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router';
|
||||
import { RoutePermissionId } from './permissions';
|
||||
|
||||
@ -17,98 +17,82 @@ declare module 'vue-router' {
|
||||
const routes: RouteRecordRaw[] = [
|
||||
{
|
||||
path: '/',
|
||||
component: () => import('@/views/MainPage.vue')
|
||||
},
|
||||
{
|
||||
path: '/user',
|
||||
component: () => import('@/views/UserPage.vue'),
|
||||
meta: {
|
||||
permissionId: RoutePermissionId.USER_PAGE
|
||||
}
|
||||
component: () => import('@/views/MainPage.vue'),
|
||||
},
|
||||
{
|
||||
path: '/user/:id',
|
||||
component: () => import('@/views/UserPage.vue')
|
||||
component: () => import('@/views/UserPage.vue'),
|
||||
},
|
||||
{
|
||||
path: '/club',
|
||||
component: () => import('@/views/ClubPage.vue'),
|
||||
meta: {
|
||||
permissionId: RoutePermissionId.CLUB_PAGE
|
||||
}
|
||||
permissionId: RoutePermissionId.CLUB_PAGE,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/2048',
|
||||
component: () => import('@/views/Game2048Page.vue')
|
||||
component: () => import('@/views/Game2048Page.vue'),
|
||||
// meta: {
|
||||
// permissionId: RoutePermissionId.USER_PAGE,
|
||||
// },
|
||||
},
|
||||
{
|
||||
path: '/:path(.*)*',
|
||||
name: 'NotFound',
|
||||
component: () => import('@/views/ErrorPage.vue')
|
||||
}
|
||||
name: 'notFound',
|
||||
component: () => import('@/views/ErrorPage.vue'),
|
||||
props: {
|
||||
type: PageErrorType.NOT_FOUND,
|
||||
} satisfies PageErrorReason,
|
||||
},
|
||||
];
|
||||
(function processRoutes(routes: RouteRecordRaw[], idPool: IdPool) {
|
||||
for (const route of routes) {
|
||||
if (route.name === undefined) {
|
||||
route.name = `AnonymousPage${idPool.newId()}`;
|
||||
processRoutes(route.children ?? [], idPool);
|
||||
}
|
||||
}
|
||||
})(routes, new IdPool(1));
|
||||
export const tempErrorPageName = '_TempError';
|
||||
// (function processRoutes(routes: RouteRecordRaw[], idPool: IdPool) {
|
||||
// for (const route of routes) {
|
||||
// if (route.name === undefined) {
|
||||
// route.name = `AnonymousPage${idPool.newId()}`;
|
||||
// processRoutes(route.children ?? [], idPool);
|
||||
// }
|
||||
// }
|
||||
// })(routes, new IdPool(1));
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes: routes
|
||||
routes: routes,
|
||||
});
|
||||
|
||||
router.beforeEach(async (to, from) => {
|
||||
router.beforeEach(async (to) => {
|
||||
const userStore = useUserStore();
|
||||
const pageStore = usePageStore();
|
||||
const { permissionId } = to.meta;
|
||||
pageStore.setNewRouteId(to);
|
||||
try {
|
||||
switch (userStore.userInfoStatus) {
|
||||
case 'uninitialized': {
|
||||
const succeed = await userStore.updateSelfUserInfo(true);
|
||||
if (!succeed) {
|
||||
if (permissionId === undefined) {
|
||||
return;
|
||||
}
|
||||
return pageStore.createTempErrorRoute(
|
||||
{
|
||||
type: 'networkError',
|
||||
originalPath: to.fullPath
|
||||
},
|
||||
to
|
||||
);
|
||||
if (!userStore.userInfo) {
|
||||
const succeed = await userStore.updateSelfUserInfo(true);
|
||||
if (!succeed) {
|
||||
if (permissionId === undefined) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
return pageStore.createTempErrorRoute(
|
||||
{
|
||||
type: PageErrorType.NETWORK_ERROR,
|
||||
},
|
||||
to,
|
||||
);
|
||||
}
|
||||
case 'initializing':
|
||||
return false;
|
||||
}
|
||||
console.log(userStore.permissions);
|
||||
|
||||
if (permissionId) {
|
||||
if (userStore.hasPermission(permissionId)) {
|
||||
return true;
|
||||
} else {
|
||||
return pageStore.createTempErrorRoute({ type: 'noPermission' }, from);
|
||||
return pageStore.createTempErrorRoute({ type: PageErrorType.NO_PERMISSION }, to);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
pageStore.removeRouteId(to);
|
||||
}
|
||||
});
|
||||
router.beforeEach((to) => {
|
||||
const pageStore = usePageStore();
|
||||
switch (to.name) {
|
||||
case 'NotFound':
|
||||
pageStore.pageErrorReason = { type: 'notFound' };
|
||||
break;
|
||||
case undefined:
|
||||
pageStore.pageErrorReason = undefined;
|
||||
}
|
||||
});
|
||||
// router.afterEach(
|
||||
// useThrottleFn(
|
||||
// async (to) => {
|
||||
@ -130,8 +114,5 @@ router.beforeEach((to) => {
|
||||
router.afterEach((to) => {
|
||||
const pageStore = usePageStore();
|
||||
pageStore.removeRouteId(to);
|
||||
if (to.name === tempErrorPageName) {
|
||||
router.removeRoute(tempErrorPageName);
|
||||
}
|
||||
});
|
||||
export default router;
|
||||
|
@ -1,5 +1,5 @@
|
||||
export enum RoutePermissionId {
|
||||
MAIN_PAGE = 1,
|
||||
USER_PAGE = 2,
|
||||
CLUB_PAGE = 7
|
||||
CLUB_PAGE = 7,
|
||||
}
|
||||
|
@ -6,25 +6,25 @@ function createResponseSchema<T extends z.ZodTypeAny>(data: T) {
|
||||
.object({
|
||||
code: z.number().min(200).max(299),
|
||||
msg: z.string(),
|
||||
time: z.coerce.date()
|
||||
time: z.coerce.date(),
|
||||
})
|
||||
.extend({ data: data })
|
||||
.transform((raw) =>
|
||||
Object.assign(raw, {
|
||||
type: 'success'
|
||||
} as const)
|
||||
type: 'success',
|
||||
} as const),
|
||||
),
|
||||
z
|
||||
.object({
|
||||
code: z.number(),
|
||||
msg: z.string(),
|
||||
time: z.coerce.date()
|
||||
time: z.coerce.date(),
|
||||
})
|
||||
.transform((raw) =>
|
||||
Object.assign(raw, {
|
||||
type: 'error'
|
||||
} as const)
|
||||
)
|
||||
type: 'error',
|
||||
} as const),
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
@ -35,11 +35,11 @@ export const loginResponseSchema = ordinarySchema;
|
||||
export const registerResponseSchema = ordinarySchema;
|
||||
export const idAndNameSchema = z.object({
|
||||
id: z.number(),
|
||||
name: z.string()
|
||||
name: z.string(),
|
||||
});
|
||||
|
||||
const _authSchema = idAndNameSchema.extend({
|
||||
permissions: idAndNameSchema.array()
|
||||
permissions: idAndNameSchema.array(),
|
||||
});
|
||||
export const userInfoResponseSchema = createResponseSchema(
|
||||
idAndNameSchema.extend({
|
||||
@ -48,10 +48,10 @@ export const userInfoResponseSchema = createResponseSchema(
|
||||
club: z.nullable(
|
||||
idAndNameSchema.extend({
|
||||
commit: z.string(),
|
||||
auth: _authSchema
|
||||
})
|
||||
)
|
||||
})
|
||||
auth: _authSchema,
|
||||
}),
|
||||
),
|
||||
}),
|
||||
);
|
||||
export type SucceedUserInfoResponse = SucceedResponse<z.infer<typeof userInfoResponseSchema>>;
|
||||
export type UserInfo = SucceedUserInfoResponse['data'];
|
||||
@ -59,10 +59,10 @@ export const verifyResponseSchema = createResponseSchema(
|
||||
z
|
||||
.object({
|
||||
img: z.string(),
|
||||
key: z.string()
|
||||
key: z.string(),
|
||||
})
|
||||
.transform((raw) => {
|
||||
raw.img = `data:image/png;base64,${raw.img}`;
|
||||
return raw;
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
@ -39,14 +39,14 @@ export const useGame2048Store = defineStore('2048', () => {
|
||||
watch([width, height], $reset, { flush: 'sync' });
|
||||
const rawTileGrid = useLocalStorage<Pick<Tile, 'number' | 'removed'>[][][]>(
|
||||
'2048-tile-grid',
|
||||
create()
|
||||
create(),
|
||||
);
|
||||
watch(
|
||||
rawTileGrid,
|
||||
() => {
|
||||
isInitial.value = false;
|
||||
},
|
||||
{ flush: 'sync' }
|
||||
{ flush: 'sync' },
|
||||
);
|
||||
const isInitial = useLocalStorage('2048-is-initial', true);
|
||||
return {
|
||||
@ -59,6 +59,6 @@ export const useGame2048Store = defineStore('2048', () => {
|
||||
rawTileGrid,
|
||||
isInitial,
|
||||
gameKey,
|
||||
$reset
|
||||
$reset,
|
||||
};
|
||||
});
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { DoubleQueue } from '@/utils/double-queue';
|
||||
import { defineStore } from 'pinia';
|
||||
import { reactive, ref } from 'vue';
|
||||
import { reactive, readonly, ref } from 'vue';
|
||||
|
||||
export type BackgroundURL = string | undefined;
|
||||
export type BackgroundOptions = {
|
||||
@ -12,15 +13,14 @@ export type BackgroundTask = {
|
||||
};
|
||||
|
||||
export class CancelledError extends Error {}
|
||||
|
||||
export const useBackgroundStore = defineStore('background', () => {
|
||||
const taskQueue = reactive<BackgroundTask[]>([]);
|
||||
const taskQueue = new DoubleQueue<BackgroundTask>();
|
||||
const getFuturesMap = new Map<number, PromiseWithResolvers<BackgroundTask>>();
|
||||
const maxCompId = ref(0);
|
||||
|
||||
function getURL(id: number) {
|
||||
const future = Promise.withResolvers<BackgroundTask>();
|
||||
if (taskQueue.length) {
|
||||
if (taskQueue.size) {
|
||||
future.resolve(taskQueue.shift()!);
|
||||
} else {
|
||||
getFuturesMap.set(id, future);
|
||||
@ -30,7 +30,7 @@ export const useBackgroundStore = defineStore('background', () => {
|
||||
|
||||
function addURL(
|
||||
url: BackgroundURL,
|
||||
options: BackgroundOptions = { resolveTiming: 'transitionEnd' }
|
||||
options: BackgroundOptions = { resolveTiming: 'transitionEnd' },
|
||||
) {
|
||||
const addFuture = Promise.withResolvers<void>();
|
||||
// addFuture.promise.then(() => console.log('resolve', getFuturesMap));
|
||||
@ -38,7 +38,7 @@ export const useBackgroundStore = defineStore('background', () => {
|
||||
const task: BackgroundTask = {
|
||||
url,
|
||||
options,
|
||||
addFuture: addFuture
|
||||
addFuture: addFuture,
|
||||
};
|
||||
if (getFuturesMap.size) {
|
||||
const [[id, getFuture]] = getFuturesMap;
|
||||
@ -63,7 +63,6 @@ export const useBackgroundStore = defineStore('background', () => {
|
||||
for (const task of taskQueue) {
|
||||
task.addFuture.reject(new CancelledError());
|
||||
}
|
||||
taskQueue.splice(0);
|
||||
console.log(taskQueue);
|
||||
for (const id of [...getFuturesMap.keys()]) {
|
||||
unregisterComp(id);
|
||||
@ -71,11 +70,10 @@ export const useBackgroundStore = defineStore('background', () => {
|
||||
}
|
||||
|
||||
return {
|
||||
taskQueue: taskQueue,
|
||||
getURL,
|
||||
addURL,
|
||||
newCompId,
|
||||
unregisterComp,
|
||||
$reset
|
||||
$reset,
|
||||
};
|
||||
});
|
||||
|
@ -1,27 +1,16 @@
|
||||
import router, { tempErrorPageName } from '@/router';
|
||||
import router from '@/router';
|
||||
import { IdPool } from '@/utils';
|
||||
import type { PageErrorReason } from '@/views/ErrorPage.vue';
|
||||
import { defineStore } from 'pinia';
|
||||
import { computed, ref } from 'vue';
|
||||
import { computed } from 'vue';
|
||||
import {
|
||||
type RouteLocationNormalized,
|
||||
type RouteLocationRaw,
|
||||
type RouteRecordRaw,
|
||||
useRoute
|
||||
} from 'vue-router';
|
||||
|
||||
export type PageErrorReason =
|
||||
| {
|
||||
type: 'notFound';
|
||||
}
|
||||
| {
|
||||
type: 'networkError';
|
||||
originalPath: string;
|
||||
}
|
||||
| {
|
||||
type: 'noPermission';
|
||||
};
|
||||
const tempErrorRouteName = '_TempError';
|
||||
export const usePageStore = defineStore('page-store', () => {
|
||||
const pageErrorReason = ref<PageErrorReason>();
|
||||
const routeIdPool = new IdPool();
|
||||
const routeIds = routeIdPool.usedIdSet;
|
||||
const pageLoadingCount = computed(() => routeIds.size);
|
||||
@ -41,35 +30,30 @@ export const usePageStore = defineStore('page-store', () => {
|
||||
function createTempErrorRoute(
|
||||
reason: Exclude<
|
||||
PageErrorReason,
|
||||
| {
|
||||
type: 'notFound';
|
||||
}
|
||||
| undefined
|
||||
{
|
||||
type: 'notFound';
|
||||
}
|
||||
>,
|
||||
currentRoute: RouteLocationNormalized | undefined = useRoute(),
|
||||
root: boolean = false
|
||||
to: RouteLocationNormalized,
|
||||
): RouteLocationRaw {
|
||||
pageErrorReason.value = reason;
|
||||
const tempErrorRoute: RouteRecordRaw = {
|
||||
path: currentRoute.fullPath,
|
||||
name: tempErrorPageName,
|
||||
component: () => import('@/views/ErrorPage.vue')
|
||||
path: to.fullPath,
|
||||
name: tempErrorRouteName,
|
||||
component: () => import('@/views/ErrorPage.vue'),
|
||||
props: reason,
|
||||
};
|
||||
const parentRouteName = currentRoute.matched.at(-2)?.name;
|
||||
if (parentRouteName !== undefined && !root) {
|
||||
router.addRoute(parentRouteName, tempErrorRoute);
|
||||
} else {
|
||||
router.addRoute(tempErrorRoute);
|
||||
if (router.hasRoute(tempErrorRouteName)) {
|
||||
router.removeRoute(tempErrorRouteName);
|
||||
}
|
||||
return { name: tempErrorPageName, replace: true };
|
||||
router.addRoute(tempErrorRoute);
|
||||
return { name: tempErrorRouteName, replace: true };
|
||||
}
|
||||
|
||||
return {
|
||||
pageErrorReason,
|
||||
routeIds,
|
||||
pageLoadingCount,
|
||||
setNewRouteId,
|
||||
removeRouteId,
|
||||
createTempErrorRoute
|
||||
createTempErrorRoute,
|
||||
};
|
||||
});
|
||||
|
@ -12,59 +12,53 @@ import { z } from 'zod';
|
||||
type Permission = z.infer<typeof idAndNameSchema>;
|
||||
export const useUserStore = defineStore('user', () => {
|
||||
const token = useLocalStorage<string | null>('token', null, {
|
||||
serializer: StorageSerializers.string
|
||||
serializer: StorageSerializers.string,
|
||||
});
|
||||
const userInfo = useLocalStorage<UserInfo | null>('user-info', null, {
|
||||
serializer: StorageSerializers.object
|
||||
serializer: StorageSerializers.object,
|
||||
});
|
||||
const permissions = computed<Permission[]>(() => userInfo.value?.auth.permissions ?? []);
|
||||
const userInfoStatus = ref<'uninitialized' | 'initializing' | 'initialized'>('uninitialized');
|
||||
const isInitializing = ref(false);
|
||||
watch(
|
||||
userInfo,
|
||||
(info) => {
|
||||
userInfoStatus.value = info ? 'initialized' : 'uninitialized';
|
||||
isInitializing.value = !!info;
|
||||
},
|
||||
{ flush: 'sync' }
|
||||
{ flush: 'sync' },
|
||||
);
|
||||
async function updateSelfUserInfo(
|
||||
showErrorMessage: boolean,
|
||||
silent: boolean = false
|
||||
): Promise<boolean> {
|
||||
if (!silent) userInfoStatus.value = 'initializing';
|
||||
const raw = await axiosInstance
|
||||
.get<RawResp>('/api/user/info')
|
||||
.then((r) => r.data)
|
||||
.catch((e: AxiosError) => {
|
||||
if (!showErrorMessage) return;
|
||||
ElMessage.error(errorMessage('获取用户信息失败', e.code, e.message));
|
||||
});
|
||||
const resp = userInfoResponseSchema.parse(raw);
|
||||
if (resp.type === 'error') {
|
||||
if (showErrorMessage) {
|
||||
ElMessage.error(errorMessage('获取用户信息失败', resp.code, resp.msg));
|
||||
async function updateSelfUserInfo(showErrorMessage: boolean): Promise<boolean> {
|
||||
isInitializing.value = true;
|
||||
try {
|
||||
const raw = await axiosInstance
|
||||
.get<RawResp>('/api/user/info')
|
||||
.then((r) => r.data)
|
||||
.catch((e: AxiosError) => {
|
||||
if (!showErrorMessage) return;
|
||||
ElMessage.error(errorMessage('获取用户信息失败', e.code, e.message));
|
||||
});
|
||||
const resp = userInfoResponseSchema.parse(raw);
|
||||
if (resp.type === 'error') {
|
||||
if (showErrorMessage) {
|
||||
ElMessage.error(errorMessage('获取用户信息失败', resp.code, resp.msg));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
userInfo.value = resp.data;
|
||||
return true;
|
||||
} finally {
|
||||
isInitializing.value = false;
|
||||
}
|
||||
userInfo.value = resp.data;
|
||||
return true;
|
||||
}
|
||||
function hasPermission(permissionId: number) {
|
||||
return permissions.value.find((permission) => permission.id === permissionId);
|
||||
}
|
||||
|
||||
function $reset() {
|
||||
token.value = null;
|
||||
userInfo.value = null;
|
||||
userInfoStatus.value = 'uninitialized';
|
||||
}
|
||||
|
||||
return {
|
||||
token,
|
||||
userInfo,
|
||||
permissions,
|
||||
userInfoStatus,
|
||||
isInitializing,
|
||||
updateSelfUserInfo,
|
||||
hasPermission,
|
||||
$reset
|
||||
};
|
||||
});
|
||||
|
@ -1,6 +1,6 @@
|
||||
export function create2DArray<T>(height: number, width: number, cell: T | (() => T)): T[][] {
|
||||
return Array.from({ length: height }, () =>
|
||||
Array.from({ length: width }, () => (cell instanceof Function ? cell() : cell))
|
||||
Array.from({ length: width }, () => (cell instanceof Function ? cell() : cell)),
|
||||
);
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@ export function get2DArrayItem<T>(
|
||||
grid: T[][],
|
||||
first: number,
|
||||
second: number,
|
||||
colFirst: boolean
|
||||
colFirst: boolean,
|
||||
): T {
|
||||
return colFirst ? grid[first][second] : grid[second][first];
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
export function errorMessage(
|
||||
operation: string,
|
||||
code: string | number | undefined,
|
||||
message: string
|
||||
message: string,
|
||||
) {
|
||||
return `${operation}: ${code !== undefined ? `(${code})${message}` : message}`;
|
||||
}
|
||||
|
87
src/utils/double-queue.ts
Normal file
87
src/utils/double-queue.ts
Normal file
@ -0,0 +1,87 @@
|
||||
interface Node<T> {
|
||||
value: T;
|
||||
prev?: Node<T>;
|
||||
next?: Node<T>;
|
||||
}
|
||||
export class DoubleQueue<T> {
|
||||
protected head?: Node<T>;
|
||||
protected tail?: Node<T>;
|
||||
protected _size = 0;
|
||||
constructor(it?: Iterable<T>) {
|
||||
if (it) {
|
||||
for (const item of it) {
|
||||
this.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
get size() {
|
||||
return this._size;
|
||||
}
|
||||
push(item: T) {
|
||||
const node: Node<T> = { value: item };
|
||||
if (this.size) {
|
||||
(this.tail!.next = node).prev = this.tail;
|
||||
this.tail = node;
|
||||
} else {
|
||||
this.head = this.tail = node;
|
||||
}
|
||||
this._size++;
|
||||
}
|
||||
pop(): T | undefined {
|
||||
if (!this.size) return undefined;
|
||||
const node = this.tail!;
|
||||
if (this.size === 1) {
|
||||
this.head = this.tail = undefined;
|
||||
} else {
|
||||
node.prev!.next = undefined;
|
||||
}
|
||||
this._size--;
|
||||
if (this.size === 1) this.head = this.tail;
|
||||
return node.value;
|
||||
}
|
||||
unshift(item: T) {
|
||||
const node: Node<T> = { value: item };
|
||||
if (this.size) {
|
||||
(this.head!.prev = node).next = this.head;
|
||||
this.head = node;
|
||||
} else {
|
||||
this.head = this.tail = node;
|
||||
}
|
||||
this._size++;
|
||||
}
|
||||
shift(): T | undefined {
|
||||
if (!this.size) return undefined;
|
||||
const node = this.head!;
|
||||
if (this.size === 1) {
|
||||
this.head = this.tail = undefined;
|
||||
} else {
|
||||
node.next!.prev = undefined;
|
||||
}
|
||||
this._size--;
|
||||
if (this.size === 1) this.tail = this.head;
|
||||
return node.value;
|
||||
}
|
||||
get(i: number): T | undefined {
|
||||
let reversed = i < 0;
|
||||
if (i < -this.size || i >= this.size) return undefined;
|
||||
let node = reversed ? this.tail! : this.head!;
|
||||
if (reversed) i = -i - 1;
|
||||
for (let j = 1; j <= i; j++) {
|
||||
node = reversed ? node.prev! : node.next!;
|
||||
}
|
||||
return node.value;
|
||||
}
|
||||
*iter() {
|
||||
for (let node = this.head; node; node = node.next) {
|
||||
yield node.value;
|
||||
}
|
||||
}
|
||||
[Symbol.iterator]() {
|
||||
return this.iter();
|
||||
}
|
||||
*reverseIter() {
|
||||
for (let node = this.tail; node; node = node.prev) {
|
||||
yield node.value;
|
||||
}
|
||||
}
|
||||
}
|
@ -13,7 +13,7 @@ export function useRefresh() {
|
||||
key,
|
||||
refresh() {
|
||||
return ++key.value;
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1,12 +1,14 @@
|
||||
<template>
|
||||
<div class="page-root">
|
||||
<template v-if="reason !== undefined">
|
||||
<div class="error-container">
|
||||
<div v-if="reason.type === 'notFound'" class="error-reason">404</div>
|
||||
<div v-else-if="reason.type === 'networkError'" class="error-reason">网络错误</div>
|
||||
<div v-else-if="reason.type === 'noPermission'" class="error-reason">没有权限</div>
|
||||
<div class="error-container">
|
||||
<div v-if="reason.type === PageErrorType.NOT_FOUND" class="error-reason">404</div>
|
||||
<div v-else-if="reason.type === PageErrorType.NETWORK_ERROR" class="error-reason">
|
||||
网络错误
|
||||
</div>
|
||||
</template>
|
||||
<div v-else-if="reason.type === PageErrorType.NO_PERMISSION" class="error-reason">
|
||||
没有权限
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -21,10 +23,16 @@
|
||||
font-size: 50px;
|
||||
}
|
||||
</style>
|
||||
<script lang="ts" setup>
|
||||
import { usePageStore } from '@/stores/page';
|
||||
import { toRef } from 'vue';
|
||||
|
||||
const pageStore = usePageStore();
|
||||
const reason = toRef(pageStore, 'pageErrorReason');
|
||||
<script lang="ts">
|
||||
export enum PageErrorType {
|
||||
NOT_FOUND,
|
||||
NETWORK_ERROR,
|
||||
NO_PERMISSION,
|
||||
}
|
||||
</script>
|
||||
<script lang="ts" setup>
|
||||
export type PageErrorReason = {
|
||||
type: PageErrorType.NOT_FOUND | PageErrorType.NETWORK_ERROR | PageErrorType.NO_PERMISSION;
|
||||
};
|
||||
const reason = defineProps<PageErrorReason>();
|
||||
</script>
|
||||
|
@ -59,7 +59,7 @@ const userStore = useUserStore();
|
||||
const initialized = ref(false);
|
||||
const userInfo = ref<UserInfo>();
|
||||
const isSelf = computed(() =>
|
||||
userStore.userInfo === null ? false : userStore.userInfo.id === userInfo.value?.id
|
||||
userStore.userInfo === null ? false : userStore.userInfo.id === userInfo.value?.id,
|
||||
);
|
||||
const route = useRoute();
|
||||
const id = route.params.id as string | undefined;
|
||||
@ -72,7 +72,7 @@ watch(
|
||||
() => userStore.userInfo,
|
||||
(info) => {
|
||||
userInfo.value = info ?? undefined;
|
||||
}
|
||||
},
|
||||
);
|
||||
onBeforeMount(async () => {
|
||||
if (id == undefined) {
|
||||
@ -81,14 +81,18 @@ onBeforeMount(async () => {
|
||||
} else {
|
||||
try {
|
||||
const userInfoResponse = userInfoResponseSchema.parse(
|
||||
(await axiosInstance.get(`/api/user/info/${id}`)).data
|
||||
(await axiosInstance.get(`/api/user/info/${id}`)).data,
|
||||
);
|
||||
if (userInfoResponse.type === 'error') {
|
||||
return;
|
||||
}
|
||||
userInfo.value = userInfoResponse.data;
|
||||
if (!userInfo.value.avatar) { /* empty */ }
|
||||
} catch (e) { /* empty */ } finally {
|
||||
if (!userInfo.value.avatar) {
|
||||
/* empty */
|
||||
}
|
||||
} catch (e) {
|
||||
/* empty */
|
||||
} finally {
|
||||
initialized.value = true;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user