结合工作实例,封装了一个有多功能的通用弹窗组件
所用到技术:Vue3.2+arco-design+ts
实现功能:实现表格的增删改查,实现弹窗的封装,一个弹窗适应增删改三个功能点
大体思路如下:
要是想了解如何封装组件的前端开发,刷到可以细看,封装的应该是非常简介和易懂的
父组件代码如下: 具体可以细看,相关接口可能需要自己调用他人接口或mock数据
<template>
<a-space direction="vertical" :size="20" fill>
<div class="header">
<a-space fill>
<div class="search">
<a-input
v-model="searchKey"
:style="{ width: '320px' }"
:placeholder="$t('settings.search')"
class="search"
allow-clear
@clear="searchIdenInfo"
@keyup.enter="searchIdenInfo"
>
<template #prefix>
<a-button type="text" shape="circle" @click="searchIdenInfo">
<template #icon>
<SvgIcon name="search" />
</template>
</a-button>
</template>
</a-input>
</div>
<div class="btns">
<a-button
type="primary"
:disabled="selectedKeys.length === 0"
@click="delChooseInfo"
>
{{ $t('basicData.mailList.identityInfo.del') }}
</a-button>
<a-button type="primary" @click="addIdenInfo">
{{ $t('basicData.mailList.identityInfo.add') }}
</a-button>
</div>
</a-space>
</div>
<div class="table">
<div class="theTable">
<a-table
ref="tableRef"
v-model:selectedKeys="selectedKeys"
row-key="guid"
:columns="columns"
:data="IndeListdata"
:loading="loading"
:bordered="{ headerCell: true, wrapper: false }"
:row-selection="rowSelection"
:scroll="{ x: '100%', y: '90%' }"
:hoverable="false"
:pagination="false"
>
<!-- 警号插槽 -->
<template #siren="{ record }">
<a-link @click="getPolice(record)">{{ record.siren }}</a-link>
</template>
<!-- 操作插槽 -->
<template #optional="{ record }">
<div class="btns">
<a-button
shape="circle"
:disabled="record.judge_sync_edit !== 1"
@click="editRow(record)"
>
<template #icon>
<SvgIcon name="edit" />
</template>
</a-button>
<a-button
shape="circle"
:disabled="record.judge_sync_edit !== 1"
@click="delRow(record)"
>
<template #icon>
<SvgIcon name="delete" />
</template>
</a-button>
</div>
</template>
</a-table>
</div>
<div class="pagination">
<a-pagination
ref="paginationRef"
show-total
show-jumper
show-page-size
:total="pageTotal"
:default-page-size="defaultPageSize"
:page-size-options="pageSizeOption"
:current="currentPage.current"
@change="pageChange"
@page-size-change="pageSizeChange"
/>
</div>
</div>
</a-space>
<IdenInfoModal
ref="IdenInfoModelRef"
:operation-type="operationType"
@refreshInfo="searchIdenInfo"
/>
</template>
<script lang="ts" setup>
import { ref, reactive, computed } from 'vue';
import { Message } from '@arco-design/web-vue';
import { useI18n } from 'vue-i18n';
import { Random } from 'mockjs';
import { configLoginInfoStore } from '@/store';
import request from '@/utils/request';
import useLoading from '@/hooks/loading';
import IdenInfoModal from './IdenInfoModal.vue';
const { t } = useI18n();
const { loading, setLoading } = useLoading(true);
const configLoginStore = configLoginInfoStore();
const IdenInfoModelRef = ref(null);
const searchKey = ref<string>('');
const selectedKeys = ref([]);
type operType = 'add' | 'edit' | 'del' | 'view' | 'delChoose';
const operationType = ref<operType>('add');
const rowSelection = reactive<any>({
type: 'checkbox',
showCheckedAll: true,
});
const defaultPageSize = 15;
const pageSizeOption = [10, 15, 20, 30, 40, 50];
const pageTotal = ref<number>(0);
const currentPage = ref({
current: 1,
pageSize: 15,
});
const columns = computed<any>(() => {
return [
{
title: t('basicData.mailList.identityInfo.sysId'),
dataIndex: 'system_id',
align: 'center',
ellipsis: true,
tooltip: true,
width: 100,
},
{
title: t('basicData.mailList.identityInfo.personId'),
dataIndex: 'id_no',
align: 'center',
ellipsis: true,
width: 100,
},
{
title: t('basicData.mailList.identityInfo.policeID'),
slotName: 'siren',
align: 'center',
width: 100,
},
{
title: t('basicData.mailList.identityInfo.policeName'),
dataIndex: 'siren_name',
align: 'center',
ellipsis: true,
tooltip: true,
width: 100,
},
{
title: t('basicData.mailList.identityInfo.departmentId'),
dataIndex: 'department_id',
align: 'center',
ellipsis: true,
tooltip: true,
width: 100,
},
{
title: t('basicData.mailList.identityInfo.departmentAll'),
dataIndex: 'department_full_name',
align: 'center',
ellipsis: true,
tooltip: true,
width: 100,
},
{
title: t('basicData.mailList.identityInfo.departmentShort'),
dataIndex: 'department_name',
align: 'center',
ellipsis: true,
tooltip: true,
width: 100,
},
{
title: t('basicData.mailList.identityInfo.operation'),
slotName: 'optional',
align: 'center',
fixed: 'right',
width: 100,
},
];
});
const IndeListdata = ref([]);
const searchIdenInfo = async () => {
setLoading(true);
const params = {
realm: configLoginStore.getRealm,
cmd_name: 'staff_identifier_request',
cmd_guid: Random.guid(),
is_fuzzy_qry: 1,
page_sizes: currentPage.value.pageSize,
page_index: currentPage.value.current,
querykey: searchKey.value,
};
const data: any = await request(params);
if (data.result === 0) {
IndeListdata.value = data.staff_identifier_list;
pageTotal.value = data.count ? data.count : 0;
setLoading(false);
}
console.log(data);
};
const getPolice = (item) => {
operationType.value = 'view';
IdenInfoModelRef.value.handleClick(item);
};
const addIdenInfo = () => {
operationType.value = 'add';
IdenInfoModelRef.value.handleClick();
};
const isChecked = ref([]);
const getAllChecked = () => {
isChecked.value = IndeListdata.value
.map((item) => {
if (selectedKeys.value.includes(item.guid)) {
return {
puc_id: configLoginStore.getPucId,
realm: configLoginStore.getRealm,
system_id: item.system_id,
siren: item.siren,
};
}
return null;
})
.filter((v) => v);
};
const delChooseInfo = () => {
operationType.value = 'delChoose';
getAllChecked();
IdenInfoModelRef.value.handleClick(isChecked.value);
};
const editRow = (item) => {
operationType.value = 'edit';
IdenInfoModelRef.value.handleClick(item);
};
const delRow = (item) => {
operationType.value = 'del';
IdenInfoModelRef.value.handleClick(item);
};
const pageChange = (current: number) => {
currentPage.value.current = current;
searchIdenInfo();
};
const pageSizeChange = (pageSize: number) => {
currentPage.value.pageSize = pageSize;
searchIdenInfo();
};
defineExpose({
searchIdenInfo,
});
</script>
<style lang="less" scoped>
</style>
子组件代码
<template>
<a-modal
v-model:visible="visible"
:title="title"
title-align="start"
:ok-text="
operationType !== 'del' && operationType !== 'delChoose'
? $t('basicData.mailList.identityInfo.save')
: $t('basicData.mailList.identityInfo.sure')
"
:cancel-text="$t('basicData.mailList.identityInfo.cancel')"
:width="
operationType !== 'del' && operationType !== 'delChoose'
? '680px'
: '440px'
"
:footer="operationType !== 'view'"
@cancel="handleCancel"
@before-ok="handleBeforeOk"
>
<a-form
v-if="operationType !== 'del' && operationType !== 'delChoose'"
ref="formRef"
class="form-modal"
layout="vertical"
:model="form"
:rules="rules"
:disabled="operationType === 'view'"
>
<a-form-item
:disabled="operationType === 'edit' || operationType === 'view'"
field="system_id"
:label="$t('basicData.mailList.identityInfo.sysId')"
>
<a-select v-model="form.system_id" allow-clear>
<a-option
v-for="item of systemList"
:key="item.label"
:value="item.value"
:label="item.label"
/>
</a-select>
</a-form-item>
<a-form-item
:disabled="operationType === 'edit' || operationType === 'view'"
field="siren"
:label="$t('basicData.mailList.identityInfo.policeID')"
>
<a-input v-model="form.siren" />
</a-form-item>
<a-form-item
field="id_no"
:label="$t('basicData.mailList.identityInfo.personId')"
>
<a-input v-model="form.id_no" />
</a-form-item>
<a-form-item
field="siren_name"
:label="$t('basicData.mailList.identityInfo.policeName')"
>
<a-input v-model="form.siren_name" />
</a-form-item>
<a-form-item
field="department_id"
:label="$t('basicData.mailList.identityInfo.departmentId')"
>
<a-input v-model="form.department_id" />
</a-form-item>
<a-form-item
field="department_full_name"
:label="$t('basicData.mailList.identityInfo.departmentAll')"
>
<a-input v-model="form.department_full_name" />
</a-form-item>
<a-form-item
field="department_name"
:label="$t('basicData.mailList.identityInfo.departmentShort')"
>
<a-input v-model="form.department_name" />
</a-form-item>
</a-form>
<div v-else>
{{
props.operationType === 'del'
? $t('basicData.mailList.identityInfo.sureCancelRow')
: $t('basicData.mailList.identityInfo.sureCancelChoose')
}}
</div>
</a-modal>
</template>
<script setup lang="ts">
import { ref, computed, nextTick } from 'vue';
import { useI18n } from 'vue-i18n';
import { Random } from 'mockjs';
import { configLoginInfoStore } from '@/store';
import { Message } from '@arco-design/web-vue';
import { showErrorMsgDesc } from '@/hooks/error';
import request from '@/utils/request/index';
const emit = defineEmits<{
(event: 'refreshInfo'): void;
}>();
const { t } = useI18n();
const configLoginStore = configLoginInfoStore();
interface formType {
system_id: string;
id_no: string;
siren: string;
siren_name: string;
department_id: string;
department_full_name: string;
department_name: string;
}
const visible = ref(false);
const formRef = ref();
const systemList = ref<object[]>([]);
const props = defineProps<{
operationType: string;
}>();
const form = ref<formType>({
system_id: '',
id_no: '',
siren: '',
siren_name: '',
department_id: '',
department_full_name: '',
department_name: '',
});
const getSystemList = async () => {
const params = ref({
cmd_name: 'system_list_request',
puc_id: configLoginStore.getPucId,
user_id: configLoginStore.getUserId,
realm: configLoginStore.getRealm,
});
const res = await request(params.value);
const data = res?.system_list || [];
const system_list = data.map((item) => {
return {
...item,
value: item.system_id,
label: `${item.system_alias}(${item.system_id})`,
};
});
systemList.value = system_list;
console.log(systemList.value);
};
const delMultiple = ref([]);
const title = computed(() => {
return t(`basicData.mailList.identityInfo.${props.operationType}`);
});
const rules = computed(() => {
return {
system_id: [
{
required: true,
message: t('basicData.mailList.identityInfo.notNull'),
},
],
siren: [
{
required: true,
trigger: 'blur',
message: t('basicData.mailList.identityInfo.notNull'),
},
{
match: /^[0-9]*$/,
message: t('basicData.mailList.equipment.onlynumber'),
},
],
id_no: [
{
required: true,
trigger: 'blur',
message: t('basicData.mailList.identityInfo.notNull'),
},
{
match: /^[0-9]*$/,
message: t('basicData.mailList.equipment.onlynumber'),
},
],
siren_name: [
{
required: false,
trigger: 'blur',
message: t('basicData.mailList.identityInfo.notNull'),
},
{
validator: (value: any, cb: any) => {
const nameReg = /[\\/:*?<>|&#@%$^]/im;
if (nameReg.test(value)) {
cb(
t('basicData.mailList.equipment.device_alias.reg').replace(
'$1',
'/ : * ? < > | & # @ % $ ^'
)
);
}
},
},
],
department_id: [
{
required: false,
trigger: 'blur',
message: t('basicData.mailList.identityInfo.notNull'),
},
{
match: /^[0-9]*$/,
message: t('basicData.mailList.equipment.onlynumber'),
},
],
department_full_name: [
{
required: false,
trigger: 'blur',
message: t('basicData.mailList.identityInfo.notNull'),
},
{
validator: (value: any, cb: any) => {
const nameReg = /[\\/:*?<>|&#@%$^]/im;
if (nameReg.test(value)) {
cb(
t('basicData.mailList.equipment.device_alias.reg').replace(
'$1',
'/ : * ? < > | & # @ % $ ^'
)
);
}
},
},
],
department_name: [
{
required: false,
trigger: 'blur',
message: t('basicData.mailList.identityInfo.notNull'),
},
{
validator: (value: any, cb: any) => {
const nameReg = /[\\/:*?<>|&#@%$^]/im;
if (nameReg.test(value)) {
cb(
t('basicData.mailList.equipment.device_alias.reg').replace(
'$1',
'/ : * ? < > | & # @ % $ ^'
)
);
}
},
},
],
};
});
const clearForm = () => {
formRef.value.resetFields();
};
const handleClick = (info) => {
getSystemList();
nextTick(() => {
visible.value = true;
console.log(props.operationType);
if (props.operationType === 'add') {
clearForm();
return;
}
if (props.operationType === 'delChoose') {
delMultiple.value = info;
}
form.value = JSON.parse(JSON.stringify(info));
});
};
const add = async (done: (arg: boolean) => void) => {
const param = {
cmd_name: 'add_staff_identifier',
cmd_guid: Random.guid(),
staff_identifier_info: {
puc_id: configLoginStore.getPucId,
guid: '',
realm: configLoginStore.getRealm,
...form.value,
},
};
const res: any = await request(param);
if (res.extbody.result !== 0) {
done(false);
showErrorMsgDesc(res.result, res.msg);
return;
}
await nextTick(() => {
emit('refreshInfo');
done(true);
});
clearForm();
};
const edit = async (done: (arg: boolean) => void) => {
const param = {
cmd_name: 'update_staff_identifier',
cmd_guid: Random.guid(),
staff_identifier_info: {
puc_id: configLoginStore.getPucId,
guid: '',
realm: configLoginStore.getRealm,
...form.value,
},
};
const res = await request(param);
if (res.extbody.result !== 0) {
done(false);
showErrorMsgDesc(res.result, res.msg);
return;
}
await nextTick(() => {
emit('refreshInfo');
done(true);
});
};
const del = async (done: (arg: boolean) => void) => {
const param = {
cmd_name: 'delete_staff_identifier',
cmd_guid: Random.guid(),
del_info_list: [
{
puc_id: configLoginStore.getPucId,
realm: configLoginStore.getRealm,
system_id: form.value.system_id,
siren: form.value.siren,
},
],
};
const res = await request(param);
if (res.extbody.result !== 0) {
done(false);
showErrorMsgDesc(res.result, res.msg);
return;
}
await nextTick(() => {
emit('refreshInfo');
done(true);
});
};
const delChoose = async (done: (arg: boolean) => void) => {
const param = {
cmd_name: 'delete_staff_identifier',
cmd_guid: Random.guid(),
del_info_list: delMultiple.value,
};
const res = await request(param);
if (res.extbody.result !== 0) {
done(false);
showErrorMsgDesc(res.result, res.msg);
return;
}
await nextTick(() => {
emit('refreshInfo');
done(true);
});
done(true);
};
const handleBeforeOk = (done) => {
if (props.operationType === 'add') {
formRef.value.validate().then((value) => {
if (value) {
Message.warning(t('basicData.mailList.identityInfo.checkwarn'));
done(false);
} else {
add(done);
}
});
}
if (props.operationType === 'edit') {
formRef.value.validate().then((value) => {
if (value) {
Message.warning(t('basicData.mailList.identityInfo.checkwarn'));
done(false);
} else {
edit(done);
}
});
}
if (props.operationType === 'del') {
del(done);
}
if (props.operationType === 'delChoose') {
delChoose(done);
}
};
const handleCancel = () => {
visible.value = false;
if (props.operationType !== 'delChoose' && props.operationType !== 'del') {
clearForm();
}
};
defineExpose({
handleClick,
});
</script>
<style scoped lang="less">
</style>