Skip to content

Commit 6688bcb

Browse files
committed
refactor: apply Vue best practices across src
1 parent ef5f548 commit 6688bcb

35 files changed

Lines changed: 599 additions & 385 deletions

src/App.vue

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,24 @@
11
<template>
22
<div id="app" @click="handleClick">
33
<CookieNotice />
4-
<router-view v-slot="{ Component }">
5-
<!-- keep alive源自于vue-router的缓存 -->
6-
<!-- keep alive comes from cach function from vue-router -->
4+
<router-view v-slot="{ Component, route }">
75
<keep-alive>
8-
<component :is="Component" v-if="$route.meta.keepAlive" :key="$route.fullPath" />
6+
<component :is="Component" v-if="route.meta.keepAlive" :key="route.fullPath" />
97
</keep-alive>
10-
<component :is="Component" v-if="!$route.meta.keepAlive" :key="$route.fullPath" />
8+
<component :is="Component" v-if="!route.meta.keepAlive" :key="route.fullPath" />
119
</router-view>
1210
</div>
1311
</template>
1412

1513
<script setup lang="ts">
1614
import showUserCard from '@popup/userProfileDialog.ts'
1715
import CookieNotice from './components/utils/CookieNotice.vue'
16+
1817
function handleClick(event: MouseEvent) {
19-
const target = event.target as HTMLElement
20-
if (target.classList.contains('RUser')) {
21-
showUserCard(target.dataset.user || '')
22-
}
18+
if (!(event.target instanceof Element)) return
19+
const userElement = event.target.closest<HTMLElement>('.RUser')
20+
const userId = userElement?.dataset.user
21+
if (userId) showUserCard(userId)
2322
}
2423
</script>
2524

src/components/friends/item.vue

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,20 @@
1616
</template>
1717

1818
<script setup lang="ts">
19-
import { ref, onMounted } from 'vue'
19+
import { computed, ref, watch } from 'vue'
2020
import type { User } from '@services/../pl-serve-type-main/type/main'
2121
import showUserCard from '@popup/userProfileDialog.ts'
2222
import { getPath } from '@services/utils'
2323
import { getData } from '@services/api/getData'
2424
import { getUserUrl } from '@services/utils'
25-
const { user } = defineProps<{
25+
const props = defineProps<{
2626
user: User
2727
}>()
2828
const iconPath = ref(getPath('/@base/assets/user/Status-None.png'))
29-
const avararUrl = getUserUrl(user)
29+
const avararUrl = computed(() => getUserUrl(props.user))
3030
3131
async function getIconPath() {
32-
const re = await getData('/Users/GetUser', { ID: user.ID })
32+
const re = await getData('/Users/GetUser', { ID: props.user.ID })
3333
if (!re.Data) return '/@base/assets/user/Status-None.png'
3434
return getIcon(Number(re.Data.Relation))
3535
}
@@ -47,10 +47,17 @@ function getIcon(relation: number) {
4747
}
4848
}
4949
50-
onMounted(async () => {
51-
const p = await getIconPath()
52-
iconPath.value = getPath(p)
53-
})
50+
let iconRequestId = 0
51+
52+
watch(
53+
() => props.user.ID,
54+
async () => {
55+
const requestId = ++iconRequestId
56+
const p = await getIconPath()
57+
if (requestId === iconRequestId) iconPath.value = getPath(p)
58+
},
59+
{ immediate: true },
60+
)
5461
</script>
5562

5663
<style scoped>

src/components/friends/list.vue

