✨ 完成大作业部分及其打磨
This commit is contained in:
parent
3ab2c26776
commit
0622b15509
|
@ -6,7 +6,9 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
const colorMode = useColorMode();
|
||||
|
||||
useHead({
|
||||
title: `Lichx's WebClass`,
|
||||
});
|
||||
onMounted(() => {
|
||||
watchEffect(() => {
|
||||
if (colorMode.value === 'dark') {
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -45,7 +45,6 @@
|
|||
for (const [key, value] of Object.entries(cityDataTyped['00']!)) {
|
||||
if (value === name) {
|
||||
provinceIndex.value = key;
|
||||
console.log('finish');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -67,6 +67,5 @@ export const $http: HTTP = {
|
|||
};
|
||||
|
||||
export default defineNuxtPlugin((nuxtApp) => {
|
||||
console.log('$http', $http);
|
||||
nuxtApp.provide('http', $http);
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
|
@ -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 '未知';
|
||||
}
|
||||
},
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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']>
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Reference in New Issue