完成大作业部分及其打磨

This commit is contained in:
li-chx 2025-05-11 17:22:21 +08:00
parent 3ab2c26776
commit 0622b15509
23 changed files with 651 additions and 160 deletions

View File

@ -6,7 +6,9 @@
<script setup lang="ts">
const colorMode = useColorMode();
useHead({
title: `Lichx's WebClass`,
});
onMounted(() => {
watchEffect(() => {
if (colorMode.value === 'dark') {

View File

@ -0,0 +1,55 @@
<script setup lang="ts">
import BackButton from '~/components/BackButton.vue';
import { MdEditor } from 'md-editor-v3';
import 'md-editor-v3/lib/style.css';
const emit = defineEmits(['articleEditFinish']);
const { baseContent, baseTitle, saveButton } = defineProps({
baseContent: {
type: String,
default: '',
},
baseTitle: {
type: String,
default: '',
},
saveButton: {
type: String,
default: '保存文章',
},
});
const content = ref(baseContent);
const title = ref(baseTitle);
async function saveArticle() {
if (title.value === '' || content.value === '') {
await MessagePlugin.error('标题和正文不能为空');
return;
}
emit('articleEditFinish', {
title: title.value,
content: content.value,
});
}
</script>
<template>
<div class="p-6">
<back-button class="ml-3" />
<div
class="m-2 bg-white rounded-xl p-6 border-gray-200 border-2 min-h-[300px] dark:bg-neutral-800 dark:border-zinc-600"
>
<t-form @submit="saveArticle">
<t-form-item name="title" label="标题">
<t-input v-model="title"></t-input>
</t-form-item>
<t-form-item name="content" label="正文">
<MdEditor v-model="content" :theme="useColorMode().preference"/>
</t-form-item>
<t-form-item>
<t-button theme="primary" type="submit">{{ saveButton }}</t-button>
</t-form-item>
</t-form>
</div>
</div>
</template>

View File

@ -0,0 +1,7 @@
<template>
<div>
<t-button theme="primary" variant="base" @click="() => useRouter().back()">
<t-icon name="arrow-left" size="22px" />返回
</t-button>
</div>
</template>

View File

@ -0,0 +1,116 @@
<script setup lang="tsx">
import { ExampleUserInfo } from '~/stores/user';
const { avatarUrl, showWelcome, data } = defineProps({
avatarUrl: {
type: String,
default: '',
},
showWelcome: {
type: Boolean,
default: true,
},
data: {
type: ExampleUserInfo,
default: () => ({}),
},
});
function doShow(x: string) {
const notShow = [
'userAvatarPath',
'userAvatarUrl',
'userId',
'usernameOrEmail',
'lastGetTime',
];
for (const i of notShow) {
if (x === i) return false;
}
return true;
}
function toChinese(x: string) {
const map = {
userName: '用户名',
userId: '用户ID',
userEmail: '邮箱',
phone: '手机号',
lastGetTime: '最后登录时间',
userAvatar: '头像',
userAmount: '余额',
userBirthday: '生日',
};
return map[x as keyof typeof map];
}
function getIcon(key: string) {
const map = {
userName: 'user',
userEmail: 'mail',
userAmount: 'money',
userBirthday: 'cake',
};
const val = map[key as keyof typeof map];
return <t-icon v-if="" class="t-menu__operations-icon" name={val} />;
}
</script>
<template>
<div class="p-6">
<div class="m-2">
<slot name="header"></slot>
</div>
<div class="flex md:flex-row flex-col">
<div
class="flex-[1] m-2 h-full bg-white rounded-xl border-gray-200 border-2 min-h-[300px] min-w-48 dark:bg-zinc-800 dark:border-zinc-600"
>
<div class="flex flex-row md:flex-col justify-center p-4">
<t-image
:src="avatarUrl"
alt="头像"
:fit="'contain'"
shape="circle"
class="max-h-72 max-w-72 m-auto"
/>
<span
v-if="showWelcome"
class="text-2xl ml-auto mr-auto md:mt-6 mt-20 mb-6"
>欢迎 {{ data.userName }}
</span>
</div>
</div>
<div
class="flex-[2] m-2 bg-white rounded-xl border-gray-200 border-2 flex justify-center pt-8 min-w-[558px] dark:bg-[rgb(36,36,36)] dark:border-zinc-600"
>
<t-card
title="个人信息"
class="w-10/12 max-w-[700px]"
:bordered="false"
>
<t-row v-for="(value, key) of data" :key="key" class="w-full">
<div
v-if="doShow(key)"
class="border-collapse flex flex-row w-full h-10"
>
<t-col
class="flex-[1] flex justify-center border-2 items-center dark:border-zinc-600"
>{{ toChinese(key) }}</t-col
>
<t-col
class="flex-[2] border-2 flex items-center dark:border-zinc-600"
>
<span class="ml-4">
<component :is="getIcon(key)" />
{{ value }}
</span>
</t-col>
</div>
</t-row>
</t-card>
</div>
</div>
<slot />
</div>
</template>
<style scoped lang="less"></style>

View File

@ -13,16 +13,11 @@
const pagination = defineModel<TableProps['pagination']>('pagination');
async function onPageChange(pageInfo: { current: number; pageSize: number }) {
console.log('Page change');
console.log(pageInfo);
emit('pageChange', pageInfo);
}
function onRowClick(x: RowEventContext<TableRowData>) {
emit('onRowClick', x);
}
function onSelectChange() {
console.log('Select change');
}
</script>
<template>
@ -37,7 +32,6 @@
:lazy-load="true"
:loading="loading"
@page-change="onPageChange"
@select-change="onSelectChange"
@row-click="onRowClick"
>
</t-table>

View File

@ -12,12 +12,7 @@
);
const store = useUserStore();
const modeName = ref('sunny');
// watch()
// computed(())
watch(useWindowSize().width, (val) => {
console.log(val);
console.log(collapsed.value);
console.log(manualCollapsed.value);
setCollapsed(val);
});
function setCollapsed(val: number) {
@ -42,8 +37,6 @@
function changeMode() {
const x = useColorMode();
console.log(x.preference);
console.log(x.value);
if (x.preference === 'light') {
x.preference = 'dark';
modeName.value = 'moon';
@ -56,7 +49,6 @@
}
}
function changeTheme() {
console.log(theme.theme);
if (theme.theme === 'default') {
theme.setTheme('test');
} else if (theme.theme === 'test') {
@ -98,7 +90,7 @@
</script>
<template>
<t-layout class="h-full">
<t-layout class="h-screen w-screen">
<t-header>
<t-head-menu value="item1" height="120px">
<span class="text-xl">李晨鑫 Web 实验</span>
@ -203,8 +195,8 @@
</template>
</t-menu>
</t-aside>
<t-layout>
<t-content class="overflow-auto">
<t-layout class="overflow-auto">
<t-content>
<NuxtPage />
</t-content>
<t-footer class="m-auto">
@ -221,10 +213,12 @@
:deep(.t-layout__sider) {
@apply w-16;
}
height: calc(100vh - 56px);
}
.non-collapsed {
:deep(.t-layout__sider) {
@apply w-60;
}
height: calc(100vh - 56px);
}
</style>

View File

@ -0,0 +1,24 @@
<script setup lang="ts">
import { MdPreview } from 'md-editor-v3';
import 'md-editor-v3/lib/style.css';
import BackButton from '~/components/BackButton.vue';
const store = useArticleStore();
const content = store.currentArticle.content;
const title = store.currentArticle.title;
</script>
<template>
<div class="p-6">
<back-button class="ml-3" />
<div
class="m-2 bg-white rounded-xl p-6 border-gray-200 border-2 min-h-[300px] dark:bg-black dark:border-zinc-600"
>
<span class="text-4xl">{{ title }}</span>
<MdPreview
v-model="content"
:theme="useColorMode().preference"
class="mt-6"
></MdPreview>
</div>
</div>
</template>

View File

@ -0,0 +1,23 @@
<script setup lang="ts">
const store = useArticleStore();
const content = ref(store.currentArticle.content);
const title = ref(store.currentArticle.title);
async function saveArticle(data: { title: string; content: string }) {
store.currentArticle.title = data.title;
store.currentArticle.content = data.content;
await store.modifyArticle();
useRouter().back();
}
</script>
<template>
<ArticleEditor
:base-content="content"
:base-title="title"
:save-button="'更改文章'"
@article-edit-finish="saveArticle"
/>
</template>
<style scoped lang="less"></style>

View File

@ -1,11 +1,125 @@
<script setup lang="ts">
<script setup lang="tsx">
import SuperTable from '~/components/SuperTable.vue';
import * as echarts from 'echarts';
import type { TableProps, TableRowData } from 'tdesign-vue-next';
import { useWindowSize } from '@vueuse/core';
const store = useArticleStore();
const { pagination, articleList } = storeToRefs(store);
const columns = ref<TableProps['columns']>([
{
colKey: 'index',
title: '序号',
cell: (_, { rowIndex }: { rowIndex: number }) => (
<div>
{(pagination.value!.current! - 1) * pagination.value!.pageSize! +
rowIndex +
1}
</div>
),
},
{
colKey: 'name',
title: '作者姓名',
},
{
colKey: 'amount',
title: '文章数量',
},
{
colKey: 'operator',
title: '操作',
cell: (_, { row }) => (
<div class="w-40 flex flex-row justify-evenly">
<t-button onClick={() => manageArticle(row)}>进入文章管理</t-button>
</div>
),
},
]);
function pageChange(x: { current: number; pageSize: number }) {
if (pagination.value) {
pagination.value.current = x.current;
pagination.value.pageSize = x.pageSize;
store.getArticleList();
}
}
function manageArticle(row: TableRowData) {
const router = useRouter();
router.push(`/base/articleManage/${row.id}`);
// router.push()
}
onMounted(async () => {
await store.getTotal();
await store.getArticleList();
const chartDom = document.getElementById('echarts')!;
const chart = echarts.init(chartDom);
const option = {
title: {
text: '柱状图示例',
left: 'center',
},
tooltip: {},
xAxis: {
type: 'category',
data: articleList.value.map((item: ArticleUser) => item.name),
},
yAxis: {
type: 'value',
},
series: [
{
data: articleList.value.map((item: ArticleUser) => item.amount),
type: 'bar',
itemStyle: {
color: (params: { dataIndex: number }) => {
const colors = [
'#f1dbc3',
'#7493a8',
'#edc5ac',
'#cdaba2',
'#abb0b3',
'#cfc2b9',
'#a2979f',
];
return colors[params.dataIndex % colors.length];
},
},
},
],
};
chart.setOption(option);
watch(useWindowSize().width, (_val) => {
chart.resize();
});
});
</script>
<template>
<div class="p-6">
<div class="flex md:flex-row flex-col">
<div
class="flex-[1] m-2 p-6 bg-white rounded-xl dark:bg-zinc-800 border-2 dark:border-zinc-600"
>
<SuperTable
v-model:pagination="pagination"
:data="articleList"
:columns="columns"
:loading="false"
@page-change="pageChange"
/>
</div>
<div
class="m-2 flex-[1] p-6 bg-white dark:bg-zinc-800 border-2 dark:border-zinc-600 rounded-xl"
>
<div id="echarts" class="w-full h-96"></div>
</div>
</div>
</div>
</template>
<style scoped lang="less">
</style>
<style scoped lang="less"></style>

View File

@ -5,9 +5,8 @@
try {
avatarURL.value = (await useUserStore().getAvatarURL())!;
} catch (e) {
console.log(e);
await MessagePlugin.error(e as string);
}
console.log(avatarURL.value);
});
</script>
@ -37,5 +36,3 @@
</div>
</div>
</template>
<style scoped lang="less"></style>

View File

@ -1,99 +1,15 @@
<script setup lang="tsx">
useSystemStore().currentPage = 'home';
<script setup lang="ts">
import BasicInfo from '~/components/BasicInfo.vue';
const avatarURL = ref('');
useSystemStore().currentPage = 'home';
onMounted(async () => {
try {
avatarURL.value = (await useUserStore().getAvatarURL())!;
} catch (e) {
console.log(e);
await MessagePlugin.error(e as string);
}
console.log(avatarURL.value);
});
function doShow(x: string) {
const notShow = ['userAvatar', 'userId', 'usernameOrEmail', 'lastGetTime'];
for (const i of notShow) {
if (x === i) return false;
}
return true;
}
function toChinese(x: string) {
const map = {
userName: '用户名',
userId: '用户ID',
userEmail: '邮箱',
phone: '手机号',
lastGetTime: '最后登录时间',
userAvatar: '头像',
userAmount: '余额',
userBirthday: '生日',
};
return map[x as keyof typeof map];
}
function getIcon(key: string) {
const map = {
userName: 'user',
userEmail: 'mail',
userAmount: 'money',
userBirthday: 'cake',
};
const val = map[key as keyof typeof map];
return <t-icon v-if="" class="t-menu__operations-icon" name={val} />;
}
</script>
<template>
<div class="p-6">
<div class="flex md:flex-row flex-col">
<div
class="flex-[1] m-2 h-full bg-white rounded-xl border-gray-200 border-2 min-h-[300px] min-w-48"
>
<div class="flex flex-row md:flex-col justify-center p-4">
<t-image
:src="avatarURL"
alt="头像"
:fit="'contain'"
shape="circle"
class="max-h-72 max-w-72 m-auto"
/>
<span class="text-2xl ml-auto mr-auto md:mt-6 mt-20 mb-6"
>欢迎 {{ useUserStore().userName }}
</span>
</div>
</div>
<div
class="flex-[2] m-2 bg-white rounded-xl border-gray-200 border-2 flex justify-center pt-8 min-w-[558px]"
>
<t-card
title="个人信息"
class="w-10/12 max-w-[700px]"
:bordered="false"
>
<t-row
v-for="(value, key) of useUserStore().$state"
:key="key"
class="w-full"
>
<div
v-if="doShow(key)"
class="border-collapse flex flex-row w-full h-10"
>
<t-col
class="flex-[1] flex justify-center border-2 items-center"
>{{ toChinese(key) }}</t-col
>
<t-col class="flex-[2] border-2 flex items-center">
<span class="ml-4">
<component :is="getIcon(key)" />
{{ value }}
</span>
</t-col>
</div>
</t-row>
</t-card>
</div>
</div>
</div>
<BasicInfo :avatar-url="avatarURL" :data="useUserStore().$state" />
</template>
<style scoped lang="less"></style>

View File

@ -45,7 +45,6 @@
for (const [key, value] of Object.entries(cityDataTyped['00']!)) {
if (value === name) {
provinceIndex.value = key;
console.log('finish');
return;
}
}

View File

@ -2,7 +2,7 @@
import SuperTable from '~/components/SuperTable.vue';
import type { TableProps, TableRowData } from 'tdesign-vue-next';
import PeopleForm from '~/pages/base/index/peopleManage/components/PeopleForm.vue';
import { TButton } from '#components';
const store = usePeopleStore();
const { peopleList, pagination, situation } = storeToRefs(usePeopleStore());
@ -46,12 +46,23 @@
{
colKey: 'operator',
title: '操作',
cell: (_, { row }) => (
<div class="w-40 flex flex-row justify-evenly">
<t-button onClick={() => editUser(row)}>编辑</t-button>
<t-button onClick={() => deleteUser(row)}>删除</t-button>
</div>
),
cell: (_, { row }) =>
h('div', { class: 'w-40 flex flex-row justify-evenly' }, [
h(
TButton,
{
onClick: () => editUser(row),
},
{ default: () => '编辑' },
),
h(
TButton,
{
onClick: () => showDeleteUserDialog(row),
},
{ default: () => '删除' },
),
]),
},
]);
@ -63,29 +74,34 @@
await store.getTotal();
});
function editUser(_row: TableRowData) {
function editUser(row: TableRowData) {
if (form.value) form.value.resetForm();
store.currentForm = {
...peopleList.value.find((e) => e.id === _row.id)!,
...peopleList.value.find((e) => e.id === row.id)!,
} as PeopleInfo;
console.log(store.currentForm.province);
form.value?.setProvinceIndexByName(store.currentForm.province);
console.log(store.currentForm);
store.showEditPeople = true;
}
function deleteUser(_row: TableRowData) {
store.deletePeople(_row.id);
console.log(_row);
const showDeleteDialog = ref(false);
let deleteRowId = 0;
function showDeleteUserDialog(row: TableRowData) {
deleteRowId = row.id;
showDeleteDialog.value = true;
}
function pageChange(x: { current: number; pageSize: number }) {
function deleteUser() {
showDeleteDialog.value = false;
store.deletePeople(deleteRowId);
}
async function pageChange(x: { current: number; pageSize: number }) {
if (pagination.value) {
pagination.value.current = x.current;
pagination.value.pageSize = x.pageSize;
store.getPeopleList();
await store.getPeopleList();
}
console.log(store.pagination);
}
async function doSearch() {
@ -147,6 +163,15 @@
<people-form ref="form" />
</template>
</t-dialog>
<t-dialog
v-model:visible="showDeleteDialog"
header="你确定吗?"
width="40%"
:confirm-on-enter="true"
:on-confirm="deleteUser"
>
此操作将永久删除该人员确定吗
</t-dialog>
</div>
</template>

View File

@ -10,6 +10,10 @@
await router.push('/base/home');
}
}
onMounted(() => {
loginForm.value.usernameOrEmail = '';
loginForm.value.password = '';
});
</script>
<template>
@ -47,7 +51,7 @@
class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 rounded-full border-4 border-gray-200 w-10 h-10 bg-loginBG"
>
<span
class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 z-1"
class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 z-1 dark:text-black"
>or
</span>
</div>

View File

@ -70,7 +70,7 @@
:label-width="10"
:data="registerForm"
:rules="rules"
@submit="() => store.register()"
@submit="() => store.register(backToLogin)"
>
<t-form-item name="email">
<div class="flex flex-row items-center gap-2">

View File

@ -52,7 +52,7 @@
:data="resetForm"
:rules="rules"
class="flex flex-col items-center gap-4"
@submit="store.reset()"
@submit="store.reset(backToLogin)"
>
<div
class="flex md:flex-row items-center md:justify-center w-full flex-col gap-4 md:gap-0"

View File

@ -67,6 +67,5 @@ export const $http: HTTP = {
};
export default defineNuxtPlugin((nuxtApp) => {
console.log('$http', $http);
nuxtApp.provide('http', $http);
});

197
app/stores/articleUser.ts Normal file
View File

@ -0,0 +1,197 @@
export type Article = {
id: number;
title: string;
mainIdea: string;
content: string;
};
export type ArticleUser = {
id: number;
name: string;
amount: number;
};
type ArticleStore = {
articleList: ArticleUser[];
pagination: BasicPagination;
currentManageUser: UserInfo;
currentUserAvatar: string;
currentUserLastGetTime: Date;
currentUserArticleList: Article[];
currentUserPagination: BasicPagination;
currentArticle: Article;
};
export const useArticleStore = defineStore('Article', {
state: (): ArticleStore => ({
articleList: [],
pagination: {
current: 1,
pageSize: 5,
defaultPageSize: 5,
total: 0,
defaultCurrent: 1,
},
currentManageUser: {
userId: '',
userName: '',
userEmail: '',
userAvatarPath: '',
userAmount: '',
userBirthday: '',
},
currentUserAvatar: '',
currentUserLastGetTime: new Date(),
currentUserArticleList: [],
currentUserPagination: {
current: 1,
pageSize: 5,
defaultPageSize: 5,
total: 0,
defaultCurrent: 1,
},
currentArticle: {
id: 0,
title: '',
mainIdea: '',
content: '',
},
}),
actions: {
async modifyArticle() {
const { $http } = useNuxtApp();
try {
await $http.POST('/article/updateArticle', {
...this.currentArticle,
author: this.currentManageUser.userName,
});
this.getCurrentManageUserArticleList();
} catch (error) {
await MessagePlugin.error('Error adding article:' + error);
}
},
async deleteArticle(id: number) {
const { $http } = useNuxtApp();
try {
await $http.GET('/article/deleteArticle', {
id: '' + id,
});
this.getCurrentManageUserArticleList();
} catch (error) {
await MessagePlugin.error('Error deleting article:' + error);
}
},
async addNewArticle() {
const { $http } = useNuxtApp();
try {
await $http.POST('/article/addArticle', {
...this.currentArticle,
author: this.currentManageUser.userName,
});
this.getCurrentManageUserArticleList();
} catch (error) {
await MessagePlugin.error('Error adding article:' + error);
}
},
async getCurrentManageUserArticleList() {
const { $http } = useNuxtApp();
try {
const res = await $http.GET<{ records: Article[] }>(
`/article/page/${this.currentManageUser.userName}`,
{
page: '' + this.pagination!.current,
size: '' + this.pagination!.pageSize,
},
);
this.currentUserArticleList = res.data.records;
} catch (error) {
await MessagePlugin.error('Error fetching people list:' + error);
}
},
async getCurrentManageUserArticleTotal() {
const { $http } = useNuxtApp();
try {
const res = await $http.GET<number>(
`/article/total/${this.currentManageUser.userName}`,
);
console.log(res.data);
this.currentUserPagination.total = res.data;
if (
(this.currentUserPagination!.current - 1) *
this.currentUserPagination!.pageSize >
this.currentUserPagination!.total
) {
this.currentUserPagination!.current = Math.ceil(
this.currentUserPagination!.total /
this.currentUserPagination!.pageSize,
);
}
} catch (error) {
await MessagePlugin.error('Error fetching article total:' + error);
}
},
async getCurrentManageUserAvatar(forceUpdate: boolean = false) {
const now = new Date();
if (
!forceUpdate &&
this.currentUserAvatar !== '' &&
now.getTime() - this.currentUserLastGetTime.getTime() < 1000 * 60 * 9
) {
return this.currentUserAvatar;
}
const { $http } = useNuxtApp();
try {
const res = await $http.GET<string>('/article/userAvatar', {
userId: this.currentManageUser.userId,
});
this.currentUserAvatar = res.data;
return this.currentUserAvatar;
} catch (error) {
await MessagePlugin.error(
'Error fetching article user avatar:' + error,
);
}
},
async getCurrentManageUserInfo(userId: string) {
const { $http } = useNuxtApp();
try {
const res = await $http.GET<UserInfo>('/article/userInfo', {
userId: userId,
});
this.currentManageUser = res.data;
} catch (error) {
await MessagePlugin.error('Error fetching article user info:' + error);
}
},
async getArticleList() {
const { $http } = useNuxtApp();
try {
const res = await $http.GET<{ records: ArticleUser[] }>(
'/article/page',
{
page: '' + this.pagination!.current,
size: '' + this.pagination!.pageSize,
},
);
this.articleList = res.data.records;
} catch (error) {
await MessagePlugin.error('Error fetching article list:' + error);
}
},
async getTotal() {
const { $http } = useNuxtApp();
try {
const res = await $http.GET<number>('/article/total');
this.pagination.total = res.data;
if (
(this.pagination!.current - 1) * this.pagination!.pageSize >
this.pagination!.total
) {
this.pagination!.current = Math.ceil(
this.pagination!.total / this.pagination!.pageSize,
);
this.getArticleList();
}
} catch (error) {
await MessagePlugin.error('Error fetching article total:' + error);
}
},
},
});

View File

@ -79,7 +79,7 @@ export const useLoginStore = defineStore('Login', {
email: email,
});
} catch (e) {
console.log(e);
await MessagePlugin.error(e as string);
return false;
}
if (result.code === 200) {
@ -106,19 +106,20 @@ export const useLoginStore = defineStore('Login', {
}
return false;
},
async register() {
async register(recallWhenSuccess: () => void) {
const { $http } = useNuxtApp();
try {
await $http.POST('/login/register', this.registerForm);
recallWhenSuccess();
} catch {
return;
}
},
async reset() {
console.log(this.resetForm);
async reset(recallWhenSuccess: () => void) {
const { $http } = useNuxtApp();
try {
await $http.POST('/login/passwordReset', this.resetForm);
recallWhenSuccess();
} catch {
return;
}
@ -144,7 +145,6 @@ export const useLoginStore = defineStore('Login', {
case PageType.reset:
return '重置密码';
default:
console.log('未知');
return '未知';
}
},

View File

@ -58,9 +58,8 @@ export const usePeopleStore = defineStore('People', {
},
);
this.peopleList = res.data.records;
console.log(this.peopleList);
} catch (error) {
console.error('Error fetching people list:', error);
await MessagePlugin.error('Error fetching people list:' + error);
}
},
async getTotal() {
@ -79,12 +78,11 @@ export const usePeopleStore = defineStore('People', {
);
this.getPeopleList();
}
} catch {
return;
} catch (error) {
await MessagePlugin.error('Error fetching people total:' + error);
}
},
async addPeople() {
console.log('add');
const { $http } = useNuxtApp();
try {
await $http.POST('/userManager/addUser', this.currentForm);

View File

@ -2,36 +2,45 @@ export interface UserInfo {
userId: string;
userName: string;
userEmail: string;
userAvatar: string;
userAvatarPath: string;
userAmount: string;
userBirthday: string;
lastGetTime: Date;
}
export class ExampleUserInfo implements UserInfo {
userId = '';
userName = '';
userEmail = '';
userAvatarPath = '';
userAmount = '0';
userBirthday = '';
}
export interface UserState extends UserInfo {
lastGetTime: Date;
usernameOrEmail: string;
userAvatarUrl: string;
}
export const useUserStore = defineStore('User', {
state: (): UserState => ({
usernameOrEmail: '',
userId: '',
userName: '',
userEmail: '',
userAvatar: '',
userAvatarPath: '',
userAmount: '',
userBirthday: '',
lastGetTime: new Date(),
usernameOrEmail: '',
userAvatarUrl: '',
}),
actions: {
async getUserInfo() {
const { $http } = useNuxtApp();
try {
const res = await $http.GET<UserInfo>('/user/info');
console.log(res.data);
this.userId = res.data.userId;
this.userName = res.data.userName;
this.userEmail = res.data.userEmail;
this.userAvatar = res.data.userAvatar;
this.userAmount = '500';
this.userAvatarPath = res.data.userAvatarPath;
this.userAmount = res.data.userAmount;
this.userBirthday = res.data.userBirthday;
this.lastGetTime = new Date();
} catch (error) {
@ -40,14 +49,23 @@ export const useUserStore = defineStore('User', {
},
async getAvatarURL() {
const now = new Date();
if (now.getTime() - this.lastGetTime.getTime() < 1000 * 60 * 9) {
return this.userAvatar;
console.log('Current time:', now);
if (
this.userAvatarUrl !== '' &&
now.getTime() - this.lastGetTime.getTime() < 1000 * 60 * 9
) {
console.log('Using cached avatar URL');
console.log('Cached avatar URL:', this.userAvatarUrl);
return this.userAvatarUrl;
}
console.log('Fetching new avatar URL');
this.lastGetTime = now;
const { $http } = useNuxtApp();
try {
const res = await $http.GET<string>('/user/avatar');
this.userAvatar = res.data;
this.userAvatarUrl = res.data;
console.log('User avatar URL:', this.userAvatarUrl);
return this.userAvatarUrl;
} catch (error) {
console.error('Error fetching user avatar:', error);
}

View File

@ -8,6 +8,7 @@ export {}
declare global {
const APIResponse: typeof import('../utils/APIResponse')['APIResponse']
const APIResponseErrorException: typeof import('../utils/APIResponse')['APIResponseErrorException']
const ExampleUserInfo: typeof import('../stores/user')['ExampleUserInfo']
const PageType: typeof import('../stores/login')['PageType']
const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate']
const createPinia: typeof import('pinia')['createPinia']
@ -21,6 +22,7 @@ declare global {
const setActivePinia: typeof import('pinia')['setActivePinia']
const setMapStoreSuffix: typeof import('pinia')['setMapStoreSuffix']
const storeToRefs: typeof import('pinia')['storeToRefs']
const useArticleStore: typeof import('../stores/articleUser')['useArticleStore']
const useLoginStore: typeof import('../stores/login')['useLoginStore']
const usePeopleStore: typeof import('../stores/people')['usePeopleStore']
const useSystemStore: typeof import('../stores/system')['useSystemStore']
@ -29,6 +31,9 @@ declare global {
}
// for type re-export
declare global {
// @ts-ignore
export type { Article, ArticleUser } from '../stores/articleUser'
import('../stores/articleUser')
// @ts-ignore
export type { PageType, LoginStore } from '../stores/login'
import('../stores/login')
@ -39,7 +44,7 @@ declare global {
export type { SystemStore } from '../stores/system'
import('../stores/system')
// @ts-ignore
export type { UserInfo, UserState } from '../stores/user'
export type { ExampleUserInfo, UserInfo, UserState } from '../stores/user'
import('../stores/user')
// @ts-ignore
export type { APIResponse, APIResponseErrorException, IAPIResponse } from '../utils/APIResponse'
@ -53,6 +58,7 @@ declare module 'vue' {
interface ComponentCustomProperties {
readonly APIResponse: UnwrapRef<typeof import('../utils/APIResponse')['APIResponse']>
readonly APIResponseErrorException: UnwrapRef<typeof import('../utils/APIResponse')['APIResponseErrorException']>
readonly ExampleUserInfo: UnwrapRef<typeof import('../stores/user')['ExampleUserInfo']>
readonly PageType: UnwrapRef<typeof import('../stores/login')['PageType']>
readonly acceptHMRUpdate: UnwrapRef<typeof import('pinia')['acceptHMRUpdate']>
readonly createPinia: UnwrapRef<typeof import('pinia')['createPinia']>
@ -66,6 +72,7 @@ declare module 'vue' {
readonly setActivePinia: UnwrapRef<typeof import('pinia')['setActivePinia']>
readonly setMapStoreSuffix: UnwrapRef<typeof import('pinia')['setMapStoreSuffix']>
readonly storeToRefs: UnwrapRef<typeof import('pinia')['storeToRefs']>
readonly useArticleStore: UnwrapRef<typeof import('../stores/articleUser')['useArticleStore']>
readonly useLoginStore: UnwrapRef<typeof import('../stores/login')['useLoginStore']>
readonly usePeopleStore: UnwrapRef<typeof import('../stores/people')['usePeopleStore']>
readonly useSystemStore: UnwrapRef<typeof import('../stores/system')['useSystemStore']>

View File

@ -28,13 +28,15 @@
"@nuxtjs/tailwindcss": "^6.13.2",
"@pinia/nuxt": "^0.10.1",
"@unhead/vue": "^2.0.5",
"echarts": "^5.6.0",
"md-editor-v3": "^5.5.0",
"nuxt": "^3.16.2",
"pinia": "^3.0.2",
"tdesign-vue-next": "^1.11.5",
"unplugin-auto-import": "^19.1.2",
"unplugin-vue-components": "^28.4.1"
},
"packageManager": "pnpm@10.9.0",
"packageManager": "pnpm@10.10.0",
"pnpm": {
"onlyBuiltDependencies": [
"@parcel/watcher",