feat:入驻单位入驻位置修改
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
<script setup lang="ts">
|
||||
import type { PropType } from 'vue';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { SyncOutlined } from '@ant-design/icons-vue';
|
||||
import { Empty, InputSearch, Skeleton, Tree } from 'ant-design-vue';
|
||||
import {communityTree} from "#/api/property/community";
|
||||
import type {CommunityVO} from "#/api/property/community/model";
|
||||
|
||||
defineOptions({ inheritAttrs: false });
|
||||
|
||||
withDefaults(defineProps<{ showSearch?: boolean }>(), { showSearch: true });
|
||||
|
||||
const emit = defineEmits<{
|
||||
/**
|
||||
* 点击刷新按钮的事件
|
||||
*/
|
||||
reload: [];
|
||||
/**
|
||||
* 点击节点的事件
|
||||
*/
|
||||
select: [];
|
||||
}>();
|
||||
|
||||
const selectRoomId = defineModel('selectRoomId', {
|
||||
type: Array as PropType<string[]>,
|
||||
default: () => [],
|
||||
});
|
||||
const checkedRoomId = defineModel('checkedRoomId', {
|
||||
type: Array as PropType<string[]>,
|
||||
default: () => [],
|
||||
});
|
||||
const searchValue = defineModel('searchValue', {
|
||||
type: String,
|
||||
default: '',
|
||||
});
|
||||
const checkable = defineModel('checkable', {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
});
|
||||
|
||||
/** 房间数据源 */
|
||||
type RoomTreeArray = CommunityVO[];
|
||||
const roomTreeArray = ref<RoomTreeArray>([]);
|
||||
/** 骨架屏加载 */
|
||||
const showTreeSkeleton = ref<boolean>(true);
|
||||
|
||||
async function loadTree() {
|
||||
showTreeSkeleton.value = true;
|
||||
searchValue.value = '';
|
||||
selectRoomId.value = [];
|
||||
checkedRoomId.value = [];
|
||||
roomTreeArray.value = await communityTree(4);
|
||||
showTreeSkeleton.value = false;
|
||||
}
|
||||
|
||||
async function handleReload() {
|
||||
await loadTree();
|
||||
emit('reload');
|
||||
}
|
||||
|
||||
function selectStyle(id:string){
|
||||
if(selectRoomId.value.includes(id)){
|
||||
return {
|
||||
backgroundColor: '#e6f4ff',
|
||||
padding:'0 10px',
|
||||
borderRadius:'4px',
|
||||
fontWeight:'bold'
|
||||
}
|
||||
}
|
||||
return {}
|
||||
}
|
||||
|
||||
onMounted(loadTree);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="$attrs.class">
|
||||
<Skeleton
|
||||
:loading="showTreeSkeleton"
|
||||
:paragraph="{ rows: 8 }"
|
||||
active
|
||||
class="p-[8px]"
|
||||
>
|
||||
<div
|
||||
class="bg-background flex h-full flex-col overflow-y-auto rounded-lg"
|
||||
>
|
||||
<!-- 固定在顶部 必须加上bg-background背景色 否则会产生'穿透'效果 -->
|
||||
<div
|
||||
v-if="showSearch"
|
||||
class="bg-background z-100 sticky left-0 top-0 p-[8px]"
|
||||
>
|
||||
<InputSearch
|
||||
v-model:value="searchValue"
|
||||
:placeholder="$t('pages.common.search')"
|
||||
size="small"
|
||||
>
|
||||
<template #enterButton>
|
||||
<a-button @click="handleReload">
|
||||
<SyncOutlined class="text-primary" />
|
||||
</a-button>
|
||||
</template>
|
||||
</InputSearch>
|
||||
</div>
|
||||
<div class="h-full overflow-x-hidden px-[8px]">
|
||||
<Tree
|
||||
:checkable="checkable"
|
||||
:selectable="false"
|
||||
v-bind="$attrs"
|
||||
v-if="roomTreeArray.length > 0"
|
||||
v-model:checked-keys="checkedRoomId"
|
||||
:class="$attrs.class"
|
||||
:field-names="{ title: 'label', key: 'id' }"
|
||||
:tree-data="roomTreeArray"
|
||||
:virtual="false"
|
||||
default-expand-all
|
||||
>
|
||||
<template #title="{ label,id }">
|
||||
<span v-if="label.indexOf(searchValue) > -1" :style="selectStyle(id)">
|
||||
{{ label.substring(0, label.indexOf(searchValue)) }}
|
||||
<span style="color: #f50">{{ searchValue }}</span>
|
||||
{{
|
||||
label.substring(
|
||||
label.indexOf(searchValue) + searchValue.length,
|
||||
)
|
||||
}}
|
||||
</span>
|
||||
<span v-else :style="selectStyle(id)">{{ label}}</span>
|
||||
</template>
|
||||
</Tree>
|
||||
<!-- 仅本人数据权限 可以考虑直接不显示 -->
|
||||
<div v-else class="mt-5">
|
||||
<Empty
|
||||
:image="Empty.PRESENTED_IMAGE_SIMPLE"
|
||||
description="无部门数据"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Skeleton>
|
||||
</div>
|
||||
</template>
|
@@ -47,12 +47,12 @@ export const columns: VxeGridProps['columns'] = [
|
||||
return row.id
|
||||
}
|
||||
},
|
||||
width: 100
|
||||
width: 180
|
||||
},
|
||||
{
|
||||
title: '单位名称',
|
||||
field: 'name',
|
||||
width: 100
|
||||
minWidth: 180
|
||||
},
|
||||
{
|
||||
title: '单位类型',
|
||||
@@ -72,7 +72,7 @@ export const columns: VxeGridProps['columns'] = [
|
||||
{
|
||||
title: '联系电话',
|
||||
field: 'phone',
|
||||
width: 100
|
||||
width: 120
|
||||
},
|
||||
// {
|
||||
// title: '入驻位置',
|
||||
@@ -82,7 +82,7 @@ export const columns: VxeGridProps['columns'] = [
|
||||
{
|
||||
title: '入驻时间',
|
||||
field: 'time',
|
||||
width: 100,
|
||||
width: 180,
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
@@ -98,19 +98,22 @@ export const columns: VxeGridProps['columns'] = [
|
||||
{
|
||||
title: '备注',
|
||||
field: 'remark',
|
||||
width: 100,
|
||||
width: 180,
|
||||
slots: { default: ({ row }) => {
|
||||
return row.remark || '-'
|
||||
}, },
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
field: 'createTime',
|
||||
width: 100,
|
||||
width: 180,
|
||||
},
|
||||
{
|
||||
field: 'action',
|
||||
fixed: 'right',
|
||||
slots: { default: 'action' },
|
||||
title: '操作',
|
||||
minWidth: 180,
|
||||
width: 180,
|
||||
},
|
||||
]
|
||||
|
||||
@@ -155,13 +158,13 @@ export const modalSchema: FormSchemaGetter = () => [
|
||||
z.number().int().min(1000000000).max(19999999999, { message: '手机号格式错误' })
|
||||
]).transform(val => val.toString()),
|
||||
},
|
||||
{
|
||||
label: '入驻位置',
|
||||
fieldName: 'locations',
|
||||
component: 'TreeSelect',
|
||||
rules: 'selectRequired',
|
||||
formItemClass: 'col-span-2'
|
||||
},
|
||||
// {
|
||||
// label: '入驻位置',
|
||||
// fieldName: 'locations',
|
||||
// component: 'TreeSelect',
|
||||
// rules: 'selectRequired',
|
||||
// formItemClass: 'col-span-2'
|
||||
// },
|
||||
{
|
||||
label: '入驻时间',
|
||||
fieldName: 'time',
|
||||
@@ -173,11 +176,6 @@ export const modalSchema: FormSchemaGetter = () => [
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
label: '',
|
||||
fieldName: 'Placeholder',
|
||||
component: ''
|
||||
},
|
||||
{
|
||||
label: '授权期限',
|
||||
fieldName: 'authTime',
|
||||
@@ -188,6 +186,7 @@ export const modalSchema: FormSchemaGetter = () => [
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
},
|
||||
rules: 'required',
|
||||
formItemClass: 'col-span-1'
|
||||
},
|
||||
{
|
||||
label: '通行权限组',
|
||||
|
@@ -12,6 +12,7 @@ import relativeTime from 'dayjs/plugin/relativeTime';
|
||||
|
||||
import {resident_unitInfo, authGroupList} from '#/api/property/resident/unit';
|
||||
import {renderDict} from "#/utils/render";
|
||||
import RoomTree from "./components/room-tree.vue";
|
||||
|
||||
dayjs.extend(duration);
|
||||
dayjs.extend(relativeTime);
|
||||
@@ -39,15 +40,27 @@ async function handleOpenChange(open: boolean) {
|
||||
authGroupName.value = authGroup.find(item => item.id === authGroupId)?.name;
|
||||
// 赋值
|
||||
unitDetail.value = response;
|
||||
if(response.location){
|
||||
selectRoomId.value=response.location.split(',');
|
||||
}
|
||||
|
||||
modalApi.modalLoading(false);
|
||||
}
|
||||
|
||||
const selectRoomId = ref<string[]>([]);
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BasicModal :footer="false" :fullscreen-button="false" title="入驻单位信息" class="w-[70%]">
|
||||
<Descriptions v-if="unitDetail" size="small" :column="2" bordered :labelStyle="{width:'120px'}">
|
||||
<div class="flex gap-[8px]">
|
||||
<div class="w-[260px]">
|
||||
<RoomTree
|
||||
class="max-h-[calc(100vh-40vh)]"
|
||||
v-model:select-room-id="selectRoomId"
|
||||
/>
|
||||
</div>
|
||||
<Descriptions class="flex-1" v-if="unitDetail" size="middle" :column="1" bordered :labelStyle="{width:'120px'}">
|
||||
<DescriptionsItem label="单位编号">
|
||||
{{ unitDetail.id }}
|
||||
</DescriptionsItem>
|
||||
@@ -62,11 +75,11 @@ async function handleOpenChange(open: boolean) {
|
||||
<DescriptionsItem label="联系人">
|
||||
{{ unitDetail.contactPerson +'-'+unitDetail.phone}}
|
||||
</DescriptionsItem>
|
||||
<DescriptionsItem label="入驻位置" :span="2">
|
||||
{{ unitDetail.locationDetail }}
|
||||
</DescriptionsItem>
|
||||
<DescriptionsItem label="入驻面积(㎡)" :span="2">
|
||||
{{ unitDetail.area }}
|
||||
<!-- <DescriptionsItem label="入驻位置" >-->
|
||||
<!-- {{ unitDetail.locationDetail }}-->
|
||||
<!-- </DescriptionsItem>-->
|
||||
<DescriptionsItem label="入驻面积">
|
||||
<span v-if="unitDetail.area">{{ unitDetail.area }}(㎡)</span>
|
||||
</DescriptionsItem>
|
||||
<DescriptionsItem label="入驻时间">
|
||||
{{ unitDetail.time }}
|
||||
@@ -89,5 +102,6 @@ async function handleOpenChange(open: boolean) {
|
||||
{{ unitDetail.remark ?? '-' }}
|
||||
</DescriptionsItem>
|
||||
</Descriptions>
|
||||
</div>
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
@@ -1,20 +1,21 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue';
|
||||
import {computed, ref} from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { $t } from '@vben/locales';
|
||||
import { cloneDeep, handleNode } from '@vben/utils';
|
||||
import {useVbenModal} from '@vben/common-ui';
|
||||
import {$t} from '@vben/locales';
|
||||
import {cloneDeep} from '@vben/utils';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import {useVbenForm} from '#/adapter/form';
|
||||
import {
|
||||
resident_unitAdd,
|
||||
resident_unitInfo,
|
||||
resident_unitUpdate,
|
||||
} from '#/api/property/resident/unit';
|
||||
import { defaultFormValueGetter, useBeforeCloseDiff } from '#/utils/popup';
|
||||
import {defaultFormValueGetter, useBeforeCloseDiff} from '#/utils/popup';
|
||||
|
||||
import { modalSchema } from './data';
|
||||
import { communityTree } from '#/api/property/community';
|
||||
import {modalSchema} from './data';
|
||||
import RoomTree from "./components/room-tree.vue";
|
||||
import {message} from "ant-design-vue";
|
||||
|
||||
const emit = defineEmits<{ reload: [] }>();
|
||||
|
||||
@@ -26,7 +27,7 @@ const title = computed(() => {
|
||||
const [BasicForm, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
// 默认占满两列
|
||||
formItemClass: 'col-span-1',
|
||||
formItemClass: 'col-span-2',
|
||||
// 默认label宽度 px
|
||||
labelWidth: 100,
|
||||
// 通用配置项 会影响到所有表单项
|
||||
@@ -39,7 +40,7 @@ const [BasicForm, formApi] = useVbenForm({
|
||||
wrapperClass: 'grid-cols-2',
|
||||
});
|
||||
|
||||
const { onBeforeClose, markInitialized, resetInitialized } = useBeforeCloseDiff(
|
||||
const {onBeforeClose, markInitialized, resetInitialized} = useBeforeCloseDiff(
|
||||
{
|
||||
initializedGetter: defaultFormValueGetter(formApi),
|
||||
currentGetter: defaultFormValueGetter(formApi),
|
||||
@@ -59,16 +60,16 @@ const [BasicModal, modalApi] = useVbenModal({
|
||||
}
|
||||
modalApi.modalLoading(true);
|
||||
|
||||
const { id } = modalApi.getData() as { id?: number | string };
|
||||
const {id} = modalApi.getData() as { id?: number | string };
|
||||
isUpdate.value = !!id;
|
||||
await initLocationOptions();
|
||||
if (isUpdate.value && id) {
|
||||
const record = await resident_unitInfo(id);
|
||||
let roomIds=record.location.split(',')
|
||||
await formApi.setValues({...record,
|
||||
authTime:[record.authBegDate,record.authEndDate],
|
||||
locations:roomIds
|
||||
let roomIds = record.location.split(',')
|
||||
await formApi.setValues({
|
||||
...record,
|
||||
authTime: [record.authBegDate, record.authEndDate],
|
||||
});
|
||||
checkedRoomId.value = roomIds
|
||||
}
|
||||
await markInitialized();
|
||||
|
||||
@@ -79,7 +80,11 @@ const [BasicModal, modalApi] = useVbenModal({
|
||||
async function handleConfirm() {
|
||||
try {
|
||||
modalApi.lock(true);
|
||||
const { valid } = await formApi.validate();
|
||||
const {valid} = await formApi.validate();
|
||||
if (!checkedRoomId.value.length) {
|
||||
message.error('请选择入驻位置');
|
||||
return;
|
||||
}
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
@@ -88,7 +93,7 @@ async function handleConfirm() {
|
||||
|
||||
data.authBegDate = data.authTime[0];
|
||||
data.authEndDate = data.authTime[1];
|
||||
data.location=data.locations.join(',')
|
||||
data.location = checkedRoomId.value.join(',')
|
||||
await (isUpdate.value ? resident_unitUpdate(data) : resident_unitAdd(data));
|
||||
resetInitialized();
|
||||
emit('reload');
|
||||
@@ -99,51 +104,26 @@ async function handleConfirm() {
|
||||
modalApi.lock(false);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 入驻位置数据
|
||||
*/
|
||||
async function initLocationOptions() {
|
||||
const locationList = await communityTree(4);
|
||||
const splitStr = '/';
|
||||
handleNode(locationList, 'label', splitStr, function (node: any) {
|
||||
if (node.level != 4) {
|
||||
node.disabled = true;
|
||||
}
|
||||
});
|
||||
formApi.updateSchema([
|
||||
{
|
||||
componentProps: () => ({
|
||||
class: 'w-full',
|
||||
fieldNames: {
|
||||
key: 'id',
|
||||
label: 'label',
|
||||
value: 'code',
|
||||
children: 'children',
|
||||
},
|
||||
placeholder: '请选择入驻位置',
|
||||
showSearch: true,
|
||||
treeData: locationList,
|
||||
treeDefaultExpandAll: true,
|
||||
treeLine: { showLeafIcon: false },
|
||||
// 筛选的字段
|
||||
treeNodeFilterProp: 'label',
|
||||
// 选中后显示在输入框的值
|
||||
treeNodeLabelProp: 'fullName',
|
||||
multiple:true
|
||||
}),
|
||||
fieldName: 'locations',
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
async function handleClosed() {
|
||||
await formApi.resetForm();
|
||||
resetInitialized();
|
||||
}
|
||||
|
||||
const checkedRoomId = ref<string[]>([]);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BasicModal :title="title">
|
||||
<BasicForm> </BasicForm>
|
||||
<div class="flex gap-[8px]">
|
||||
<div class="w-[260px]">
|
||||
<RoomTree
|
||||
:checkable="true"
|
||||
class="max-h-[calc(100vh-45vh)]"
|
||||
v-model:checked-room-id="checkedRoomId"
|
||||
/>
|
||||
</div>
|
||||
<BasicForm class="flex-1"></BasicForm>
|
||||
</div>
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
Reference in New Issue
Block a user