first commit
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { Modal } from 'ant-design-vue';
|
||||
|
||||
enum Api {
|
||||
list = '/sys/category/rootList',
|
||||
save = '/sys/category/add',
|
||||
edit = '/sys/category/edit',
|
||||
deleteCategory = '/sys/category/delete',
|
||||
deleteBatch = '/sys/category/deleteBatch',
|
||||
importExcel = '/sys/category/importExcel',
|
||||
exportXls = '/sys/category/exportXls',
|
||||
loadTreeData = '/sys/category/loadTreeRoot',
|
||||
getChildList = '/sys/category/childList',
|
||||
getChildListBatch = '/sys/category/getChildListBatch',
|
||||
}
|
||||
/**
|
||||
* 导出api
|
||||
* @param params
|
||||
*/
|
||||
export const getExportUrl = Api.exportXls;
|
||||
/**
|
||||
* 导入api
|
||||
* @param params
|
||||
*/
|
||||
export const getImportUrl = Api.importExcel;
|
||||
/**
|
||||
* 列表接口
|
||||
* @param params
|
||||
*/
|
||||
export const list = (params) => defHttp.get({ url: Api.list, params });
|
||||
/**
|
||||
* 删除
|
||||
*/
|
||||
export const deleteCategory = (params, handleSuccess) => {
|
||||
return defHttp.delete({ url: Api.deleteCategory, params }, { joinParamsToUrl: true }).then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 批量删除
|
||||
* @param params
|
||||
*/
|
||||
export const batchDeleteCategory = (params, handleSuccess) => {
|
||||
Modal.confirm({
|
||||
title: '确认删除',
|
||||
content: '是否删除选中数据',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
onOk: () => {
|
||||
return defHttp.delete({ url: Api.deleteBatch, data: params }, { joinParamsToUrl: true }).then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
/**
|
||||
* 保存或者更新
|
||||
* @param params
|
||||
*/
|
||||
export const saveOrUpdateDict = (params, isUpdate) => {
|
||||
let url = isUpdate ? Api.edit : Api.save;
|
||||
return defHttp.post({ url: url, params });
|
||||
};
|
||||
/**
|
||||
* 查询全部树形节点数据
|
||||
* @param params
|
||||
*/
|
||||
export const loadTreeData = (params) => defHttp.get({ url: Api.loadTreeData, params });
|
||||
/**
|
||||
* 查询子节点数据
|
||||
* @param params
|
||||
*/
|
||||
export const getChildList = (params) => defHttp.get({ url: Api.getChildList, params });
|
||||
/**
|
||||
* 批量查询子节点数据
|
||||
* @param params
|
||||
*/
|
||||
export const getChildListBatch = (params) => defHttp.get({ url: Api.getChildListBatch, params }, { isTransformResponse: false });
|
||||
@@ -0,0 +1,67 @@
|
||||
import { BasicColumn } from '/@/components/Table';
|
||||
import { FormSchema } from '/@/components/Table';
|
||||
|
||||
export const columns: BasicColumn[] = [
|
||||
{
|
||||
title: '分类名称',
|
||||
dataIndex: 'name',
|
||||
width: 350,
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: '分类编码',
|
||||
dataIndex: 'code',
|
||||
},
|
||||
];
|
||||
|
||||
export const searchFormSchema: FormSchema[] = [
|
||||
{
|
||||
label: '名称',
|
||||
field: 'name',
|
||||
component: 'JInput',
|
||||
colProps: { span: 6 },
|
||||
},
|
||||
{
|
||||
label: '编码',
|
||||
field: 'code',
|
||||
component: 'JInput',
|
||||
colProps: { span: 6 },
|
||||
},
|
||||
];
|
||||
|
||||
export const formSchema: FormSchema[] = [
|
||||
{
|
||||
label: '',
|
||||
field: 'id',
|
||||
component: 'Input',
|
||||
show: false,
|
||||
},
|
||||
{
|
||||
label: '父级节点',
|
||||
field: 'pid',
|
||||
component: 'TreeSelect',
|
||||
componentProps: {
|
||||
//update-begin---author:wangshuai ---date:20230829 for:replaceFields已过期,使用fieldNames代替------------
|
||||
fieldNames: {
|
||||
//update-end---author:wangshuai ---date:20230829 for:replaceFields已过期,使用fieldNames代替------------
|
||||
value: 'key',
|
||||
},
|
||||
dropdownStyle: {
|
||||
maxHeight: '50vh',
|
||||
},
|
||||
getPopupContainer: () => document.body,
|
||||
},
|
||||
show: ({ values }) => {
|
||||
return values.pid !== '0';
|
||||
},
|
||||
dynamicDisabled: ({ values }) => {
|
||||
return !!values.id;
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '分类名称',
|
||||
field: 'name',
|
||||
required: true,
|
||||
component: 'Input',
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,107 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose width="550px" :title="getTitle" @ok="handleSubmit">
|
||||
<BasicForm @register="registerForm" />
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, unref } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/src/components/Modal';
|
||||
import { BasicForm, useForm } from '/src/components/Form';
|
||||
import { formSchema } from '../category.data';
|
||||
import { loadTreeData, saveOrUpdateDict } from '../category.api';
|
||||
// 获取emit
|
||||
const emit = defineEmits(['register', 'success']);
|
||||
const isUpdate = ref(true);
|
||||
const expandedRowKeys = ref([]);
|
||||
const treeData = ref([]);
|
||||
const isSubAdd = ref(false);
|
||||
//表单配置
|
||||
const [registerForm, { resetFields, setFieldsValue, validate, updateSchema }] = useForm({
|
||||
schemas: formSchema,
|
||||
showActionButtonGroup: false,
|
||||
labelCol: {
|
||||
xs: { span: 24 },
|
||||
sm: { span: 4 },
|
||||
},
|
||||
wrapperCol: {
|
||||
xs: { span: 24 },
|
||||
sm: { span: 18 },
|
||||
},
|
||||
});
|
||||
//表单赋值
|
||||
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
|
||||
//重置表单
|
||||
await resetFields();
|
||||
expandedRowKeys.value = [];
|
||||
setModalProps({ confirmLoading: false, minHeight: 80 });
|
||||
isUpdate.value = !!data?.isUpdate;
|
||||
//update-begin---author:wangshuai ---date: 20230829 for:分类字典data.record为空报错------------
|
||||
isSubAdd.value = !data?.isUpdate && data.record && data.record.id;
|
||||
//update-end---author:wangshuai ---date: 20230829 for:分类字典data.record为空报错------------
|
||||
if (data?.record) {
|
||||
//表单赋值
|
||||
await setFieldsValue({
|
||||
...data.record,
|
||||
});
|
||||
}
|
||||
//父级节点树信息
|
||||
treeData.value = await loadTreeData({ async: false, pcode: '' });
|
||||
updateSchema({
|
||||
field: 'pid',
|
||||
componentProps: { treeData },
|
||||
});
|
||||
});
|
||||
//设置标题
|
||||
const getTitle = computed(() => (!unref(isUpdate) ? '新增字典' : '编辑字典'));
|
||||
|
||||
/**
|
||||
* 根据pid获取展开的节点
|
||||
* @param pid
|
||||
* @param arr
|
||||
*/
|
||||
function getExpandKeysByPid(pid, arr) {
|
||||
if (pid && arr && arr.length > 0) {
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (arr[i].key == pid && unref(expandedRowKeys).indexOf(pid) < 0) {
|
||||
//需要获取同一级的key
|
||||
getSameLevelExpandKeysByPid(arr[i]);
|
||||
expandedRowKeys.value.push(arr[i].key);
|
||||
getExpandKeysByPid(arr[i]['parentId'], unref(treeData));
|
||||
} else {
|
||||
getExpandKeysByPid(pid, arr[i].children);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//表单提交事件
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
let values = await validate();
|
||||
setModalProps({ confirmLoading: true });
|
||||
//提交表单
|
||||
await saveOrUpdateDict(values, isUpdate.value);
|
||||
//关闭弹窗
|
||||
closeModal();
|
||||
//展开的节点信息
|
||||
await getExpandKeysByPid(values['pid'], unref(treeData));
|
||||
//刷新列表(isUpdate:是否编辑;values:表单信息;expandedArr:展开的节点信息)
|
||||
emit('success', { isUpdate: unref(isUpdate), isSubAdd:unref(isSubAdd), values: { ...values }, expandedArr: unref(expandedRowKeys).reverse() });
|
||||
} finally {
|
||||
setModalProps({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取同一级的id和同一级的子级id
|
||||
*/
|
||||
function getSameLevelExpandKeysByPid(arr) {
|
||||
if (arr.children && arr.children.length > 0) {
|
||||
for (const children of arr.children) {
|
||||
if (unref(expandedRowKeys).indexOf(children.key) < 0 && children.children && children.children.length > 0) {
|
||||
getSameLevelExpandKeysByPid(children);
|
||||
expandedRowKeys.value.push(children.key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,298 @@
|
||||
<template>
|
||||
<div>
|
||||
<!--引用表格-->
|
||||
<BasicTable
|
||||
@register="registerTable"
|
||||
:rowSelection="rowSelection"
|
||||
:expandedRowKeys="expandedRowKeys"
|
||||
@expand="handleExpand"
|
||||
@fetch-success="onFetchSuccess"
|
||||
>
|
||||
<!--插槽:table标题-->
|
||||
<template #tableTitle>
|
||||
<a-button type="primary" preIcon="ant-design:plus-outlined" @click="handleCreate"> 新增</a-button>
|
||||
<a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
|
||||
<j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
|
||||
<a-dropdown v-if="selectedRowKeys.length > 0">
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="1" @click="batchHandleDelete">
|
||||
<Icon icon="ant-design:delete-outlined"></Icon>
|
||||
删除
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button
|
||||
>批量操作
|
||||
<Icon icon="ant-design:down-outlined"></Icon>
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
<!--操作栏-->
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getTableAction(record)" />
|
||||
</template>
|
||||
</BasicTable>
|
||||
<!--字典弹窗-->
|
||||
<CategoryModal @register="registerModal" @success="handleSuccess" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="system-category" setup>
|
||||
//ts语法
|
||||
import { ref, computed, unref, toRaw, nextTick } from 'vue';
|
||||
import { BasicTable, useTable, TableAction } from '/src/components/Table';
|
||||
import { useDrawer } from '/src/components/Drawer';
|
||||
import CategoryModal from './components/CategoryModal.vue';
|
||||
import { useModal } from '/src/components/Modal';
|
||||
import { useMethods } from '/src/hooks/system/useMethods';
|
||||
import { columns, searchFormSchema } from './category.data';
|
||||
import { list, deleteCategory, batchDeleteCategory, getExportUrl, getImportUrl, getChildList, getChildListBatch } from './category.api';
|
||||
import { useListPage } from '/@/hooks/system/useListPage';
|
||||
|
||||
const expandedRowKeys = ref([]);
|
||||
const { handleExportXls, handleImportXls } = useMethods();
|
||||
//字典model
|
||||
const [registerModal, { openModal }] = useModal();
|
||||
// 列表页面公共参数、方法
|
||||
const { prefixCls, onExportXls, onImportXls, tableContext } = useListPage({
|
||||
designScope: 'category-template',
|
||||
tableProps: {
|
||||
title: '分类字典',
|
||||
api: list,
|
||||
columns: columns,
|
||||
actionColumn: {
|
||||
width: 180,
|
||||
},
|
||||
formConfig: {
|
||||
schemas: searchFormSchema,
|
||||
},
|
||||
isTreeTable: true,
|
||||
},
|
||||
exportConfig: {
|
||||
name: '分类字典列表',
|
||||
url: getExportUrl,
|
||||
},
|
||||
importConfig: {
|
||||
url: getImportUrl,
|
||||
},
|
||||
});
|
||||
|
||||
//注册table数据
|
||||
const [registerTable, { reload, collapseAll, updateTableDataRecord, findTableDataRecord, getDataSource }, { rowSelection, selectedRowKeys }] =
|
||||
tableContext;
|
||||
|
||||
/**
|
||||
* 新增事件
|
||||
*/
|
||||
function handleCreate() {
|
||||
openModal(true, {
|
||||
isUpdate: false,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑事件
|
||||
*/
|
||||
async function handleEdit(record) {
|
||||
openModal(true, {
|
||||
record,
|
||||
isUpdate: true,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 详情
|
||||
*/
|
||||
async function handleDetail(record) {
|
||||
openModal(true, {
|
||||
record,
|
||||
isUpdate: true,
|
||||
hideFooter: true,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除事件
|
||||
*/
|
||||
async function handleDelete(record) {
|
||||
await deleteCategory({ id: record.id }, importSuccess);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除事件
|
||||
*/
|
||||
async function batchHandleDelete() {
|
||||
const ids = selectedRowKeys.value.filter((item) => !item.includes('loading'));
|
||||
await batchDeleteCategory({ ids: ids }, importSuccess);
|
||||
}
|
||||
/**
|
||||
* 导入
|
||||
*/
|
||||
function importSuccess() {
|
||||
//update-begin---author:wangshuai ---date:20220530 for:[issues/54]树字典,勾选,然后批量删除,系统错误------------
|
||||
(selectedRowKeys.value = []) && reload();
|
||||
//update-end---author:wangshuai ---date:20220530 for:[issues/54]树字典,勾选,然后批量删除,系统错误--------------
|
||||
}
|
||||
/**
|
||||
* 添加下级
|
||||
*/
|
||||
function handleAddSub(record) {
|
||||
openModal(true, {
|
||||
record,
|
||||
isUpdate: false,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 成功回调
|
||||
*/
|
||||
async function handleSuccess({ isUpdate,isSubAdd, values, expandedArr }) {
|
||||
if (isUpdate) {
|
||||
//编辑回调
|
||||
updateTableDataRecord(values.id, values);
|
||||
} else {
|
||||
if (!values['pid']) {
|
||||
//新增根节点
|
||||
reload();
|
||||
} else {
|
||||
//新增子集
|
||||
//update-begin-author:liusq---date:20230411--for: [issue/4550]分类字典数据量过多会造成数据查询时间过长---
|
||||
if(isSubAdd){
|
||||
await expandTreeNode(values.pid);
|
||||
//update-end-author:liusq---date:20230411--for: [issue/4550]分类字典数据量过多会造成数据查询时间过长---
|
||||
}else{
|
||||
//update-begin-author:wangshuai---date:20240319--for: 字典树删除之后其他节点出现loading---
|
||||
//expandedRowKeys.value = [];
|
||||
//update-end-author:wangshuai---date:20240319--for: 字典树删除之后其他节点出现loading---
|
||||
for (let key of unref(expandedArr)) {
|
||||
await expandTreeNode(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 接口请求成功后回调
|
||||
*/
|
||||
function onFetchSuccess(result) {
|
||||
getDataByResult(result.items) && loadDataByExpandedRows();
|
||||
}
|
||||
/**
|
||||
* 根据已展开的行查询数据(用于保存后刷新时异步加载子级的数据)
|
||||
*/
|
||||
async function loadDataByExpandedRows() {
|
||||
if (unref(expandedRowKeys).length > 0) {
|
||||
const res = await getChildListBatch({ parentIds: unref(expandedRowKeys).join(',') });
|
||||
if (res.success && res.result.records.length > 0) {
|
||||
//已展开的数据批量子节点
|
||||
let records = res.result.records;
|
||||
const listMap = new Map();
|
||||
for (let item of records) {
|
||||
let pid = item['pid'];
|
||||
if (unref(expandedRowKeys).includes(pid)) {
|
||||
let mapList = listMap.get(pid);
|
||||
if (mapList == null) {
|
||||
mapList = [];
|
||||
}
|
||||
mapList.push(item);
|
||||
listMap.set(pid, mapList);
|
||||
}
|
||||
}
|
||||
let childrenMap = listMap;
|
||||
let fn = (list) => {
|
||||
if (list) {
|
||||
list.forEach((data) => {
|
||||
if (unref(expandedRowKeys).includes(data.id)) {
|
||||
data.children = getDataByResult(childrenMap.get(data.id));
|
||||
fn(data.children);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
fn(getDataSource());
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 处理数据集
|
||||
*/
|
||||
function getDataByResult(result) {
|
||||
if (result && result.length > 0) {
|
||||
return result.map((item) => {
|
||||
//判断是否标记了带有子节点
|
||||
if (item['hasChild'] == '1') {
|
||||
let loadChild = { id: item.id + '_loadChild', name: 'loading...', isLoading: true };
|
||||
item.children = [loadChild];
|
||||
}
|
||||
return item;
|
||||
});
|
||||
}
|
||||
}
|
||||
/**
|
||||
*树节点展开合并
|
||||
* */
|
||||
async function handleExpand(expanded, record) {
|
||||
// 判断是否是展开状态,展开状态(expanded)并且存在子集(children)并且未加载过(isLoading)的就去查询子节点数据
|
||||
if (expanded) {
|
||||
expandedRowKeys.value.push(record.id);
|
||||
if (record.children.length > 0 && !!record.children[0].isLoading) {
|
||||
let result = await getChildList({ pid: record.id });
|
||||
if (result && result.length > 0) {
|
||||
record.children = getDataByResult(result);
|
||||
} else {
|
||||
record.children = null;
|
||||
record.hasChild = '0';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let keyIndex = expandedRowKeys.value.indexOf(record.id);
|
||||
if (keyIndex >= 0) {
|
||||
expandedRowKeys.value.splice(keyIndex, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
*操作表格后处理树节点展开合并
|
||||
* */
|
||||
async function expandTreeNode(key) {
|
||||
let record:any = findTableDataRecord(key);
|
||||
//update-begin-author:liusq---date:20230411--for: [issue/4550]分类字典数据量过多会造成数据查询时间过长,显示“接口请求超时,请刷新页面重试!”---
|
||||
if(!expandedRowKeys.value.includes(key)){
|
||||
expandedRowKeys.value.push(key);
|
||||
}
|
||||
//update-end-author:liusq---date:20230411--for: [issue/4550]分类字典数据量过多会造成数据查询时间过长,显示“接口请求超时,请刷新页面重试!”---
|
||||
let result = await getChildList({ pid: key });
|
||||
if (result && result.length > 0) {
|
||||
record.children = getDataByResult(result);
|
||||
} else {
|
||||
record.children = null;
|
||||
record.hasChild = '0';
|
||||
}
|
||||
updateTableDataRecord(key, record);
|
||||
}
|
||||
/**
|
||||
* 操作栏
|
||||
*/
|
||||
function getTableAction(record) {
|
||||
return [
|
||||
{
|
||||
label: '编辑',
|
||||
onClick: handleEdit.bind(null, record),
|
||||
},
|
||||
{
|
||||
label: '删除',
|
||||
popConfirm: {
|
||||
title: '确定删除吗?',
|
||||
confirm: handleDelete.bind(null, record),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '添加下级',
|
||||
onClick: handleAddSub.bind(null, { pid: record.id }),
|
||||
},
|
||||
];
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
Reference in New Issue
Block a user