Lines changed: 40 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<template>
22
<infiniteScroll :initial-items="items" :has-more="!noMore" :margin-top="-200" @load="handleLoad">
33
<template #default="{ items }">
4-
<n-grid :cols="cols || 2">
4+
<n-grid :cols="props.cols || 2">
55
<n-gi v-for="user in items as RelationList[]" :key="user.User?.ID">
66
<UserItem v-if="user.User" :user="user.User" />
77
</n-gi>
@@ -24,70 +24,72 @@ import { showMessage } from '@popup/naiveui'
2424
2525
// cols需要在父组件传参,这可能会在好友界面和Profile界面(未实现)展现
2626
// Props `cols` needs to be passed from the parent component, which may be displayed in the Friends page and Profile page (not implemented yet).
27-
const { userid, type } = defineProps<{
27+
const props = defineProps<{
2828
userid?: string
2929
type?: string
3030
cols?: number
3131
}>()
3232
33-
let loading = ref(false)
34-
let skip = ref(0)
35-
let noMore = ref(false)
36-
let hasInformed = ref(false)
33+
const loading = ref(false)
34+
const skip = ref(0)
35+
const noMore = ref(false)
36+
const hasInformed = ref(false)
3737
const items = ref<RelationList[]>([])
3838
const { t } = useI18n()
3939
// Vue对于Ref会自动处理数据竞争问题
4040
// Vue automatically handles data race issues with Ref.
4141
4242
async function handleLoad() {
43-
if (!userid) return
43+
if (!props.userid) return
4444
if (loading.value) return // Serves as a "lock"
45-
loading.value = true
4645
if (noMore.value) {
4746
if (!hasInformed.value) showMessage('info', t('ui.messages.noMore'))
4847
hasInformed.value = true
4948
return
5049
}
51-
const getRelationsRes = await getData('/Users/GetRelations', {
52-
UserID: userid,
53-
DisplayType: type ? Number(type) : 0,
54-
Skip: skip.value,
55-
Take: 24,
56-
Query: '',
57-
})
58-
if (getRelationsRes.Status !== 200) {
59-
showAPiError(t('errors.apiErrorTitle'), t('errors.apiErrorMessage'), handleLoad)
60-
const _req = removeToken({
61-
UserID: userid,
62-
DisplayType: type,
50+
loading.value = true
51+
try {
52+
const getRelationsRes = await getData('/Users/GetRelations', {
53+
UserID: props.userid,
54+
DisplayType: props.type ? Number(props.type) : 0,
6355
Skip: skip.value,
6456
Take: 24,
6557
Query: '',
6658
})
67-
const _res = removeToken(getRelationsRes)
68-
window.$ErrorLogger.captureApiError(
69-
'POST',
70-
'/Users/GetRelations',
71-
getRelationsRes.Status,
72-
_res,
73-
_req,
74-
)
75-
console.error(`/Users/GetRelations returned ${getRelationsRes.Status}`, _res)
76-
loading.value = false
77-
return
78-
}
59+
if (getRelationsRes.Status !== 200) {
60+
showAPiError(t('errors.apiErrorTitle'), t('errors.apiErrorMessage'), handleLoad)
61+
const request = removeToken({
62+
UserID: props.userid,
63+
DisplayType: props.type,
64+
Skip: skip.value,
65+
Take: 24,
66+
Query: '',
67+
})
68+
const result = removeToken(getRelationsRes)
69+
window.$ErrorLogger.captureApiError(
70+
'POST',
71+
'/Users/GetRelations',
72+
getRelationsRes.Status,
73+
result,
74+
request,
75+
)
76+
console.error(`/Users/GetRelations returned ${getRelationsRes.Status}`, result)
77+
return
78+
}
7979
// 在某些地方用的skip传入的是时间戳,但是这里找不到可能与时间戳有关的逻辑,skip为整数也能work
8080
// In some places, the 'skip' is a timestamp, but here there doesn't seem to be any logic related to the timestamp; 'skip' as an integer also works.
81-
noMore.value = (getRelationsRes.Data?.$values?.length ?? 0) < 24
82-
loading.value = false
83-
skip.value += 24
84-
if (getRelationsRes.Data?.$values) {
85-
items.value = [...items.value, ...getRelationsRes.Data.$values]
81+
noMore.value = (getRelationsRes.Data?.$values?.length ?? 0) < 24
82+
skip.value += 24
83+
if (getRelationsRes.Data?.$values) {
84+
items.value.push(...getRelationsRes.Data.$values)
85+
}
86+
} finally {
87+
loading.value = false
8688
}
8789
}
8890
8991
window.$Logger.logPageView({
90-
pageLink: `/Social/Friends/${Number(type)}/`,
92+
pageLink: `/Social/Friends/${Number(props.type)}/`,
9193
timeStamp: Date.now(),
9294
})
9395

src/components/messages/MessageItem.vue

Lines changed: 32 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
<template>
2-
<div id="notification_container" @click="handleReply">
2+
<div class="notification_container" @click="handleReply">
33
<div class="img">
4-
<img id="avatar" :src="avatarUrl" @click.stop="showUserCard(message.UserID)" />
4+
<img class="avatar" :src="avatarUrl" alt="" @click.stop="showUserCard(message.UserID)" />
55
</div>
6-
<div id="notification" class="notification">
7-
<div id="notification_title" class="notification_title">
6+
<div class="notification">
7+
<div class="notification_title">
88
<div class="name">{{ message.Nickname }}</div>
99
<div class="time">{{ formatDate(message.ID, true) }}</div>
1010
<div v-if="currentUserId === message.UserID" class="delete" @click.stop="deleteMsg">
1111
{{ t('messagesI18n.delete') }}
1212
</div>
1313
</div>
14-
<div id="notification_message" class="notification_message">
14+
<div class="notification_message">
1515
<div
16-
id="notification_text"
1716
v-richText="
1817
() =>
1918
parse(message.Content, {
@@ -29,7 +28,7 @@
2928
</template>
3029

3130
<script setup lang="ts">
32-
import { ref, onMounted, watch } from 'vue'
31+
import { ref, watch } from 'vue'
3332
import parse from '@services/pltxt2htm/advancedParser'
3433
import showUserCard from '@popup/userProfileDialog.ts'
3534
import { getAvatarUrl } from '@services/getUserCurentAvatarByID'
@@ -43,26 +42,36 @@ const props = defineProps<{
4342
message: CommentResult
4443
}>()
4544
46-
const emit = defineEmits(['msgClick', 'deleteMsg'])
45+
const emit = defineEmits<{
46+
msgClick: [message: CommentResult]
47+
deleteMsg: [message: CommentResult]
48+
}>()
4749
const currentUserId = storageManager.getObj('userInfo')?.value?.ID || ''
4850
const avatarUrl = ref(getPath('/@base/assets/user/default-avatar.png'))
4951
52+
let avatarRequestId = 0
53+
5054
const setCurrentAvatar = async () => {
55+
const requestId = ++avatarRequestId
5156
const isAnonymous = props.message.Flags?.includes('Anonymous')
57+
let nextAvatar = getPath('/@base/assets/user/default-avatar.png')
5258
5359
if (!isAnonymous && props.message.UserID !== '') {
5460
// 有些地方是匿名的,所以userID为空,不设置心得头像就会沿用默认头像
5561
// Some places are anonymous, so if userID is empty, the default avatar will be used.
56-
avatarUrl.value = await getAvatarUrl(props.message.UserID)
62+
nextAvatar = await getAvatarUrl(props.message.UserID)
5763
} else if (/^\d{4}$/.test(props.message.Nickname)) {
58-
avatarUrl.value = getAnonymousAvatarByNickname(props.message.Nickname)
59-
} else {
60-
avatarUrl.value = getPath('/@base/assets/user/default-avatar.png')
64+
nextAvatar = getAnonymousAvatarByNickname(props.message.Nickname)
6165
}
66+
67+
if (requestId === avatarRequestId) avatarUrl.value = nextAvatar
6268
}
6369
64-
onMounted(setCurrentAvatar)
65-
watch(() => [props.message.UserID, props.message.Nickname, props.message.Flags], setCurrentAvatar)
70+
watch(
71+
() => [props.message.UserID, props.message.Nickname, props.message.Flags],
72+
setCurrentAvatar,
73+
{ immediate: true },
74+
)
6675
6776
function handleReply() {
6877
emit('msgClick', props.message)
@@ -76,7 +85,7 @@ function deleteMsg() {
7685
</script>
7786

7887
<style scoped>
79-
#notification_container {
88+
.notification_container {
8089
height: fit-content;
8190
width: calc(100% - 5px);
8291
margin-left: 5px;
@@ -90,37 +99,29 @@ function deleteMsg() {
9099
word-break: break-all;
91100
}
92101
93-
#notification_container:hover {
102+
.notification_container:hover {
94103
background-color: #f0f0f0;
95104
}
96105
97-
#avatar {
106+
.avatar {
98107
height: 60px;
99108
width: 60px;
100109
border-radius: 50%;
101110
}
102111
103-
#avatar::after {
112+
.avatar::after {
104113
content: '';
105114
mix-blend-mode: luminosity;
106115
}
107116
108-
#notification {
117+
.notification {
109118
width: 100%;
110119
display: flex;
111120
flex-direction: column;
112121
gap: 5px;
113122
}
114123
115-
#notification_icon {
116-
width: 20px;
117-
height: 20px;
118-
top: 2px;
119-
background-color: transparent;
120-
display: flex;
121-
}
122-
123-
#notification_title {
124+
.notification_title {
124125
display: flex;
125126
width: 100%;
126127
flex-direction: row;
@@ -137,15 +138,15 @@ function deleteMsg() {
137138
font-weight: lighter;
138139
}
139140
140-
#notification_message {
141+
.notification_message {
141142
width: 100%;
142143
height: fit-content;
143144
display: flex;
144145
flex-direction: row;
145146
gap: 5px;
146147
}
147148
148-
#notification_text {
149+
.notification_text {
149150
font-size: 1em;
150151
text-align: left;
151152
height: fit-content;
@@ -156,15 +157,6 @@ function deleteMsg() {
156157
text-overflow: hidden;
157158
}
158159
159-
#icon {
160-
height: 16px;
161-
width: 16px;
162-
}
163-
164-
#notification_container:hover {
165-
background-color: #f0f0f0;
166-
}
167-
168160
.time {
169161
margin-left: 5px;
170162
font-weight: normal;
@@ -175,7 +167,7 @@ div {
175167
box-sizing: border-box;
176168
}
177169
178-
#notification_message :deep(img) {
170+
.notification_message :deep(img) {
179171
max-width: 90%;
180172
height: auto;
181173
}

0 commit comments

Comments
 (0)