feat: 完善基础组件;完成用户管理
This commit is contained in:
parent
d23f5d5668
commit
f57daee407
@ -1,4 +1,4 @@
|
||||
import ButtonComponent from './button.vue';
|
||||
|
||||
console.log(ButtonComponent)
|
||||
// console.log(ButtonComponent)
|
||||
export const Button = ButtonComponent
|
||||
|
@ -2,12 +2,71 @@
|
||||
<p>输出计算</p>
|
||||
<!-- <DataField /> -->
|
||||
<Button @click="showMessage">test</Button>
|
||||
<div>
|
||||
<Demo />
|
||||
</div>
|
||||
<div>
|
||||
<Form @finish="handleSave" @finishFailed="handleFinishError" autocomplete="off" :model="userData"
|
||||
:label-col="{ span: 5 }" :wrapper-col="{ span: 18 }">
|
||||
<Form.Item label="用户名" name="nickname" :rules="[{ required: true, message: '请输入姓名' }]">
|
||||
<Input placeholder="请输入姓名" v-model:value="userData.nickname" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="账号" name="account" :rules="[{ required: true, message: '请输入手机号或邮箱' }]">
|
||||
<Input placeholder="请输入手机号或邮箱" v-model:value="userData.account" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="密码" name="password" :rules="passwordRules">
|
||||
<Input placeholder="请输入6-12位密码" type="password" v-model:value="userData.password" />
|
||||
<div>{{ userData?.id ? '如果不需要修改,请不要填写' : '' }}</div>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="角色" name="role" :rules="[{ required: true, message: '请选择用户角色' }]">
|
||||
<Select placeholder="请选择" style="width: 100%;" v-model:value="userData.role">
|
||||
<SelectOption v-for="(it, opIndex) in RoleList" :key="opIndex" :value="it.value">{{ it.label }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item class="flex justify-end mt-10 mb-0">
|
||||
<Space :size="20">
|
||||
<Button html-type="submit">确定</Button>
|
||||
</Space>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import DataField from "@/components/data-fields/index.vue"
|
||||
import Button from "../components/button/button.vue";
|
||||
import {message} from "../components/message";
|
||||
import { reactive, computed } from 'vue'
|
||||
import { Form, Input, Button,Select,SelectOption,Space } from "ant-design-vue"
|
||||
import { RoleList } from '@/core/role';
|
||||
// import DataField from "@/components/data-fields/index.vue"
|
||||
// import Button from "../components/button/button.vue";
|
||||
import { message } from "../components/message";
|
||||
|
||||
import Demo from './result/demo.vue'
|
||||
|
||||
const userData = reactive<Partial<UserInfo>>({});
|
||||
|
||||
|
||||
const passwordRules = computed(() => {
|
||||
return userData?.id ? [
|
||||
{ min: 6, max: 12, message: '请输入6-12位密码' }
|
||||
] : [
|
||||
{ required: true, message: '请输入6-12位密码' },
|
||||
{ min: 6, max: 12, message: '请输入6-12位密码' }
|
||||
]
|
||||
})
|
||||
|
||||
function handleSave() {
|
||||
console.log(userData)
|
||||
if (userData) {
|
||||
// run(userData)
|
||||
}
|
||||
//
|
||||
}
|
||||
function handleFinishError(errorInfo: any) {
|
||||
console.log('handleFinishError', errorInfo)
|
||||
}
|
||||
const showMessage = () => {
|
||||
message.show(
|
||||
'This notification does not automatically close, and you need to click the close button to close.',
|
||||
|
45
src/pages/result/demo.vue
Normal file
45
src/pages/result/demo.vue
Normal file
@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<Form v-model="formState" name="basic" :label-col="{ span: 8 }" :wrapper-col="{ span: 16 }" autocomplete="off"
|
||||
@finish="onFinish" @finishFailed="onFinishFailed">
|
||||
<Form.Item label="Username" name="username"
|
||||
:rules="[{ required: true, message: 'Please input your username!' }]">
|
||||
<Input v-model:value="formState.username" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="Password" name="password"
|
||||
:rules="[{ required: true, message: 'Please input your password!' }]">
|
||||
<InputPassword v-model:value="formState.password" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name="remember" :wrapper-col="{ offset: 8, span: 16 }">
|
||||
<Checkbox v-model:checked="formState.remember">Remember me</Checkbox>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item :wrapper-col="{ offset: 8, span: 16 }">
|
||||
<Button type="primary" html-type="submit">Submit</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { reactive } from 'vue';
|
||||
import { Form, Input, Checkbox, Button, InputPassword } from 'ant-design-vue'
|
||||
|
||||
interface FormState {
|
||||
username: string;
|
||||
password: string;
|
||||
remember: boolean;
|
||||
}
|
||||
|
||||
const formState = reactive<FormState>({
|
||||
username: '',
|
||||
password: '',
|
||||
remember: true,
|
||||
});
|
||||
const onFinish = (values: any) => {
|
||||
console.log('Success:', values);
|
||||
};
|
||||
|
||||
const onFinishFailed = (errorInfo: any) => {
|
||||
console.log('Failed:', errorInfo);
|
||||
};
|
||||
</script>
|
@ -1,56 +1,50 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, h } from "vue";
|
||||
import { ref, h, watch } from "vue";
|
||||
import { SearchOutlined, PlusOutlined } from '@ant-design/icons-vue';
|
||||
import {
|
||||
Pagination, Select, SelectOption, Input, Button, Space, Empty, Spin,
|
||||
Modal,message
|
||||
Modal, message
|
||||
} from 'ant-design-vue'
|
||||
import { RoleEnum, AllRoleList } from '@/core/role'
|
||||
import { columns, getList,deleteUser } from "@/service/api/user";
|
||||
import { columns, getList, deleteUser } from "@/service/api/user";
|
||||
import PageHeader from '@/components/page-header.vue'
|
||||
import useRequest from "@/service/useRequest";
|
||||
import EditModal from './modal.vue'
|
||||
|
||||
const editUser = ref<UserInfo>()
|
||||
const editUser = ref<Partial<UserInfo>>()
|
||||
const searchParams = ref<UserSearchParam>({
|
||||
page: 1,
|
||||
limit: 10, role: ''
|
||||
limit: 10,
|
||||
role: ''
|
||||
})
|
||||
|
||||
const { data: allDataList, loading, run, refresh } = useRequest(() => getList(searchParams.value), {
|
||||
refreshDeps: [searchParams]
|
||||
})
|
||||
|
||||
const { data: allDataList, loading, run } = useRequest(() => getList(searchParams.value))
|
||||
|
||||
// onMounted(() => {
|
||||
// getList(searchParams.value).then(ret=>{
|
||||
// allDataList.value = ret
|
||||
// })
|
||||
// })
|
||||
watch(searchParams, run, { deep: true })
|
||||
function search() {
|
||||
searchParams.value = {
|
||||
...searchParams.value,
|
||||
page: 1,
|
||||
limit: 10
|
||||
}
|
||||
searchParams.value.page = 1;
|
||||
run()
|
||||
}
|
||||
function deleteUserById(id: number) {
|
||||
deleteUser(id).then(() => {
|
||||
message.success('删除成功')
|
||||
run()
|
||||
refresh()
|
||||
})
|
||||
}
|
||||
|
||||
function handleDelete(id: number) {
|
||||
Modal.confirm({
|
||||
title:'删除账号',
|
||||
content:'确定要删除该账号吗?',
|
||||
cancelText:'取消',
|
||||
okText:'删除',
|
||||
title: '删除账号',
|
||||
content: '确定要删除该账号吗?',
|
||||
cancelText: '取消',
|
||||
okText: '删除',
|
||||
okType: 'danger',
|
||||
okButtonProps:{
|
||||
|
||||
okButtonProps: {
|
||||
|
||||
},
|
||||
onOk:()=>{
|
||||
onOk: () => {
|
||||
deleteUserById(id)
|
||||
}
|
||||
})
|
||||
@ -72,13 +66,16 @@ function handleDelete(id: number) {
|
||||
<Space class="form-item">
|
||||
<span>类型</span>
|
||||
<Select class="search-item" v-model:value="searchParams.role" style="width: 200px;">
|
||||
<SelectOption v-for="(it, opIndex) in AllRoleList" :key="opIndex" :value="it.value">{{ it.label }}
|
||||
<SelectOption v-for="(it, opIndex) in AllRoleList" :key="opIndex" :value="it.value">{{ it.label
|
||||
}}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
</Space>
|
||||
<Space class="form-item" :size="15">
|
||||
<Button :icon="h(SearchOutlined)" class="flex item-center" type="primary" @click="search()">查询</Button>
|
||||
<Button :icon="h(PlusOutlined)" class="btn-info flex item-center" @click="editUser = {id:0}">新增</Button>
|
||||
<Button :icon="h(SearchOutlined)" type="primary"
|
||||
@click="search()">查询</Button>
|
||||
<Button :icon="h(PlusOutlined)" class="btn-info"
|
||||
@click="editUser = { id: 0 }">新增</Button>
|
||||
</Space>
|
||||
</Space>
|
||||
</div>
|
||||
@ -99,7 +96,7 @@ function handleDelete(id: number) {
|
||||
<td>{{ user.role == RoleEnum.ROOT ? '超级管理员' : '管理员' }}</td>
|
||||
<td>
|
||||
<Space :size="20">
|
||||
<a @click="editUser = user">编辑</a>
|
||||
<a @click="editUser = { ...user, password: '' }">编辑</a>
|
||||
<a @click="handleDelete(user.id)">删除</a>
|
||||
</Space>
|
||||
</td>
|
||||
@ -117,15 +114,16 @@ function handleDelete(id: number) {
|
||||
:total="allDataList.total" :show-total="(total: number) => `共 ${total} 条`" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<EditModal v-if="!!editUser" v-model="editUser" />
|
||||
|
||||
<EditModal v-if="!!editUser" v-model="editUser" @saved="refresh" />
|
||||
</div>
|
||||
</template>
|
||||
<style lang="scss">
|
||||
.ant-btn-dangerous{
|
||||
.ant-btn-dangerous {
|
||||
background-color: hsl(359, 100%, 60%);
|
||||
color: white !important;
|
||||
&:hover{
|
||||
|
||||
&:hover {
|
||||
background-color: hsl(359, 100%, 70%);
|
||||
}
|
||||
}
|
||||
|
@ -1,57 +1,94 @@
|
||||
<script lang="ts" setup>
|
||||
import { defineModel } from 'vue';
|
||||
import { Modal, Form, FormItem, Input, Select, SelectOption, Space, Button } from 'ant-design-vue';
|
||||
import { computed, defineModel, reactive } from 'vue';
|
||||
import { Modal, Form, Input, Select, SelectOption, Space, Button } from 'ant-design-vue';
|
||||
import { md5 } from 'js-md5';
|
||||
|
||||
import { RoleList } from '@/core/role';
|
||||
import useRequest from '@/service/useRequest';
|
||||
import { saveUser } from '@/service/api/user';
|
||||
|
||||
const editUser = defineModel<UserInfo>();
|
||||
const editUser = defineModel<Partial<UserInfo>>();
|
||||
const userData = reactive<Partial<UserInfo>>({ ...(editUser ? editUser.value : {}) });
|
||||
|
||||
function handleSave(values: UserInfo) {
|
||||
console.log(values)
|
||||
defineExpose({
|
||||
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'saved'): void
|
||||
}>()
|
||||
|
||||
const { loading, runAsync, error } = useRequest(saveUser, {
|
||||
manual: true
|
||||
})
|
||||
|
||||
function handleSave() {
|
||||
runAsync({
|
||||
id: userData.id,
|
||||
nickname: userData.nickname,
|
||||
account: userData.account,
|
||||
role: userData.role,
|
||||
password: userData.password ? md5(userData.password) : undefined,
|
||||
}).then(() => {
|
||||
editUser.value = undefined;
|
||||
emit('saved')
|
||||
})
|
||||
//
|
||||
}
|
||||
function handleCancel() {
|
||||
editUser.value = undefined;
|
||||
}
|
||||
const passwordRules = computed(() => {
|
||||
return editUser?.value?.id ? [
|
||||
{ min: 6, max: 12, message: '请输入6-12位密码' }
|
||||
] : [
|
||||
{ required: true, message: '请输入6-12位密码' },
|
||||
{ min: 6, max: 12, message: '请输入6-12位密码' }
|
||||
]
|
||||
})
|
||||
function handleFinishError(errorInfo: any) {
|
||||
console.log('handleFinishError', errorInfo)
|
||||
}
|
||||
function onValuesChange() {
|
||||
error.value = undefined
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<Modal :width="400" :open="true" :footer="null" @cancel="handleCancel">
|
||||
<Modal :width="400" :open="true" :footer="null" @cancel="handleCancel" :mask-closable="false"
|
||||
:destroy-on-close="true">
|
||||
<div class="pt-12">
|
||||
<Form @finish="handleSave" autocomplete="off" v-model="editUser" :label-col="{ span: 5 }"
|
||||
:wrapper-col="{ span: 18 }">
|
||||
<FormItem label="用户名" name="nickname" :rules="[{ required: true, message: '请输入姓名' }]">
|
||||
<Input placeholder="请输入姓名" />
|
||||
</FormItem>
|
||||
<Form @finish="handleSave" @finishFailed="handleFinishError" @validate="onValuesChange" autocomplete="off"
|
||||
:model="userData" :label-col="{ span: 5 }" :wrapper-col="{ span: 18 }">
|
||||
<Form.Item label="姓名" name="nickname" :rules="[{ required: true, message: '请输入姓名' }]">
|
||||
<Input placeholder="请输入姓名" v-model:value="userData.nickname" />
|
||||
</Form.Item>
|
||||
|
||||
<FormItem label="账号" name="account" :rules="[{ required: true, message: '请输入手机号或邮箱' }]">
|
||||
<Input placeholder="请输入手机号或邮箱" />
|
||||
</FormItem>
|
||||
<Form.Item label="账号" name="account" :rules="[{ required: true, message: '请输入手机号或邮箱' }]">
|
||||
<Input placeholder="请输入手机号或邮箱" v-model:value="userData.account" />
|
||||
</Form.Item>
|
||||
|
||||
<FormItem label="密码" name="password" :rules="[{
|
||||
validator: async (_:any, value:string) => {
|
||||
console.log('mima',value)
|
||||
if(editUser.value?.id){
|
||||
return true;
|
||||
}
|
||||
if(!value || value.length < 6 || value.length > 12){
|
||||
throw new Error('密码长度6-12位');
|
||||
};
|
||||
return true;
|
||||
}
|
||||
}]">
|
||||
<Input placeholder="请输入6-12位密码" type="password" />
|
||||
</FormItem>
|
||||
<Form.Item label="密码" name="password" :rules="passwordRules">
|
||||
<Input placeholder="请输入6-12位密码" type="password" v-model:value="userData.password" />
|
||||
<template #extra v-if="editUser?.id">
|
||||
<div>留空为不修改密码</div>
|
||||
</template>
|
||||
</Form.Item>
|
||||
|
||||
<FormItem label="角色" name="role" :rules="[{ required: true, message: '请选择用户角色' }]">
|
||||
<Select placeholder="请选择" style="width: 100%;">
|
||||
<Form.Item label="角色" name="role" :rules="[{ required: true, message: '请选择用户角色' }]">
|
||||
<Select placeholder="请选择" style="width: 100%;" v-model:value="userData.role">
|
||||
<SelectOption v-for="(it, opIndex) in RoleList" :key="opIndex" :value="it.value">{{ it.label }}
|
||||
</SelectOption>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem class="flex justify-end mt-10 mb-0">
|
||||
</Form.Item>
|
||||
<div v-if="error" class="text-red-500 text-center">
|
||||
{{ error?.message }}
|
||||
</div>
|
||||
<Form.Item class="flex justify-end mt-6 mb-0">
|
||||
<Space :size="20">
|
||||
<Button @click="handleCancel">取消</Button>
|
||||
<Button html-type="submit" class="btn-info">确定</Button>
|
||||
<Button :loading="loading" html-type="submit" class="btn-info">确定</Button>
|
||||
</Space>
|
||||
</FormItem>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
</Modal>
|
||||
|
@ -2,6 +2,10 @@ import { sleep } from "@/core/sleep";
|
||||
import { get, post } from "./request";
|
||||
|
||||
|
||||
export async function saveUser(params: Partial<UserInfo>) {
|
||||
await sleep(200);
|
||||
return await post<UserInfo>(`/user/save`, params);
|
||||
}
|
||||
|
||||
export function userLogin(params: LoginModel) {
|
||||
return post<{ token: string; user: UserInfo }>(`/user/login`, params);
|
||||
@ -21,7 +25,7 @@ export function deleteUser(id:number){
|
||||
}
|
||||
|
||||
export const columns = [
|
||||
{ title: '账号', dataIndex: 'account' },
|
||||
{ title: '姓名', dataIndex: 'nickname' },
|
||||
{ title: '账号', dataIndex: 'account' },
|
||||
{ title: '角色', dataIndex: 'role' },
|
||||
]
|
@ -1,40 +1,64 @@
|
||||
import { onMounted, ref, watch } from "vue";
|
||||
import { onMounted, Ref, ref, watch } from "vue";
|
||||
|
||||
export default function useRequest<T>(
|
||||
service: () => Promise<T>,
|
||||
type UseRequestResult<R = any, P extends any[] = any[]> = {
|
||||
data: Ref<R | undefined>;
|
||||
loading: Ref<boolean>;
|
||||
error: Ref<Error | undefined>;
|
||||
refresh: () => void;
|
||||
run: (...args: P) => Promise<void>;
|
||||
runAsync: (...args: P) => Promise<void>;
|
||||
}
|
||||
|
||||
export type Service<R, P extends any[]> = (...args: P) => Promise<R>;
|
||||
export default function useRequest<T = any, P extends any[] = any[]>(
|
||||
service: Service<T, P>,
|
||||
options?: {
|
||||
defaultParams?: P;
|
||||
manual?: boolean;
|
||||
refreshDeps?: any[];
|
||||
}
|
||||
) {
|
||||
): UseRequestResult<T, P> {
|
||||
const params = ref<P>((options?.defaultParams || []) as P) as Ref<P>;
|
||||
const data = ref<T>();
|
||||
const loading = ref(false);
|
||||
const error = ref<string>();
|
||||
const error = ref<Error>();
|
||||
|
||||
const _request = () => {
|
||||
const _request = (...args: P) => {
|
||||
loading.value = true
|
||||
return service()
|
||||
return service(...args)
|
||||
.then((res) => {
|
||||
data.value = res
|
||||
})
|
||||
.catch((e: Error) => {
|
||||
error.value = e.message
|
||||
error.value = e
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
const runAsync = async (...args: P) => {
|
||||
try {
|
||||
loading.value = true
|
||||
const _ = await service(...args);
|
||||
data.value = _
|
||||
loading.value = false
|
||||
} catch (e) {
|
||||
loading.value = false
|
||||
error.value = e as Error;
|
||||
throw e
|
||||
}
|
||||
}
|
||||
const refresh = () => {
|
||||
_request()
|
||||
_request(...params.value)
|
||||
}
|
||||
onMounted(() => {
|
||||
if (!options?.manual) {
|
||||
_request()
|
||||
_request(...params.value)
|
||||
}
|
||||
});
|
||||
if (options?.refreshDeps) {
|
||||
watch(options.refreshDeps, () => {
|
||||
_request()
|
||||
_request(...params.value)
|
||||
})
|
||||
}
|
||||
return {
|
||||
@ -42,6 +66,7 @@ export default function useRequest<T>(
|
||||
refresh,
|
||||
loading,
|
||||
error,
|
||||
run: _request
|
||||
run: _request,
|
||||
runAsync
|
||||
}
|
||||
}
|
@ -168,9 +168,8 @@ img {
|
||||
}
|
||||
|
||||
.ant-btn {
|
||||
.anticon {
|
||||
position: relative;
|
||||
transform: translateY(1px);
|
||||
svg {
|
||||
vertical-align: -1px;
|
||||
}
|
||||
|
||||
&.btn-info {
|
||||
|
Loading…
x
Reference in New Issue
Block a user