完成实验部分功能

This commit is contained in:
li-chx
2025-05-03 19:34:43 +08:00
parent 15c0a1de0e
commit eae8781eaa
21 changed files with 1693 additions and 215 deletions
+42 -21
View File
@@ -10,6 +10,7 @@
const lg = parseFloat(
config.theme.screens.lg.substring(0, config.theme.screens.lg.length - 2),
);
const store = useUserStore();
const modeName = ref('sunny');
// watch()
// computed(())
@@ -65,9 +66,23 @@
}
}
function backToLogin() {
let lastPage = '';
function toCalendar() {
const router = useRouter();
router.push('/login');
lastPage = currentPage.value;
currentPage.value = 'calendar';
router.push('/base/calendar');
}
function backToLastPage() {
const router = useRouter();
currentPage.value = lastPage;
router.back();
}
function backToLogin() {
useLoginStore().logout();
}
watch(currentPage, (val) => {
@@ -76,36 +91,36 @@
});
async function sendTest() {
await useLoginStore().login();
try {
// const response = await $fetch('/api/login/test', {
// method: 'GET',
// });
$fetch('/api/file/test', { method: 'GET' }).catch(() => {
const { $http } = useNuxtApp();
useNuxtApp().$http.GET('/test');
console.log($http); // 检查是否为 undefined
console.log($fetch);
$http.GET('/file/test').catch(() => {
console.log('error');
$fetch('/api/login/login', {
method: 'POST',
body: {
$http
.POST('/login/login', {
username: 'admin',
password: 'password',
},
}).then(async () => {
const result = await $fetch('/api/file/test', { method: 'GET' });
console.log(result);
});
})
.then(async () => {
const result = await $http.GET('/api/file/test');
console.log(result);
});
});
// const response = await $fetch('/api/login/login', {
// method: 'POST',
// body: {
// username: 'admin',
// password: 'password',
// },
// });
// console.log(response);
} catch (err) {
console.log(err);
}
}
onMounted(() => setCollapsed(useWindowSize().width.value));
onMounted(async () => {
await store.getUserInfo();
await store.getAvatarURL();
});
</script>
<template>
@@ -120,9 +135,15 @@
<a @click="changeTheme">
<t-icon class="t-menu__operations-icon" name="fill-color" />
</a>
<a @click="backToLogin">
<a v-if="currentPage != 'calendar'" @click="toCalendar">
<t-icon class="t-menu__operations-icon" name="calendar" />
</a>
<a v-else @click="backToLastPage">
<t-icon class="t-menu__operations-icon" name="rollback" />
</a>
<a @click="backToLogin">
<t-icon class="t-menu__operations-icon" name="logout" />
</a>
<t-button @click="sendTest" />
</template>
<!-- <template #logo>-->
@@ -248,7 +269,7 @@
<style scoped lang="less">
.collapsed {
:deep(.t-layout__sider) {
@apply w-20;
@apply w-16;
}
}
.non-collapsed {
+38
View File
@@ -0,0 +1,38 @@
<script setup lang="ts">
const avatarURL = ref('');
useSystemStore().currentPage = 'calendar';
onMounted(async () => {
try {
avatarURL.value = (await useUserStore().getAvatarURL())!;
} catch (e) {
console.log(e);
}
console.log(avatarURL.value);
});
</script>
<template>
<div class="p-6">
<div class="flex md:flex-row flex-col">
<div class="flex-[1] m-2 max-h-80">
<div class="flex flex-row md:flex-col justify-center">
<span class="text-2xl ml-auto mr-auto md:mt-6 mt-20 mb-6"
>欢迎 {{ useUserStore().userName }}
</span>
<t-image
:src="avatarURL"
alt="头像"
:fit="'contain'"
class="max-h-80 max-w-80 m-auto"
/>
</div>
</div>
<div class="flex-[2] m-2">
<t-calendar> </t-calendar>
</div>
</div>
</div>
</template>
<style scoped lang="less"></style>
+4 -2
View File
@@ -1,5 +1,5 @@
<script setup lang="ts">
const x = ref();
useSystemStore().currentPage = 'home';
</script>
<template>
@@ -21,7 +21,9 @@
</div>
<div class="flex-[2] m-2">
<t-calendar> </t-calendar>
<t-card>
<t-table> </t-table>
</t-card>
</div>
</div>
</div>
@@ -0,0 +1,141 @@
<script setup lang="ts">
import cityData from 'public/city.json';
import cityZip from 'public/zipcode_district_data.json';
import type {
FormInstanceFunctions,
FormProps,
SelectValue,
} from 'tdesign-vue-next';
const cityDataTyped = cityData as Record<string, Record<string, string>>;
const cityZipTyped = cityZip as Record<string, string | number>;
function getZipCode(city: string): string {
for (const cityName of [
city,
city.substring(0, city.length - 1),
city.substring(0, city.length - 3),
])
if (cityZipTyped[cityName] !== undefined) {
return '' + cityZipTyped[cityName];
}
return '';
}
const store = usePeopleStore();
const { currentForm } = storeToRefs(store);
const form = ref<FormInstanceFunctions>();
const provinceIndex = ref('');
async function submit() {
// 执行提交逻辑
if (!form.value) return;
const result = await form.value.validate();
if (typeof result === 'boolean' && result) form.value.submit();
}
function resetForm() {
if (form.value) form.value.reset();
}
function changeProvince(val: SelectValue) {
setProvinceIndexByName(val as string);
currentForm.value.city = '';
currentForm.value.zip = '';
}
function setProvinceIndexByName(name: string) {
for (const [key, value] of Object.entries(cityDataTyped['00']!)) {
if (value === name) {
provinceIndex.value = key;
console.log('finish');
return;
}
}
}
defineExpose({
submit,
resetForm,
setProvinceIndexByName,
});
const rules: FormProps['rules'] = {
date: [{ required: true, message: '请输入日期', trigger: 'blur' }],
name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
province: [
{ required: true, message: '请输入省份', trigger: 'submit' },
{ required: true, message: '请输入省份', trigger: 'change' },
],
city: [{ required: true, message: '请输入城市', trigger: 'submit' }],
address: [{ required: true, message: '请输入地址', trigger: 'blur' }],
zip: [{ required: true, message: '请输入邮编', trigger: 'blur' }],
};
</script>
<template>
<t-form
ref="form"
:rules="rules"
:data="currentForm"
@submit="
() => {
if (store.showAddPeople) store.addPeople();
else if (store.showEditPeople) store.updatePeople();
}
"
>
<t-form-item name="date" label="日期">
<t-date-picker
v-model="currentForm.date"
class="w-80"
placeholder="请输入日期"
/>
</t-form-item>
<t-form-item name="name" label="姓名">
<t-input v-model="currentForm.name" placeholder="请输入姓名" />
</t-form-item>
<t-form-item name="province" label="省份">
<t-select
v-model="currentForm.province"
placeholder="请输入省份"
@change="changeProvince"
>
<t-option
v-for="item of cityDataTyped['00']"
:key="item"
:label="item"
:value="item"
>
{{ item }}
</t-option>
</t-select>
</t-form-item>
<t-form-item name="city" label="城市">
<t-select
v-model="currentForm.city"
placeholder="请输入城市"
:disabled="currentForm.province === ''"
@change="
(x: SelectValue) => {
currentForm.zip = getZipCode(x as string);
}
"
>
<t-option
v-for="item of cityDataTyped[provinceIndex]"
:key="item"
:label="item"
:value="item"
>
{{ item }}
</t-option>
</t-select>
</t-form-item>
<t-form-item name="address" label="地址">
<t-input v-model="currentForm.address" placeholder="请输入地址" />
</t-form-item>
<t-form-item name="zip" label="邮编">
<t-input v-model="currentForm.zip" placeholder="请输入邮编" />
</t-form-item>
</t-form>
</template>
<style scoped lang="less"></style>
+91 -25
View File
@@ -1,16 +1,23 @@
<script setup lang="tsx">
import SuperTable from '~/components/SuperTable.vue';
import type { TableProps } from 'tdesign-vue-next';
import type { TableProps, TableRowData } from 'tdesign-vue-next';
import PeopleForm from '~/pages/base/index/peopleManage/components/PeopleForm.vue';
const store = usePeopleStore();
const { peopleList, pagination, situation } = storeToRefs(usePeopleStore());
const pagination = ref<TableProps['pagination']>({
defaultPageSize: 10,
total: 100,
defaultCurrent: 1,
});
const columns = ref<TableProps['columns']>([
{
colKey: 'index',
title: '序号',
cell: (_, { rowIndex }: { rowIndex: number }) => (
<div>
{(pagination.value!.current! - 1) * pagination.value!.pageSize! +
rowIndex +
1}
</div>
),
},
{
colKey: 'date',
@@ -33,54 +40,113 @@
title: '地址',
},
{
colKey: '',
colKey: 'zip',
title: '邮编',
},
{
colKey: 'operator',
title: '操作',
cell: (_, { row }) => (
<div>
<div class="w-40 flex flex-row justify-evenly">
<t-button onClick={() => editUser(row)}>编辑</t-button>
<t-button onClick={() => deleteUser(row)}>删除</t-button>
</div>
),
},
]);
const data = ref([]);
for (let i = 0; i < 30; i++) {
data.value.push({
index: i + 1,
date: '2023-10-01',
name: 'John Doe',
province: 'Province A',
city: 'City A',
address: 'Address A',
postalCode: '123456',
} as never);
onMounted(async () => {
await store.getPeopleList();
});
onMounted(async () => {
await store.getTotal();
});
function editUser(_row: TableRowData) {
if (form.value) form.value.resetForm();
store.currentForm = {
...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 editUser(row: never) {}
function deleteUser(_row: TableRowData) {
store.deletePeople(_row.id);
console.log(_row);
}
function deleteUser(row: never) {}
function pageChange(x: { current: number; pageSize: number }) {
if (pagination.value) {
pagination.value.current = x.current;
pagination.value.pageSize = x.pageSize;
store.getPeopleList();
}
console.log(store.pagination);
}
async function doSearch() {
await usePeopleStore().getTotal();
await usePeopleStore().getPeopleList();
}
const form: Ref<typeof PeopleForm | null> = ref(null);
</script>
<template>
<div class="p-6">
<div class="flex justify-between p-4">
<t-button>新增</t-button>
<t-input placeholder="搜索" clearable class="max-w-60">
<t-button
@click="
() => {
if (form) form.resetForm();
store.showAddPeople = true;
}
"
>添加新人员</t-button
>
<t-input
v-model="situation"
placeholder="搜索"
clearable
class="max-w-60"
@change="doSearch"
>
<template #suffixIcon>
<search-icon :style="{ cursor: 'pointer' }" />
</template>
</t-input>
</div>
<super-table
:data="data"
v-model:pagination="pagination"
:data="peopleList"
:columns="columns"
:loading="false"
:pagination="pagination"
@page-change="pageChange"
></super-table>
<t-dialog
:visible="store.showAddPeople || store.showEditPeople"
:close-btn="true"
:confirm-btn="store.showAddPeople ? '添加' : '修改'"
cancel-btn="取消"
@confirm="form?.submit"
@close="
() => {
store.showAddPeople = false;
store.showEditPeople = false;
}
"
>
<template #header>{{
store.showAddPeople ? '添加新人员' : '修改人员信息'
}}</template>
<template #body>
<people-form ref="form" />
</template>
</t-dialog>
</div>
</template>
+11 -3
View File
@@ -3,6 +3,13 @@
const store = useLoginStore();
const { currentType, loginForm } = storeToRefs(store);
async function login() {
if (await store.login()) {
useUserStore().usernameOrEmail = loginForm.value.usernameOrEmail;
const router = useRouter();
await router.push('/base/home');
}
}
</script>
<template>
@@ -47,17 +54,18 @@
</div>
<div class="flex flex-col items-center gap-4 mt-3 md:mt-0">
<TInput
v-model="loginForm.username"
v-model="loginForm.usernameOrEmail"
class="w-56"
placeholder="请输入用户名"
placeholder="请输入用户名 / 邮箱"
/>
<TInput
v-model="loginForm.password"
class="w-56"
type="password"
placeholder="请输入密码"
@enter="login"
/>
<TButton class="w-56" @click="store.login">登录</TButton>
<TButton class="w-56" @click="login">登录</TButton>
</div>
</div>
<div
+55 -17
View File
@@ -12,6 +12,9 @@
{ required: true, message: '请输入邮箱', trigger: 'blur' },
{ email: true, message: '请输入邮箱', trigger: 'change' },
],
identifyingCode: [
{ required: true, message: '请输入验证码', trigger: 'blur' },
],
username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
passwordRepeat: [
@@ -25,14 +28,36 @@
birth: [{ required: true, message: '请输入出生日期', trigger: 'change' }],
};
const uploadImage = ref<UploadInstanceFunctions>();
const formInstance = ref<FormInstanceFunctions>();
const form = ref<FormInstanceFunctions>();
const coldTime = ref(0);
const avatarURL = ref('');
async function submitForm() {
console.log(formInstance.value);
if (!formInstance.value) return;
const result = await formInstance.value.validate();
if (typeof result === 'boolean' && result) {
formInstance.value.submit();
if (!form.value) return;
const result = await form.value.validate();
if (registerForm.value.avatarFile === '') {
await MessagePlugin.error('请上传头像');
return;
}
if (typeof result === 'boolean' && result) {
form.value.submit();
}
}
async function sendIdentifyingCode() {
if (await store.sendRegisterIdentifyingCode()) {
coldTime.value = 60;
const timer: NodeJS.Timeout = setInterval(() => {
if (coldTime.value > 0) coldTime.value--;
else {
clearInterval(timer);
}
}, 1000);
}
}
function backToLogin() {
form.value?.reset();
registerForm.value.avatarFile = '';
currentType.value = PageType.login;
}
</script>
@@ -41,14 +66,25 @@
<div class="flex md:flex-row flex-col items-center gap-4 justify-around">
<div class="flex flex-col items-center gap-4 max-w-96">
<t-form
ref="formInstance"
ref="form"
:label-width="10"
:data="registerForm"
:rules="rules"
@submit="() => store.register()"
>
<t-form-item name="email">
<TInput v-model="registerForm.email" placeholder="请输入邮箱" />
<div class="flex flex-row items-center gap-2">
<TInput v-model="registerForm.email" placeholder="请输入邮箱" />
<TButton :disabled="coldTime > 0" @click="sendIdentifyingCode">{{
coldTime > 0 ? coldTime : '发送验证码'
}}</TButton>
</div>
</t-form-item>
<t-form-item name="identifyingCode">
<TInput
v-model="registerForm.identifyingCode"
placeholder="请输入验证码"
/>
</t-form-item>
<t-form-item name="username">
<TInput
@@ -88,24 +124,28 @@
:draggable="true"
:format-response="
(res) => {
res = res as IAPIResponse<string>;
res = res as IAPIResponse<{
avatarFilePath: string;
avatarFileURL: string;
}>;
if (res.code === 200) {
registerForm.avatarURL = res.data;
return { url: res.data };
registerForm.avatarFile = res.data.avatarFilePath;
avatarURL = res.data.avatarFileURL;
return { url: res.data.avatarFileURL };
}
return { fail: res.message };
}
"
>
<div v-if="registerForm.avatarURL" class="aspect-square h-full">
<div v-if="registerForm.avatarFile" class="aspect-square h-full">
<t-image
:src="registerForm.avatarURL"
:src="avatarURL"
class="w-40 h-40 object-cover rounded-md"
fit="contain"
/>
</div>
<div v-else>
<span class="text-blue-400"> 点击上传头像</span>
<span class="text-blue-400">点击上传头像</span>
<span> / 拖拽到此区域 </span>
</div>
</TUpload>
@@ -115,9 +155,7 @@
class="flex flex-row items-center bg-gray-600 w-full h-10 justify-around rounded-b-md mt-4"
>
<button class="text-white" @click="submitForm">注册用户</button>
<button class="text-white" @click="currentType = PageType.login"
>返回登录
</button>
<button class="text-white" @click="backToLogin">返回登录 </button>
</div>
</div>
</template>
+101 -31
View File
@@ -1,47 +1,117 @@
<script setup lang="ts">
import type { FormInstanceFunctions, FormProps } from 'tdesign-vue-next';
const store = useLoginStore();
const { currentType } = storeToRefs(store);
const { currentType, resetForm } = storeToRefs(store);
const form = ref<FormInstanceFunctions>();
const rules: FormProps['rules'] = {
usernameOrEmail: [
{ required: true, message: '请输入邮箱或用户名', trigger: 'blur' },
],
identifyingCode: [
{ required: true, message: '请输入验证码', trigger: 'blur' },
],
password: [{ required: true, message: '请输入新密码', trigger: 'blur' }],
passwordRepeat: [
{ required: true, message: '请再次输入密码', trigger: 'blur' },
{
validator: (val) => val === resetForm.value.password,
message: '两次输入密码不一致',
trigger: 'change',
},
],
};
async function submit() {
if (!form.value) return;
const result = await form.value.validate();
if (typeof result === 'boolean' && result) form.value.submit();
}
const coldTime = ref(0);
async function sendIdentifyingCode() {
if (await store.sendResetIdentifyingCode()) {
coldTime.value = 60;
const timer: NodeJS.Timeout = setInterval(() => {
if (coldTime.value > 0) coldTime.value--;
else {
clearInterval(timer);
}
}, 1000);
}
}
function backToLogin() {
form.value?.reset();
currentType.value = PageType.login;
}
</script>
<template>
<div class="w-full">
<div
class="flex md:flex-row items-center md:justify-center w-full flex-col gap-4"
<t-form
ref="form"
:label-width="10"
:data="resetForm"
:rules="rules"
class="flex flex-col items-center gap-4"
@submit="store.reset()"
>
<div class="flex flex-col items-center gap-4">
<!-- <TInput class="w-56" placeholder="请输入邮箱" />-->
<!-- <TButton class="w-56">发送验证码</TButton>-->
<!-- <TInput class="w-56" placeholder="请输入验证码" />-->
<!-- <TInput class="w-56" type="password" placeholder="请输入新密码" />-->
<!-- <TButton class="w-56">确认</TButton>-->
<TInput class="w-56" placeholder="用户名" />
<TInput class="w-56" placeholder="旧密码" />
</div>
<div
class="hidden md:flex flex-col items-center gap-4 w-2/5 max-w-24 select-none"
class="flex md:flex-row items-center md:justify-center w-full flex-col gap-4 md:gap-0"
>
<div class="w-1 h-44 bg-gray-200"></div>
<!-- <div-->
<!-- class="hidden 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"-->
<!-- >-->
<!--&lt;!&ndash; <span&ndash;&gt;-->
<!--&lt;!&ndash; class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 z-1"&ndash;&gt;-->
<!--&lt;!&ndash; >or&ndash;&gt;-->
<!--&lt;!&ndash; </span>&ndash;&gt;-->
<!-- </div>-->
<div class="flex flex-col items-center gap-4 w-full">
<!-- <TInput class="w-56" placeholder="请输入邮箱" />-->
<!-- <TButton class="w-56">发送验证码</TButton>-->
<!-- <TInput class="w-56" placeholder="请输入验证码" />-->
<!-- <TInput class="w-56" type="password" placeholder="请输入新密码" />-->
<!-- <TButton class="w-56">确认</TButton>-->
<t-form-item name="usernameOrEmail" class="w-64 md:w-60">
<div class="flex flex-row items-center gap-2 w-full">
<TInput
v-model="resetForm.usernameOrEmail"
placeholder="邮箱 / 用户名"
class="max-w-32 md:max-w-28"
/>
<TButton
:disabled="coldTime > 0"
class="w-32"
@click="sendIdentifyingCode"
>{{ coldTime > 0 ? coldTime : '发送验证码' }}</TButton
>
</div>
</t-form-item>
<t-form-item name="identifyingCode" class="w-64 md:w-60">
<TInput v-model="resetForm.identifyingCode" placeholder="验证码" />
</t-form-item>
</div>
<div
class="hidden md:flex flex-col items-center gap-4 w-2/5 max-w-16 select-none"
>
<div class="w-1 h-44 bg-gray-200"></div>
</div>
<div class="flex flex-col items-center gap-4 w-full">
<t-form-item name="password" class="w-64 md:w-60">
<TInput v-model="resetForm.password" placeholder="新密码" />
</t-form-item>
<t-form-item name="passwordRepeat" class="w-64 md:w-60">
<TInput
v-model="resetForm.passwordRepeat"
class="w-56"
placeholder="确认新密码"
/>
</t-form-item>
</div>
</div>
<div class="flex flex-col items-center gap-4">
<TInput class="w-56" placeholder="新密码" />
<TInput class="w-56" placeholder="确认新密码" />
</div>
</div>
</t-form>
<div
class="flex flex-row items-center bg-gray-600 w-full h-10 justify-around rounded-b-md mt-4"
>
<button class="text-white" @click="store.register()">修改密码</button>
<button class="text-white" @click="currentType = PageType.login"
>返回登录
</button>
<button class="text-white" @click="submit">修改密码</button>
<button class="text-white" @click="backToLogin">返回登录 </button>
</div>
</div>
</template>
<style scoped lang="less">
:deep(.t-form__item) {
@apply mb-0;
}
</style>
-8
View File
@@ -6,14 +6,6 @@
const store = useLoginStore();
const { currentType } = storeToRefs(store);
// watch(currentType, (val) => {
// console.log(val);
// });
onMounted(() => {
// useThemeStore().setTheme('test');
// useThemeStore().setTheme('self');
});
</script>
<template>