Skip to content

Commit

Permalink
feat(projects): Syncronize user management list page (#18)
Browse files Browse the repository at this point in the history
  • Loading branch information
mufeng889 authored May 22, 2024
1 parent cc2562b commit 64be7d2
Show file tree
Hide file tree
Showing 3 changed files with 329 additions and 4 deletions.
4 changes: 2 additions & 2 deletions src/typings/api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ declare namespace Api {
/** user name */
userName: string;
/** user gender */
userGender: UserGender | null;
userGender: UserGender;
/** user nick name */
nickName: string;
/** user phone */
Expand All @@ -136,7 +136,7 @@ declare namespace Api {
}>;

/** user search params */
type UserSearchParams = CommonType.RecordNullable<
type UserSearchParams = Partial<
Pick<Api.SystemManage.User, 'userName' | 'userGender' | 'nickName' | 'userPhone' | 'userEmail' | 'status'> &
CommonSearchParams
>;
Expand Down
209 changes: 207 additions & 2 deletions src/views/manage/user/index.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,212 @@
<script setup lang="ts"></script>
<script setup lang="tsx">
import { Button, Popconfirm, Tag } from 'ant-design-vue';
import { computed, shallowRef } from 'vue';
import { useElementSize } from '@vueuse/core';
import { fetchGetUserList } from '@/service/api';
import { $t } from '@/locales';
import { enableStatusRecord, userGenderRecord } from '@/constants/business';
import { useTable, useTableOperate } from '@/hooks/common/table';
// import UserOperateDrawer from './modules/user-operate-drawer.vue';
import UserSearch from './modules/user-search.vue';
const wrapperEl = shallowRef<HTMLElement | null>(null);
const { height: wrapperElHeight } = useElementSize(wrapperEl);
const scrollConfig = computed(() => {
return {
y: wrapperElHeight.value - 72,
x: 702
};
});
const { columns, columnChecks, data, getData, loading, mobilePagination, searchParams, resetSearchParams } = useTable({
apiFn: fetchGetUserList,
apiParams: {
current: 1,
size: 10,
// if you want to use the searchParams in Form, you need to define the following properties, and the value is null
// the value can not be undefined, otherwise the property in Form will not be reactive
status: undefined,
userName: undefined,
userGender: undefined,
nickName: undefined,
userPhone: undefined,
userEmail: undefined
},
columns: () => [
{
key: 'index',
title: $t('common.index'),
dataIndex: 'index',
align: 'center',
width: 64
},
{
key: 'userName',
dataIndex: 'userName',
title: $t('page.manage.user.userName'),
align: 'center',
minWidth: 100
},
{
key: 'userGender',
title: $t('page.manage.user.userGender'),
align: 'center',
dataIndex: 'userGender',
width: 100,
customRender: ({ record }) => {
if (record.userGender === null) {
return null;
}
const tagMap: Record<Api.SystemManage.UserGender, string> = {
1: 'processing',
2: 'error'
};
const label = $t(userGenderRecord[record.userGender]);
return <Tag color={tagMap[record.userGender]}>{label}</Tag>;
}
},
{
key: 'nickName',
dataIndex: 'nickName',
title: $t('page.manage.user.nickName'),
align: 'center',
minWidth: 100
},
{
key: 'userPhone',
dataIndex: 'userPhone',
title: $t('page.manage.user.userPhone'),
align: 'center',
width: 120
},
{
key: 'userEmail',
dataIndex: 'userEmail',
title: $t('page.manage.user.userEmail'),
align: 'center',
minWidth: 200
},
{
key: 'status',
dataIndex: 'status',
title: $t('page.manage.user.userStatus'),
align: 'center',
width: 100,
customRender: ({ record }) => {
if (record.status === null) {
return null;
}
const tagMap: Record<Api.Common.EnableStatus, string> = {
1: 'success',
2: 'warning'
};
const label = $t(enableStatusRecord[record.status]);
return <Tag color={tagMap[record.status]}>{label}</Tag>;
}
},
{
key: 'operate',
title: $t('common.operate'),
align: 'center',
width: 130,
customRender: ({ record }) => (
<div class="flex-center gap-8px">
<Button type="primary" ghost size="small" onClick={() => edit(record.id)}>
{$t('common.edit')}
</Button>
<Popconfirm title={$t('common.confirmDelete')} onConfirm={() => handleDelete(record.id)}>
<Button danger size="small">
{$t('common.delete')}
</Button>
</Popconfirm>
</div>
)
}
]
});
const {
// drawerVisible,
// operateType,
// editingData,
handleAdd,
onSelectChange,
handleEdit,
checkedRowKeys,
onBatchDeleted,
onDeleted
// closeDrawer
} = useTableOperate(data, getData);
async function handleBatchDelete() {
// request
console.log(checkedRowKeys.value);
onBatchDeleted();
}
function handleDelete(id: number) {
// request
console.log(id);
onDeleted();
}
function edit(id: number) {
handleEdit(id);
}
</script>

<template>
<LookForward />
<div class="min-h-500px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
<UserSearch v-model:model="searchParams" @reset="resetSearchParams" @search="getData" />
<ACard
:title="$t('page.manage.user.title')"
:bordered="false"
:body-style="{ flex: 1, overflow: 'hidden' }"
class="flex-col-stretch sm:flex-1-hidden card-wrapper"
>
<template #extra>
<TableHeaderOperation
v-model:columns="columnChecks"
:disabled-delete="checkedRowKeys.length === 0"
:loading="loading"
@add="handleAdd"
@delete="handleBatchDelete"
@refresh="getData"
/>
</template>
<ATable
ref="wrapperEl"
:columns="columns"
:data-source="data"
size="small"
:row-selection="{
type: 'checkbox',
selectedRowKeys: checkedRowKeys,
onChange: onSelectChange
}"
:scroll="scrollConfig"
:loading="loading"
row-key="id"
:pagination="mobilePagination"
class="h-full"
/>
<!--
<UserOperateDrawer
v-model:visible="drawerVisible"
:operate-type="operateType"
:row-data="editingData"
@submitted="getData"
/>
-->
</ACard>
</div>
</template>

<style scoped></style>
120 changes: 120 additions & 0 deletions src/views/manage/user/modules/user-search.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
<script setup lang="ts">
import { computed } from 'vue';
import { $t } from '@/locales';
import { useAntdForm, useFormRules } from '@/hooks/common/form';
import { enableStatusOptions, userGenderOptions } from '@/constants/business';
import { translateOptions } from '@/utils/common';
defineOptions({
name: 'UserSearch'
});
interface Emits {
(e: 'reset'): void;
(e: 'search'): void;
}
const emit = defineEmits<Emits>();
const { formRef, validate, resetFields } = useAntdForm();
const model = defineModel<Api.SystemManage.UserSearchParams>('model', { required: true });
type RuleKey = Extract<keyof Api.SystemManage.UserSearchParams, 'userEmail' | 'userPhone'>;
const rules = computed<Record<RuleKey, App.Global.FormRule>>(() => {
const { patternRules } = useFormRules(); // inside computed to make locale reactive
return {
userEmail: patternRules.email,
userPhone: patternRules.phone
};
});
async function reset() {
await resetFields();
emit('reset');
}
async function search() {
await validate();
emit('search');
}
</script>

<template>
<ACard :title="$t('common.search')" :bordered="false" class="card-wrapper">
<AForm
ref="formRef"
:model="model"
:rules="rules"
:label-col="{
span: 5,
md: 7
}"
>
<ARow :gutter="[16, 16]" wrap>
<ACol :span="24" :md="12" :lg="6">
<AFormItem :label="$t('page.manage.user.userName')" name="userName" class="m-0">
<AInput v-model:value="model.userName" :placeholder="$t('page.manage.user.form.userName')" />
</AFormItem>
</ACol>
<ACol :span="24" :md="12" :lg="6">
<AFormItem :label="$t('page.manage.user.userGender')" name="userGender" class="m-0">
<ASelect
v-model:value="model.userGender"
:placeholder="$t('page.manage.user.form.userGender')"
:options="translateOptions(userGenderOptions)"
clearable
/>
</AFormItem>
</ACol>
<ACol :span="24" :md="12" :lg="6">
<AFormItem :label="$t('page.manage.user.nickName')" name="nickName" class="m-0">
<AInput v-model:value="model.nickName" :placeholder="$t('page.manage.user.form.nickName')" />
</AFormItem>
</ACol>
<ACol :span="24" :md="12" :lg="6">
<AFormItem :label="$t('page.manage.user.userPhone')" name="userPhone" class="m-0">
<AInput v-model:value="model.userPhone" :placeholder="$t('page.manage.user.form.userPhone')" />
</AFormItem>
</ACol>
<ACol :span="24" :md="12" :lg="6">
<AFormItem :label="$t('page.manage.user.userEmail')" name="userEmail" class="m-0">
<AInput v-model:value="model.userEmail" :placeholder="$t('page.manage.user.form.userEmail')" />
</AFormItem>
</ACol>
<ACol :span="24" :md="12" :lg="6">
<AFormItem :label="$t('page.manage.user.userStatus')" name="userStatus" class="m-0">
<ASelect
v-model:value="model.status"
:placeholder="$t('page.manage.user.form.userStatus')"
:options="translateOptions(enableStatusOptions)"
clearable
/>
</AFormItem>
</ACol>
<div class="flex-1">
<AFormItem class="m-0">
<div class="w-full flex-y-center justify-end gap-12px">
<AButton @click="reset">
<template #icon>
<icon-ic-round-refresh class="text-icon" />
</template>
{{ $t('common.reset') }}
</AButton>
<AButton type="primary" ghost @click="search">
<template #icon>
<icon-ic-round-search class="text-icon" />
</template>
{{ $t('common.search') }}
</AButton>
</div>
</AFormItem>
</div>
</ARow>
</AForm>
</ACard>
</template>

<style scoped></style>

0 comments on commit 64be7d2

Please sign in to comment.