级联数据 组件懒加载

This commit is contained in:
TimSpan 2024-11-01 14:29:45 +08:00
parent a35abcd9cd
commit 93136c7295
6 changed files with 235 additions and 136 deletions

View File

@ -16,6 +16,7 @@ declare module 'vue' {
AConfigProvider: typeof import('ant-design-vue/es')['ConfigProvider']
ADatePicker: typeof import('ant-design-vue/es')['DatePicker']
ADivider: typeof import('ant-design-vue/es')['Divider']
AdministrativeDivisionTree: typeof import('./src/components/tree/AdministrativeDivisionTree.vue')['default']
ADropdown: typeof import('ant-design-vue/es')['Dropdown']
AForm: typeof import('ant-design-vue/es')['Form']
AFormItem: typeof import('ant-design-vue/es')['FormItem']

View File

@ -1,18 +1,24 @@
declare const __APP_ENV: ImportMetaEnv;
class TreeNodeVo<T, E = Record<string, any>> {
value: T;
parentValue: T;
label: string;
orderIndex?: number;
children?: TreeNodeVo<T>[]
extData?: E;
}
declare global {
/**
/**
*
*/
interface JsonResult<T> {
interface JsonResult<T> {
code: number;
message: string;
data?: T;
}
}
export interface SecurityUnitPagerQueryParams {
export interface SecurityUnitPagerQueryParams {
/** 名称 **/
name?: string;
/** 社会编码 **/
@ -23,20 +29,20 @@ declare global {
isEnable?: number;
/** 审核状态 **/
checkStatus?: number;
}
interface BaseEnum<T> {
}
interface BaseEnum<T> {
value: T;
label: string
}
class TreeNodeVo<T, E = Record<string, any>> {
}
class TreeNodeVo<T, E = Record<string, any>> {
value: T;
parentValue: T;
label: string;
orderIndex?: number;
children?: TreeNodeVo<T>[]
extData?: E;
}
declare interface Grid {
}
interface Grid {
//栅格占据的列数
span?: number;
//栅格左侧的间隔格数
@ -55,8 +61,8 @@ declare global {
lg?: number;
//≥1920px 响应式栅格数或者栅格属性对象
xl?: number;
}
interface dataStatus {
}
interface dataStatus {
account: string;
password: string;
remark: string;
@ -67,35 +73,35 @@ declare global {
label: string;
value: number;
};
}
class SelectNodeVo<T, E = Record<string, any>> {
}
class SelectNodeVo<T, E = Record<string, any>> {
value: T;
label: string;
options?: SelectNodeVo<T>[]
orderIndex?: number;
disabled?: boolean;
extData?: E
}
}
interface ExtData {
interface ExtData {
color?: string;
}
}
interface Option {
interface Option {
label: string;
value: string | number;
extData?: ExtData | null;
}
}
interface OptionsResponse {
interface OptionsResponse {
IsEnable: Option[];
IsOrNot: Option[];
Sex: Option[];
CheckStatus: Option[];
ServiceProjectType: Option[];
DeleteFlag: Option[];
}
}

View File

@ -67,6 +67,7 @@
:allowClear="item.componentsProps?.allowClear ?? true"
:options="item.options"
/>
<administrative-division-tree-comp v-else-if="item.type === 'administrativeDivisionTree'" style="width: 100%" v-model:value="modelValue[field]" v-bind="item.componentsProps" />
<a-range-picker
v-else-if="item.type === 'rangePicker'"
style="width: 100%"
@ -111,10 +112,14 @@
<script setup lang="ts" generic="T extends Record<string,any>">
import { FormInstance } from 'ant-design-vue'
import { ref } from 'vue'
import { defineAsyncComponent, ref } from 'vue'
import { FormExpose } from 'ant-design-vue/es/form/Form'
import { QuestionCircleOutlined } from '@ant-design/icons-vue'
import { FormProMaxItemOptions, FormProMaxItemProps, FormProMaxProps } from '@/types/components/form/index.ts'
import { ComponentProps } from 'vue-component-type-helpers'
import AdministrativeDivisionTree from '@/components/tree/AdministrativeDivisionTree.vue'
const AdministrativeDivisionTreeComp: ComponentProps<typeof AdministrativeDivisionTree> = defineAsyncComponent(() => import('@/components/tree/AdministrativeDivisionTree.vue'))
const modelValue = defineModel<T>('value', {
default: {},
@ -147,7 +152,7 @@ const props = withDefaults(defineProps<FormProMaxProps<T>>(), {
scrollToFirstError: undefined,
validateOnRuleChange: undefined,
})
console.log(props)
const formProMaxRef = ref<FormInstance>(null!)
const getResponsive = (item: FormProMaxItemProps): Grid => {

View File

@ -0,0 +1,84 @@
<template>
<a-cascader
v-model:value="modelValue"
:placeholder="placeholder"
:change-on-select="changeOnSelect"
:options="administrativeDivisionTree"
:load-data="loadData"
:allow-clear="allowClear"
/>
</template>
<script setup lang="ts">
import api from "@/axios";
import {onMounted, ref} from "vue";
import {CascaderProps} from "ant-design-vue";
import {isEmpty} from "lodash-es";
withDefaults(defineProps<{
placeholder?: string,
changeOnSelect?: boolean
allowClear?: boolean
}>(), {
placeholder: '请选择行政区划',
changeOnSelect: true,
allowClear: true
})
const modelValue = defineModel('value', {
default: []
})
const administrativeDivisionTree = ref<TreeNodeVo<string>[]>([])
const loadData: CascaderProps['loadData'] = selectedOptions => {
const targetOption = selectedOptions[selectedOptions.length - 1];
targetOption.loading = true;
administrativeDivisionByParentCode(targetOption.value as string).then(data => {
targetOption.loading = false
targetOption.children = data
administrativeDivisionTree.value = [...administrativeDivisionTree.value]
})
}
/**
* 根据父级编码查询行政区划
* @param code
*/
const administrativeDivisionByParentCode = async (code: string = '0'): Promise<TreeNodeVo<string>[]> => {
const resp = await api.get<TreeNodeVo<string>[]>('/common/administrativeDivisionByParentCode', {
parentCode: code
})
//
return resp.data.map(item => {
delete item.children
return item
});
}
onMounted(async () => {
administrativeDivisionTree.value = await administrativeDivisionByParentCode()
if (!isEmpty(modelValue.value)) {
const ps = modelValue.value.map(code => administrativeDivisionByParentCode(code))
Promise.all(ps).then(data => {
let i = 0;
const deepChildren = (treeData: TreeNodeVo<string>[]) => {
treeData.forEach(item => {
if (item.value === modelValue.value[i]) {
item.children = data[i]
i++;
deepChildren(item.children)
}
})
}
deepChildren(administrativeDivisionTree.value)
})
}
})
</script>
<style scoped lang="scss">
</style>

View File

@ -15,6 +15,7 @@ import {
} from "ant-design-vue";
import { Ref, UnwrapRef, VNode } from "vue";
import { ComponentProps } from "vue-component-type-helpers";
import AdministrativeDivisionTree from "@/components/tree/AdministrativeDivisionTree.vue";
type FormProMaxItemType =
| 'custom'
@ -32,7 +33,8 @@ type FormProMaxItemType =
| 'datePicker'
| 'rangePicker'
| 'timeRangePicker'
| 'timePicker';
| 'timePicker'
| 'administrativeDivisionTree'
interface FormProMaxItemCommonProps extends ComponentProps<typeof FormItem> {
label?: string,
@ -64,6 +66,7 @@ export type FormProMaxItemOptions<T> = {
| FormProMaxItemProps<'rangePicker', ComponentProps<typeof RangePicker>>
| FormProMaxItemProps<'timeRangePicker', ComponentProps<typeof TimeRangePicker>>
| FormProMaxItemProps<'timePicker', ComponentProps<typeof TimePicker>>
| FormProMaxItemProps<'administrativeDivisionTree', ComponentProps<typeof AdministrativeDivisionTree>>
}
export interface FormProMaxProps<T = {}> extends FormProps {

View File

@ -1,8 +1,7 @@
<template>
<div>
<!-- 企事业单位 -->
<TableProMax ref="tableRef" :request-api="reqApi" :columns="columns" :searchFormOptions="searchFormOptions"
:scroll="{ x }">
<TableProMax ref="tableRef" :request-api="reqApi" :columns="columns" :searchFormOptions="searchFormOptions" :scroll="{ x }">
<!-- <template #tableHeader>
<a-space>
<a-button type="primary" @click="addUserManagement">新增用户</a-button>
@ -16,18 +15,18 @@
</template>
<script setup lang="tsx">
import {storeTreeData, loadTreeFromCache} from '@/utils/DB.ts'
// import { storeTreeData, loadTreeFromCache } from '@/utils/DB.ts'
import api from '@/axios'
import {ref, reactive} from 'vue'
import { ref, reactive } from 'vue'
import TableProMax from '@/components/table/TableProMax.vue'
import {TableProMaxProps} from '@/types/components/table/index.ts'
import {ComponentExposed} from 'vue-component-type-helpers'
import {dictSelectNodes} from '@/config/dict.ts'
import {publicUnitPagerQueryParams, FromItem} from '@/types/views/publicUnit.ts'
import { TableProMaxProps } from '@/types/components/table/index.ts'
import { ComponentExposed } from 'vue-component-type-helpers'
import { dictSelectNodes } from '@/config/dict.ts'
import { publicUnitPagerQueryParams, FromItem } from '@/types/views/publicUnit.ts'
// import FormProMax from '@/components/form/FormProMax.vue'
import {FormProMaxItemOptions} from '@/types/components/form//index.ts'
import {FormExpose} from 'ant-design-vue/es/form/Form'
import {message} from 'ant-design-vue'
import { FormProMaxItemOptions } from '@/types/components/form//index.ts'
import { FormExpose } from 'ant-design-vue/es/form/Form'
import { message } from 'ant-design-vue'
const formRef = ref<FormExpose>(null)
type TableProps = TableProMaxProps<publicUnitPagerQueryParams>
@ -46,7 +45,7 @@ const columns: TableProps['columns'] = [
{
dataIndex: 'provinceName',
title: '行政区划',
customRender: ({record}) => {
customRender: ({ record }) => {
return `${record?.provinceName}/${record?.cityName}/${record?.districtsName}/${record?.streetName}`
},
},
@ -72,14 +71,14 @@ const columns: TableProps['columns'] = [
{
dataIndex: 'contactPersonInfo',
title: '联系人姓名',
customRender: ({record}) => {
customRender: ({ record }) => {
return record?.contactPersonInfo?.name
},
},
{
dataIndex: 'contactPersonInfo',
title: '联系人手机号',
customRender: ({record}) => {
customRender: ({ record }) => {
return record?.contactPersonInfo?.telephone
},
},
@ -100,33 +99,34 @@ const addUserManagement = () => {
title.value = ''
}
const getTree = async () => {
//
const cachedData = await loadTreeFromCache()
if (cachedData) {
console.log('未发请求')
// 使
return cachedData
} else {
console.log('发起了请求')
// API
const res = await api.get<any>('/common/administrativeDivisionTree')
await storeTreeData(res.data)
return res.data
}
}
const loadOptions = async () => {
const treeData = await getTree()
searchFormOptions.treeSelect.options = treeData
}
loadOptions()
// const getTree = async () => {
// //
// const cachedData = await loadTreeFromCache()
// if (cachedData) {
// console.log('')
// // 使
// return cachedData
// } else {
// console.log('')
// // API
// const res = await api.get<any>('/common/administrativeDivisionTree')
// await storeTreeData(res.data)
// return res.data
// }
// }
// const loadOptions = async () => {
// const treeData = await getTree()
// searchFormOptions.treeSelect.options = treeData
// }
// loadOptions()
const searchFormOptions = reactive<TableProps['searchFormOptions']>({
name: {
type: 'input',
label: '名称',
},
treeSelect: {
type: 'cascader',
// type: 'cascader',
type: 'administrativeDivisionTree',
label: '行政区划',
},
telephone: {