🎈 perf: 修改社团列表

This commit is contained in:
Litrix 2025-01-06 13:02:06 +08:00
parent b71228e061
commit 07e215bdc9
7 changed files with 107 additions and 60 deletions

View File

@ -9,31 +9,12 @@
>
<template #default="{ data }">
<div class="flex gap-10px">
<div
<club-list-item
v-for="club of data"
class="flex flex-col items-center"
:class="itemClass"
:key="club.id"
@click="onItemClick(club)"
>
<el-avatar
shape="square"
class="select-none"
:size="100"
:src="getAvatarURL(club.avatar)"
>
<div class="text-black">暂无图片</div>
</el-avatar>
<div class="text-5 font-bold">{{ club.name }}</div>
<el-text
v-if="userStore.userInfo?.club?.id === club.id"
type="success"
class="flex items-center"
>
<icon-ep-success-filled />
<b>已加入</b>
</el-text>
</div>
:club="club"
:click="click"
@click="click && $emit('click', club)"
/>
</div>
</template>
<template #empty>
@ -42,22 +23,19 @@
</paged-wrapper>
</template>
<script setup lang="ts">
import { getAvatarURL } from '@/api';
import PagedWrapper from '@/components/PagedWrapper.vue';
import { clubListRespSchema, type Club } from '@/schemas/response';
import { useUserStore } from '@/stores/user';
import type { ComponentProps } from 'vue-component-type-helpers';
import ClubListItem from './ClubListItem.vue';
const userStore = useUserStore();
const { brief = false } = defineProps<{
const { click = false, brief = false } = defineProps<{
brief?: boolean;
itemClass?: string;
click?: boolean;
}>();
const emit = defineEmits<{
click: [club: Club];
}>();
function onItemClick(club: Club) {
emit('click', club);
}
const getClubListRequestOptions: ComponentProps<typeof PagedWrapper>['getRequestOptions'] = (
page,
num,

View File

@ -0,0 +1,44 @@
<template>
<div
class="flex"
:class="{
'cursor-pointer': click,
'flex-col items-center': mode === 'v',
'gap-10px': mode === 'h',
}"
:key="club.id"
@click="click && $emit('click', club)"
>
<el-avatar shape="square" class="select-none" :size="100" :src="getAvatarURL(club.avatar)">
<div class="text-black">暂无图片</div>
</el-avatar>
<div class="flex flex-col" :class="{ 'items-center': mode === 'v' }">
<div class="text-black font-bold" :class="mode === 'v' ? 'text-5' : 'text-7'">
{{ club.name }}
</div>
<div>
<el-text
v-if="userStore.userInfo?.club?.id === club.id"
class="flex items-center gap-5px"
:size="mode === 'h' ? 'large' : 'default'"
type="success"
>
<icon-ep-success-filled />
<b>已加入</b>
</el-text>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { getAvatarURL } from '@/api';
import type { Club } from '@/schemas/response';
import { useUserStore } from '@/stores/user';
const userStore = useUserStore();
const { click = false, mode = 'v' } = defineProps<{
club: Club;
click?: boolean;
mode?: 'h' | 'v';
}>();
defineEmits<{ click: [club: Club] }>();
</script>

View File

@ -127,6 +127,7 @@ const routes: RouteRecordRaw[] = [
userPermission: {
id: UserPermissionId.MANAGE_PAGE,
},
breadcrumb: '社团管理',
},
},
],

View File

