数据增删改
基本介绍
增删改查是基本一个业务最基本的功能,他包含表格的使用,表单录入,接口请求等。
定义表格和表单配置项
在/src/const/crud/cbb/base
目录下创建名为person.ts
的文件,其中cbb
表示框架,base
表示模块,person
表示业务功能;我们在业务系统中应该在crud
目录下创建框架名,依次是模块,业务功能。 声明内容如下
ts
import type { VxeGridPropTypes, VxeFormPropTypes } from 'vxe-table';
import { REGEXP_PHONE, REGEXP_PWD, REGEXP_EMAIL, REGEXP_ID_CARD } from '@/config';
import { isName } from '@/utils';
import baseDict from '@/const/dict/base';
/**
* 账号规则验证
* @param itemValue
* @param data
* @returns {Promise<never>|Promise<unknown>}
*/
const validateAccount = ({ itemValue }: any) => {
if (!isName(itemValue)) {
return Promise.reject(new Error('登录账号只能包含中文、英文字母,不能包含特殊字符'));
}
return Promise.resolve(itemValue);
};
/**
* @func: personTableColumns
* @description: 人员table列字段配置
* @param {*}
* @return {VxeGridPropTypes.Columns}
* @example:
*/
export function personTableColumns(): VxeGridPropTypes.Columns {
return [
{ type: 'seq', width: 60, title: '序号', fixed: 'left' },
{ type: 'checkbox', width: 45 },
{ field: 'id', title: 'ID', minWidth: 100, visible: false },
{ field: 'name', title: '姓名', minWidth: 100 },
{ field: 'displayName', title: '显示姓名', minWidth: 100 },
{
field: 'gender',
title: '性别',
minWidth: 100,
formatter: ['formatDict', baseDict.getDictItems('basePersonGender')]
},
{ field: 'groupNum', title: '集团编号', minWidth: 100 },
{ field: 'account', title: '登录帐号', minWidth: 100 },
{ field: 'mobile', title: '手机号码', minWidth: 100 },
{
field: 'status',
title: '人员状态',
minWidth: 100,
formatter: ['formatDict', baseDict.getDictItems('basePersonStatus')]
},
{
title: '操作',
width: 140,
fixed: 'right',
slots: { default: 'operation' }
}
];
}
/**
* @func: personFormData
* @description: 人员form数据
* @param {*}
* @return {*}
* @example:
*/
export function personFormData() {
return {
id: null,
name: null,
displayName: null,
gender: null,
workTime: null,
email: null,
idnum: null,
mobile: null,
groupNum: null,
account: null,
memo: null,
status: null
};
}
/**
* @func: personTableFormConfig
* @description: 人员table查询表单配置
* @param {*}
* @return {*}
* @example:
*/
export function personTableFormConfig(): VxeGridPropTypes.FormConfig {
return {
data: { account: null, displayName: null, name: null, mobile: null, groupNum: null, status: '1' },
items: [
{
field: 'account',
title: '登录帐号',
itemRender: {
name: 'NInput',
attrs: { placeholder: '请输入登录帐号' }
}
},
{
field: 'displayName',
title: '显示姓名',
itemRender: {
name: 'NInput',
attrs: { placeholder: '请输入显示姓名' }
}
},
{
field: 'name',
title: '姓名',
itemRender: {
name: 'NInput',
attrs: { placeholder: '请输入姓名' }
}
},
{
field: 'mobile',
title: '手机号码',
itemRender: {
name: 'NInput',
attrs: { placeholder: '请输入手机号码' }
}
},
{
field: 'groupNum',
title: '集团编号',
itemRender: {
name: 'NInput',
attrs: { placeholder: '请输入集团编号' }
}
},
{
field: 'status',
title: '人员状态',
itemRender: {
name: 'NSelect',
options: baseDict.getDictItems('basePersonStatus'),
optionProps: { value: 'code', label: 'name' },
props: { placeholder: '请选择人员状态' }
}
},
{
itemRender: {
name: 'NButton',
props: { content: '查询', attrType: 'submit', type: 'primary' }
}
},
{
itemRender: {
name: 'NButton',
props: { content: '重置', attrType: 'reset' }
}
}
]
};
}
/**
* @func: personFormItems
* @description: 人员form表单配置项
* @param {*} options.formType 表单类型
* @return {VxeFormPropTypes.Items}
* @example:
*/
export function personFormItems(): VxeFormPropTypes.Items {
return [
{
field: 'name',
title: '姓名',
span: 12,
itemRender: {
name: 'NInput',
props: { placeholder: '请输入真实姓名' }
}
},
{
field: 'displayName',
title: '显示姓名',
span: 12,
itemRender: {
name: 'NInput',
props: { placeholder: '请输入显示姓名' }
}
},
{
field: 'gender',
title: '性别',
span: 12,
itemRender: {
name: 'NSelect',
options: baseDict.getDictItems('basePersonGender'),
optionProps: { value: 'code', label: 'name' },
props: { placeholder: '请选择性别' }
}
},
{
field: 'workTime',
title: '参加工作时间',
span: 12,
itemRender: {
name: 'NDatePicker',
props: { placeholder: '请输入参加工作时间', type: 'datetime', valueFormat: 'yyyy-MM-dd HH:mm:ss' }
}
},
{
field: 'email',
title: '电子邮件',
span: 12,
itemRender: {
name: 'NInput',
props: { placeholder: '请输入电子邮件(全小写)' }
}
},
{
field: 'idnum',
title: '身份证号',
span: 12,
itemRender: {
name: 'NInput',
props: { placeholder: '请输入身份证号(全小写,18位)' }
}
},
{
field: 'mobile',
title: '手机号码',
span: 12,
itemRender: {
name: 'NInput',
props: { placeholder: '请输入手机号码(11位数字)' }
}
},
{
field: 'groupNum',
title: '集团编号',
span: 12,
itemRender: {
name: 'NInput',
props: { placeholder: '请输入集团编号(10位以内数字)' }
}
},
{
field: 'account',
title: '登录帐号',
span: 12,
itemRender: {
name: 'NInput',
props: { placeholder: '请输入登录帐号' }
}
},
{
field: 'password',
title: '登录密码',
span: 12,
itemRender: {
name: 'NInput',
props: { placeholder: '请输入登录密码' }
}
},
{
field: 'memo',
title: '备注',
span: 24,
itemRender: {
name: 'NInput',
props: {
type: 'textarea',
autosize: { minRows: 1, maxRows: 2 },
placeholder: '请输入备注'
}
}
},
{
align: 'center',
span: 24,
slots: { default: 'formOperation' }
}
];
}
/**
* @func: personFormRules
* @description: 人员form数据验证规则
* @param {*}
* @return {VxeFormPropTypes.Rules}
* @example:
*/
export function personFormRules(): VxeFormPropTypes.Rules {
return {
name: [
{ required: true, message: '请输入姓名' },
{ max: 10, message: '长度在10个字符之内' }
],
displayName: [
{ required: true, message: '请输入名称' },
{ max: 64, message: '长度在64个字符之内' }
],
gender: [{ required: true, message: '请选择性别' }],
phone: [
{
pattern: REGEXP_PHONE,
message: '请输入11位电话号码'
}
],
email: [
{
pattern: REGEXP_EMAIL,
message: '请输入正确的邮箱,例如:example@qq.com'
}
],
idnum: [
{
pattern: REGEXP_ID_CARD,
message: '请输入正确的身份证号(第二代)'
}
],
account: [
{ required: true, message: '请输入登录帐号' },
{
min: 2,
max: 20,
message: '长度2到15位字符之内'
},
{ validator: validateAccount }
],
password: [
{
required: false,
pattern: REGEXP_PWD,
message: '包含大写字母、小写字母、数字和特殊字符,长度8-20位'
}
],
groupNum: [
{
required: false,
pattern: /^[0-9]{1,10}$/,
message: '10位以内数字'
}
]
};
}
/**
* @func: personExtTableColumns
* @description: 人员扩展属性table列字段配置
* @param {*}
* @return {VxeGridPropTypes.Columns}
* @example:
*/
export function personExtTableColumns(): VxeGridPropTypes.Columns {
return [
{ type: 'seq', width: 60, title: '序号', fixed: 'left' },
{ type: 'checkbox', width: 45 },
{ field: 'id', title: 'ID', minWidth: 100, visible: false },
{
field: 'displayName',
title: '人员名称',
minWidth: 120,
editRender: { enabled: false, name: 'NInput' }
},
{
field: 'attrCode',
title: '扩展属性',
minWidth: 100,
editRender: {
name: 'NInput',
props: {
placeholder: '请输入备注'
}
}
},
{
field: 'attrVal',
title: '属性值',
minWidth: 100,
editRender: {
name: 'NInput',
props: {
placeholder: '请输入属性值'
}
}
},
{
title: '操作',
width: 200,
fixed: 'right',
slots: { default: 'operation' }
}
];
}
/**
* @func: personPicTableColumns
* @description: 人员图片table列字段配置
* @param {*}
* @return {VxeGridPropTypes.Columns}
* @example:
*/
export function personPicTableColumns(): VxeGridPropTypes.Columns {
return [
{ type: 'seq', width: 60, title: '序号', fixed: 'left' },
{ type: 'checkbox', width: 45 },
{ field: 'id', title: 'ID', minWidth: 100, visible: false },
{
field: 'displayName',
title: '人员名称',
minWidth: 120,
editRender: { enabled: false, name: 'NInput' }
},
{
field: 'type',
title: '照片类别',
minWidth: 100,
editRender: {
name: 'baseDictRender',
props: {
autoLoadData: true,
dictCode: 'person_pic_type'
}
}
},
{
field: 'url',
title: '照片路径',
minWidth: 100,
editRender: {
name: 'NInput',
props: {
placeholder: '请输入属性值'
}
}
},
{
field: 'memo',
title: '备注',
editRender: {
name: 'NInput',
props: {
type: 'textarea',
autosize: { minRows: 1, maxRows: 1 },
placeholder: '请输入备注'
}
}
},
{
title: '操作',
width: 200,
fixed: 'right',
slots: { default: 'operation' }
}
];
}
定义接口声明
在/src/service/api/cbb/base
目录下创建名为person.ts
的文件,其中cbb
表示框架,base
表示模块,person
表示业务功能;我们在业务系统中应该在api
目录下创建框架名,依次是模块,业务功能。
表示请求后台的接口数据,提供页面调用加载数据。 声明内容如下
ts
import { EnumContentType } from '@/enum';
import { request } from '@/service/request';
export function getPersonForm(params: any) {
return request.get<any>('/base/person/form', {
params
});
}
export function getPersonPage(params: any) {
return request.get<any>('/base/person/page', {
params
});
}
export function doPersonAdd(data: any) {
return request.post<any>('/base/person/add', data, {
encryption: { type: 'aes' }
});
}
export function doPersonEdit(data: any) {
return request.post<any>('/base/person/edit', data, {
encryption: { type: 'aes' }
});
}
export function doPersonDelete(data: any) {
return request.post<any>('/base/person/delete', data, {
encryption: { type: 'aes' }
});
}
export function doPersonResetPassword(data: any) {
return request.post<any>('/base/person/resetPassword', data, {
encryption: { type: 'aes' }
});
}
export function doUnlockedPerson(data: any) {
return request.post<any>('/base/person/unlocked', data, {
encryption: {
type: 'aes',
param: ['personId'],
paramDataKey: 'personId'
},
headers: { 'Content-Type': EnumContentType.formUrlencoded }
});
}
export function getPersonListByIds(params: any) {
return request.get<any>('/base/person/listByIds', {
params
});
}
export function doBindBigAntAccount(data: any) {
return request.post<any>('/base/person/bindBigAntAccount', data, {
encryption: { type: 'aes' }
});
}
export function getBindInfo() {
return request.get<any>('/base/person/getBindInfo', {});
}
export function cancelBindBigAnt() {
return request.post<any>('/base/person/cancelBindBigAnt', null, {
encryption: { type: 'aes' }
});
}
/**
* 停用人员
* @param data
* @returns axios实例
*/
export function doPersonDisableStatus(data: any) {
return request.post<any>('/base/person/disableStatus', data, {
encryption: {
type: 'aes',
param: ['personId'],
paramDataKey: 'personId'
},
headers: { 'Content-Type': EnumContentType.formUrlencoded }
});
}
/**
* @func: getOrgCanAddPersonPage
* @description: 查询人员列表,根据传入组织查询上级组织的人员,用于判断添加上级组织中是否存在该用户,只有存在才能返回。
* @param {any} params
* @return {*}
* @example:
*/
export function getOrgCanAddPersonPage(params: any) {
return request.get<any>('/base/person/orgCanAddPersonPage', { params });
}
/**
* @func: doUploadPersonEsign
* @description: 上传人员的签名照
* @param {any} data
* @return {*}
* @example:
*/
export function doUploadPersonEsign(data: any) {
return request.post<any>('/base/person/upload/esign', data, {
headers: { 'Content-Type': EnumContentType.formData }
});
}
/**
* @func: doUploadPersonEsign
* @description: 删除人员的签名照
* @return {*}
* @example:
*/
export function doDeletePersonEsign() {
return request.post<any>('/base/person/delete/esign', {});
}
/**
* @func: getPersonPicInfo
* @description: 获取人员图片信息
* @return {*}
* @example:
*/
export function getPersonPicInfo() {
return request.get<any>('/base/person/pic/info');
}
创建功能页
在/src/views/cbb/base/person
目录下创建名为index.ts
的文件,其中cbb
表示框架,base
表示模块,person
表示业务功能;我们在业务系统中应该在views
目录下创建框架名,依次是模块,业务功能。 声明内容如下
vue
<template>
<div>
<n-card :bordered="false" class="rounded-16px shadow-sm person-container">
<crud-tabs :crud-key="crudKey">
<template #index>
<vxe-grid
ref="personTableRef"
v-bind="personTableOptions"
@form-submit="personProxy.queryTableData"
@page-change="personProxy.handlePageChange"
@sort-change="personProxy.queryTableData"
@form-reset="personProxy.handleResetForm"
@current-change="handlePersonTableCurrentChange"
>
<!--自定义插槽 toolbar buttons 插槽-->
<template #toolbar_buttons>
<n-space>
<n-button
v-permission="{ permission: ['base:person:add'] }"
type="primary"
@click="personProxy.handleAddPane({ name: 'add', tab: '人员新增', params: {} })"
>
新增
</n-button>
</n-space>
</template>
<template #operation="{ row }">
<n-space>
<n-button
v-if="row.status === '1'"
v-permission="{ permission: ['base:person:edit'] }"
text
type="primary"
@click="personProxy.handleAddPane({ name: 'edit', tab: '人员修改', params: row })"
>
修改
</n-button>
<n-button v-if="row.status === '1'" text type="info" @click="handlePersonStatus(row)"> 停用 </n-button>
<n-dropdown v-if="row.status === '1'" trigger="hover" :options="moreButton.options({ row })">
<n-button type="primary" text> 更多 </n-button>
</n-dropdown>
</n-space>
</template>
</vxe-grid>
</template>
<template #add="{ tabPane }">
<person-form :crud-key="crudKey" :tab-pane="tabPane"></person-form>
</template>
<template #view="{ tabPane }">
<person-form :crud-key="crudKey" :tab-pane="tabPane"></person-form>
</template>
<template #edit="{ tabPane }">
<person-form :crud-key="crudKey" :tab-pane="tabPane"></person-form>
</template>
</crud-tabs>
</n-card>
<vxe-modal v-model="personExtModal" destroy-on-close min-width="600" resize :title="'人员扩展属性'" width="1200">
<template #default>
<person-ext ref="personExtRef" :current-person="currentPersonRow" @on-finished="handlePersonExtFinished" />
</template>
</vxe-modal>
<vxe-modal v-model="personPicModal" destroy-on-close min-width="600" resize :title="'人员照片'" width="1200">
<template #default>
<person-pic ref="personPicRef" :current-person="currentPersonRow" @on-finished="handlePersonPicFinished" />
</template>
</vxe-modal>
</div>
</template>
<script setup lang="ts">
import { reactive, ref, onMounted } from 'vue';
import { useRoute } from 'vue-router';
import type { VxeTableInstance, VxeGridProps } from 'vxe-table';
import { useCrudStore } from '@/store';
import { usePermission } from '@/composables';
import { baseTableHeight } from '@/utils';
import { doPersonDelete, getPersonPage, doPersonResetPassword, doPersonDisableStatus } from '@/service/api/cbb/base';
import { tableMenuConfig, tablePagerConfig, tableToolbarConfig, crudProxy } from '@/const';
import { personTableColumns, personTableFormConfig } from '@/const/crud/cbb/base';
import personForm from './form.vue';
import PersonExt from './components/PersonExt.vue';
import PersonPic from './components/PersonPic.vue';
const route = useRoute();
const crudKey = route.path;
const { addCrudTab } = useCrudStore();
addCrudTab({ crudKey, pane: { name: 'index', tab: '人员管理' } });
const personProxy: Crud.CrudProxyInstance = crudProxy();
const personTableRef = ref({} as VxeTableInstance);
const orgPersonRef = ref({} as any);
const userRef = ref({} as any);
const personExtRef = ref({} as VxeTableInstance);
const personPicRef = ref({} as VxeTableInstance);
const personExtModal = ref(false);
const personPicModal = ref(false);
const currentPersonRow = ref({});
const currentPersonRowRight = ref({ status: '1' });
const tableHeight = baseTableHeight();
const personTableOptions = reactive<VxeGridProps>({
columns: personTableColumns(),
data: [],
formConfig: personTableFormConfig(),
keyboardConfig: { isArrow: true },
loading: false,
pagerConfig: tablePagerConfig(),
toolbarConfig: {
...tableToolbarConfig(),
refresh: { query: personProxy.queryTableData }
},
exportConfig: {},
printConfig: {},
menuConfig: tableMenuConfig(),
params: {
COPY_ADD: personProxy.handleCopyAdd
},
height: tableHeight
});
onMounted(() => {
personProxy.init({
tableOptions: personTableOptions,
tableRef: personTableRef.value,
crudKey,
queryApi: getPersonPage,
deleteApi: doPersonDelete,
crudName: '人员管理',
copyAddPaneName: 'add'
});
personProxy.queryTableData();
});
/**
* @func: handlePersonExtList
* @description: 处理人员扩展属性列表显示
* @param {*} row
* @return {*}
* @example:
*/
function handlePersonExtList(row: any) {
personExtModal.value = true;
currentPersonRow.value = row;
}
/**
* @func: handleOrgPersonFinished
* @description: 人员扩展属性列表处理完成
* @param {*}
* @return {*}
* @example:
*/
function handlePersonExtFinished() {
personExtModal.value = false;
personProxy.queryTableData();
}
/**
* @func: handlePersonPicList
* @description: 处理人图片列表显示
* @param {*} row
* @return {*}
* @example:
*/
function handlePersonPicList(row: any) {
personPicModal.value = true;
currentPersonRow.value = row;
}
/**
* @func: handlePersonPicFinished
* @description: 人员图片列表处理完成
* @param {*}
* @return {*}
* @example:
*/
function handlePersonPicFinished() {
personPicModal.value = false;
personProxy.queryTableData();
}
function handlePersonResetPassword(data: any[]) {
window.$dialog?.warning({
title: '重置密码',
content: '您确定要重置用户密码吗?',
positiveText: '确定',
negativeText: '取消',
maskClosable: false,
onPositiveClick: () => {
doPersonResetPassword(data).then(() => {
window.$message?.success('重置密码成功');
});
}
});
}
/**
* 更多按钮选项
* @param params
*/
const moreButton = {
options(params: any) {
const { filterDropdownOptions } = usePermission();
return filterDropdownOptions([
{
label: '重置密码',
key: 'authorizedatascope',
permissions: { permission: ['base:person:reset_password'] },
props: {
onClick: () => {
handlePersonResetPassword([params.row.id]);
}
}
},
{
label: '扩展信息',
key: 'extInfo',
permissions: { permission: ['base:person:ext'] },
props: {
onClick: () => {
handlePersonExtList(params.row);
}
}
},
{
label: '照片信息',
key: 'picInfo',
permissions: { permission: ['base:person:pic'] },
props: {
onClick: () => {
handlePersonPicList(params.row);
}
}
}
]);
}
};
function handlePersonTableCurrentChange({ row }: any) {
currentPersonRowRight.value = row;
}
async function handlePersonStatus(row: any) {
if (row.status === '1') {
const { error } = await doPersonDisableStatus({ personId: row.id });
if (!error) {
window.$message?.success('停用成功');
personProxy.queryTableData();
orgPersonRef.value.fetchOrgPersonTableData();
userRef.value.userProxy.queryTableData();
}
}
}
</script>
<style scoped></style>
创建表单页
在/src/views/cbb/base/person
目录下创建名为form.ts
的文件,其中cbb
表示框架,base
表示模块,person
表示业务功能;我们在业务系统中应该在views
目录下创建框架名,依次是模块,业务功能。 声明内容如下
vue
<template>
<div class="person-container">
<vxe-form ref="personFormRef" v-bind="personFormOptions" @submit="personFormProxy.handleSave">
<template #formOperation>
<n-space justify="center">
<n-button v-if="props.tabPane.name !== 'view'" type="primary" attr-type="submit"> 保存 </n-button>
<n-button v-if="props.tabPane.name === 'add'" @click="personFormProxy.handleResetForm"> 重置 </n-button>
<n-button @click="personFormProxy.handleClosePane"> 关闭 </n-button>
</n-space>
</template>
</vxe-form>
</div>
</template>
<script setup lang="ts">
import { reactive, onMounted, ref } from 'vue';
import type { VxeFormInstance, VxeFormProps } from 'vxe-table';
import { getPersonForm, doPersonAdd, doPersonEdit } from '@/service/api/cbb/base';
import { personFormData, personFormRules, personFormItems } from '@/const/crud/cbb/base';
import { crudFormProxy } from '@/const';
interface Props {
crudKey: string | number;
tabPane: Crud.CrudTabPaneProps;
}
const props = withDefaults(defineProps<Props>(), {
crudKey: '',
tabPane: () => {
return { name: '', params: {} };
}
});
const personFormProxy: Crud.CrudFormProxyInstance = crudFormProxy();
const personFormRef = ref({} as VxeFormInstance);
const personFormOptions = reactive<VxeFormProps>({
loading: false,
titleAlign: 'right',
titleWidth: '80',
data: personFormData(),
items: personFormItems(),
rules: personFormRules()
});
onMounted(() => {
personFormProxy.init({
props,
formOptions: personFormOptions,
formRef: personFormRef.value,
queryApi: getPersonForm,
addApi: doPersonAdd,
editApi: doPersonEdit
});
personFormProxy.handleAutoLoad();
});
</script>
<style lang="scss" scoped></style>