@ -1,3 +1,5 @@
import { REQUEST_BASE_URL } from '@/env';
export namespace Neusoft {
export type BaseResp<T extends Record<string, unknown>> = {
code: number;
@ -30,6 +32,6 @@ export namespace Neusoft.SmartParty {
export type GalleryResp = PagedResp<Gallery>;
}
export namespace Neusoft {
export const SMART_PARTY_BASE_URL = 'http://124.93.196.45:10091/Neusoft/party';
export const SMART_PARTY_BASE_URL = `${REQUEST_BASE_URL}/neusoft`;
export const isSucceed = (resp: BaseResp<{}>) => resp.code >= 200 && resp.code < 300;
}

View File

@ -11,8 +11,8 @@ const createRespSchema = <T extends z.ZodTypeAny>(data: T) =>
.extend({ data: data })
.transform((raw) =>
Object.assign(raw, {
type: 'success',
} as const),
type: 'success' as const,
}),
),
z
.object({
@ -22,10 +22,18 @@ const createRespSchema = <T extends z.ZodTypeAny>(data: T) =>
})
.transform((raw) =>
Object.assign(raw, {
type: 'error',
} as const),
type: 'error' as const,
}),
),
]);
export const createPagedRespSchema = <T extends z.ZodTypeAny>(data: T) =>
createRespSchema(
z.object({
data: data.array(),
total: z.number(),
}),
);
export type AnyPagedRespSchema = ReturnType<typeof createPagedRespSchema>;
export type SucceedRespOf<T> = Extract<T, { type: 'success' }>;
export type AnyRespSchema = ReturnType<typeof createRespSchema>;
export type ErrorResp = Extract<z.infer<AnyRespSchema>, { type: 'error' }>;
@ -38,14 +46,16 @@ export const idAndNameSchema = z.object({
const authSchema = idAndNameSchema.extend({
permissions: idAndNameSchema.array(),
});
export const clubSchema = idAndNameSchema.extend({
avatar: z.optional(z.string()),
commit: z.string(),
});
export type Club = z.infer<typeof clubSchema>;
export const clubListRespSchema = createPagedRespSchema(clubSchema);
const userInfoDataSchema = idAndNameSchema.extend({
avatar: z.optional(z.string()),
auth: authSchema,
club: z.optional(
idAndNameSchema.extend({
commit: z.string(),
}),
),
club: z.optional(clubSchema),
clubAuth: z.optional(authSchema),
});
export const userInfoRespSchema = createRespSchema(userInfoDataSchema);
@ -65,17 +75,3 @@ export const verifyRespSchema = createRespSchema(
}),
);
export const uploadAvatarRespSchema = createRespSchema(z.string());
export const clubSchema = idAndNameSchema.extend({
avatar: z.optional(z.string()),
commit: z.string(),
});
export type Club = z.infer<typeof clubSchema>;
export const createPagedRespSchema = <T extends z.ZodTypeAny>(data: T) =>
createRespSchema(
z.object({
data: data.array(),
total: z.number(),
}),
);
export type AnyPagedRespSchema = ReturnType<typeof createPagedRespSchema>;
export const clubListRespSchema = createPagedRespSchema(clubSchema);

View File

@ -5,15 +5,34 @@
<template #header>
<b>社团列表</b>
</template>
<club-list item-class="cursor-pointer" @click="onClubClick" />
<club-list click @click="onClubClick" />
</el-card>
</div>
<el-dialog v-model="showClubDialog">
<template #title>社团信息</template>
<template v-if="showClubDialog"></template>
<el-dialog :class="$style['el-dialog']" v-model="showClubDialog" align-center>
<template #title><b>社团信息</b></template>
<div class="flex flex-col" v-if="showingClub">
<club-list-item :club="showingClub" mode="h" />
<div>简介{{ showingClub.commit }}</div>
<template v-if="userStore.logined && userInfo">
<el-button
v-if="userInfo.club && userInfo.club.id === showingClub.id"
class="self-end"
type="primary"
>
进入主页
</el-button>
<el-button v-else class="self-end" type="success">加入社团</el-button>
</template>
</div>
</el-dialog>
</el-main>
</template>
<style lang="scss" module>
.el-dialog {
position: relative;
--el-text-color-regular: black;
}
</style>
<style lang="scss" scoped>
.el-card {
--el-card-padding: 15px;
@ -21,11 +40,13 @@
</style>
<script lang="ts" setup>
import ClubList from '@/components/club/ClubList.vue';
import { UserPermissionId } from '@/router/permissions';
import ClubListItem from '@/components/club/ClubListItem.vue';
import type { Club } from '@/schemas/response';
import { useUserStore } from '@/stores/user';
import { storeToRefs } from 'pinia';
import { ref } from 'vue';
const userStore = useUserStore();
const { userInfo } = storeToRefs(userStore);
const showingClub = ref<Club>();
const showClubDialog = ref(false);
function onClubClick(club: Club) {

View File

@ -1,3 +1,8 @@
<template>
<div>123</div>
<el-main>
<manage-breadcrumb />
</el-main>
</template>
<script setup lang="ts">
import ManageBreadcrumb from '@/components/manage/ManageBreadcrumb.vue';
</script>