1、房屋收费
Some checks are pending
Gitea Actions Demo / Explore-Gitea-Actions (push) Waiting to run

2、退费审核
3、房间添加套内面积
This commit is contained in:
2025-07-18 18:40:13 +08:00
parent 8c606397df
commit a5f93c3a6c
21 changed files with 1801 additions and 96 deletions

View File

@@ -0,0 +1,61 @@
import type { HouseChargeVO, HouseChargeForm, HouseChargeQuery } from './model';
import type { ID, IDS } from '#/api/common';
import type { PageResult } from '#/api/common';
import { commonExport } from '#/api/helper';
import { requestClient } from '#/api/request';
/**
* 查询房屋收费列表
* @param params
* @returns 房屋收费列表
*/
export function houseChargeList(params?: HouseChargeQuery) {
return requestClient.get<PageResult<HouseChargeVO>>('/property/houseCharge/list', { params });
}
/**
* 导出房屋收费列表
* @param params
* @returns 房屋收费列表
*/
export function houseChargeExport(params?: HouseChargeQuery) {
return commonExport('/property/houseCharge/export', params ?? {});
}
/**
* 查询房屋收费详情
* @param id id
* @returns 房屋收费详情
*/
export function houseChargeInfo(id: ID) {
return requestClient.get<HouseChargeVO>(`/property/houseCharge/${id}`);
}
/**
* 新增房屋收费
* @param data
* @returns void
*/
export function houseChargeAdd(data: HouseChargeForm) {
return requestClient.postWithMsg<void>('/property/houseCharge', data);
}
/**
* 更新房屋收费
* @param data
* @returns void
*/
export function houseChargeUpdate(data: HouseChargeForm) {
return requestClient.putWithMsg<void>('/property/houseCharge', data);
}
/**
* 删除房屋收费
* @param id id
* @returns void
*/
export function houseChargeRemove(id: ID | IDS) {
return requestClient.deleteWithMsg<void>(`/property/houseCharge/${id}`);
}

View File

@@ -0,0 +1,130 @@
import type {PageQuery, BaseEntity} from '#/api/common';
export interface HouseChargeVO {
/**
* 主键
*/
id: string | number;
/**
* 房屋
*/
roomId: string | number;
/**
* 收费项目
*/
costItemsId: string | number;
/**
* 应收金额
*/
amountReceivable: number;
/**
* 计费开始时间
*/
startTime: string;
/**
* 计费结束时间
*/
endTime: string;
/**
* 状态
*/
state: string;
/**
* 说明
*/
remark: string;
chargeTime: any[];
/**
* 缴费周期
*/
chargeCycle: number;
}
export interface HouseChargeForm extends BaseEntity {
/**
* 主键
*/
id?: string | number;
/**
* 房屋
*/
roomId?: string | number;
/**
* 收费项目
*/
costItemsId?: string | number;
/**
* 应收金额
*/
amountReceivable?: number;
/**
* 计费开始时间
*/
startTime?: string;
/**
* 计费结束时间
*/
endTime?: string;
/**
* 状态
*/
state?: string;
/**
* 说明
*/
remark?: string;
}
export interface HouseChargeQuery extends PageQuery {
/**
* 房屋
*/
roomId?: string | number;
/**
* 收费项目
*/
costItemsId?: string | number;
/**
* 应收金额
*/
amountReceivable?: number;
/**
* 计费开始时间
*/
startTime?: string;
/**
* 计费结束时间
*/
endTime?: string;
/**
* 状态
*/
state?: string;
/**
* 日期范围参数
*/
params?: any;
}

View File

@@ -0,0 +1,61 @@
import type { ReturnPayFeeVO, ReturnPayFeeForm, ReturnPayFeeQuery } from './model';
import type { ID, IDS } from '#/api/common';
import type { PageResult } from '#/api/common';
import { commonExport } from '#/api/helper';
import { requestClient } from '#/api/request';
/**
* 查询退费审核列表
* @param params
* @returns 退费审核列表
*/
export function returnPayFeeList(params?: ReturnPayFeeQuery) {
return requestClient.get<PageResult<ReturnPayFeeVO>>('/property/returnPayFee/list', { params });
}
/**
* 导出退费审核列表
* @param params
* @returns 退费审核列表
*/
export function returnPayFeeExport(params?: ReturnPayFeeQuery) {
return commonExport('/property/returnPayFee/export', params ?? {});
}
/**
* 查询退费审核详情
* @param id id
* @returns 退费审核详情
*/
export function returnPayFeeInfo(id: ID) {
return requestClient.get<ReturnPayFeeVO>(`/property/returnPayFee/${id}`);
}
/**
* 新增退费审核
* @param data
* @returns void
*/
export function returnPayFeeAdd(data: ReturnPayFeeForm) {
return requestClient.postWithMsg<void>('/property/returnPayFee', data);
}
/**
* 更新退费审核
* @param data
* @returns void
*/
export function returnPayFeeUpdate(data: ReturnPayFeeForm) {
return requestClient.putWithMsg<void>('/property/returnPayFee', data);
}
/**
* 删除退费审核
* @param id id
* @returns void
*/
export function returnPayFeeRemove(id: ID | IDS) {
return requestClient.deleteWithMsg<void>(`/property/returnPayFee/${id}`);
}

View File

@@ -0,0 +1,154 @@
import type { PageQuery, BaseEntity } from '#/api/common';
export interface ReturnPayFeeVO {
/**
* 主键
*/
id: string | number;
/**
* 退款单号
*/
returnNo?: string;
/**
* 支付单号
*/
payNo?: string;
/**
* 退款原因
*/
reason?: string;
/**
* 费用类型id
*/
itemId?: string | number;
/**
* 业主id
*/
userId?: string | number;
/**
* 支付金额
*/
payAcount?: number;
/**
* 支付时间
*/
payTime?: string;
/**
* 退费状态
*/
state: string;
/**
* 说明
*/
remark: string;
}
export interface ReturnPayFeeForm extends BaseEntity {
/**
* 主键
*/
id?: string | number;
/**
* 退款单号
*/
returnNo?: string;
/**
* 支付单号
*/
payNo?: string;
/**
* 退款原因
*/
reason?: string;
/**
* 费用类型id
*/
itemId?: string | number;
/**
* 业主id
*/
userId?: string | number;
/**
* 支付金额
*/
payAcount?: number;
/**
* 支付时间
*/
payTime?: string;
/**
* 退费状态
*/
state?: string;
/**
* 说明
*/
remark?: string;
}
export interface ReturnPayFeeQuery extends PageQuery {
/**
* 退款单号
*/
returnNo?: string;
/**
* 支付单号
*/
payNo?: string;
/**
* 退款原因
*/
reason?: string;
/**
* 费用类型id
*/
itemId?: string | number;
/**
* 业主id
*/
userId?: string | number;
/**
* 支付金额
*/
payAcount?: number;
/**
* 支付时间
*/
payTime?: string;
/**
* 退费状态
*/
state?: string;
/**
* 日期范围参数
*/
params?: any;
}

View File

@@ -56,6 +56,11 @@ export interface MaintainPlanVO {
*/
state: string;
planDate: any[]
userId: string[];
machineMaintainPlanStaffBoList:any[];
}
export interface MaintainPlanForm extends BaseEntity {

View File

@@ -22,10 +22,15 @@ export interface RoomVO {
roomType: number;
/**
* 面积(平方米)
* 建筑面积(平方米)
*/
area: number;
/**
* 套内面积(平方米)
*/
insideInArea: number;
/**
* 户型(如2室1厅1卫)
*/

View File

@@ -0,0 +1,181 @@
import type {FormSchemaGetter} from '#/adapter/form';
import type {VxeGridProps} from '#/adapter/vxe-table';
import {getDictOptions} from "#/utils/dict";
import {renderDict} from "#/utils/render";
import {costItemSettingList} from "#/api/property/costManagement/costItemSetting";
import { h } from 'vue';
export const querySchema: FormSchemaGetter = () => [
{
component: 'ApiSelect',
componentProps: {
api:async ()=>{
return (await costItemSettingList({pageSize: 1000, pageNum: 1}))?.rows
},
afterFetch: (data: { chargeItem: string; id: string }[]) => {
return data.map((item: any) => ({
label: item.chargeItem,
value: item.id,
}));
},
},
fieldName: 'costItemsId',
label: '收费项目',
},
{
component: 'Select',
fieldName: 'state',
label: '状态',
componentProps: {
options: getDictOptions('wy_fysfzt')
},
},
];
export const columns: VxeGridProps['columns'] = [
{type: 'checkbox', width: 60},
{
title: '房屋',
field: 'roomId',
minWidth: 150,
},
{
title: '收费项目',
field: 'costItemsId',
width: 150,
},
{
title: '应收金额',
field: 'amountReceivable',
width: 150,
},
{
title: '计费开始时间',
field: 'startTime',
width: 150,
},
{
title: '计费结束时间',
field: 'endTime',
width: 150,
},
{
title: '状态',
field: 'state',
width: 150,
slots: {
default: ({row}) => {
return renderDict(row.state, 'wy_fysfzt')
}
}
},
{
title: '说明',
field: 'remark',
width: 150,
},
{
field: 'action',
fixed: 'right',
slots: {default: 'action'},
title: '操作',
width: 180,
},
];
export const modalSchema: FormSchemaGetter = () => [
{
label: '主键',
fieldName: 'id',
component: 'Input',
dependencies: {
show: () => false,
triggerFields: [''],
},
},
{
label: '房屋',
fieldName: 'roomId',
component: 'TreeSelect',
componentProps: {
},
rules: 'selectRequired',
},
{
label: '费用类型',
fieldName: 'costType',
component: 'Select',
componentProps: {
},
rules: 'selectRequired',
},
{
label: '收费项目',
fieldName: 'costItemsId',
component: 'ApiSelect',
componentProps: {},
rules: 'selectRequired',
},
{
label: '应收金额',
fieldName: 'amountReceivable',
component: 'Input',
dependencies: {
show: () => false,
triggerFields: [''],
},
},
{
label: '计费时间',
fieldName: 'chargeTime',
component: 'RangePicker',
componentProps: {
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
},
rules: 'selectRequired',
},
{
label: '说明',
fieldName: 'remark',
component: 'Textarea',
formItemClass: 'col-span-2',
dependencies: {
show: () => false,
triggerFields: [''],
},
},
];
export const modalSchemaUpdate: FormSchemaGetter = () => [
{
label: '主键',
fieldName: 'id',
component: 'Input',
dependencies: {
show: () => false,
triggerFields: [''],
},
},
{
label: '支付方式',
fieldName: 'payType',
component: 'Select',
componentProps: {
options:getDictOptions('wy_zffs')
},
rules: 'selectRequired',
},
{
label: '缴费周期',
fieldName: 'chargeCycle',
component: 'InputNumber',
componentProps: {
min:1,
precision:0,
placeholder:'请输入缴费周期(月)'
},
suffix: () => h('span', { style: {fontSize: '0.875rem',fontWeight:500,} }, '月'),
rules: 'selectRequired',
},
]

View File

@@ -0,0 +1,91 @@
<script setup lang="ts">
import {ref, shallowRef} from 'vue';
import {useVbenModal} from '@vben/common-ui';
import {Descriptions, DescriptionsItem} from 'ant-design-vue';
import dayjs from 'dayjs';
import duration from 'dayjs/plugin/duration';
import relativeTime from 'dayjs/plugin/relativeTime';
import {renderDict} from "#/utils/render";
dayjs.extend(duration);
dayjs.extend(relativeTime);
import {houseChargeInfo} from "#/api/property/costManagement/houseCharge";
import type {HouseChargeVO} from "#/api/property/costManagement/houseCharge/model";
import type {RoomVO} from "#/api/property/room/model";
import type {CostItemSettingVO} from "#/api/property/costManagement/costItemSetting/model";
const [BasicModal, modalApi] = useVbenModal({
onOpenChange: handleOpenChange,
onClosed() {
houseChargeDetail.value = null;
},
});
const houseChargeDetail = shallowRef<null | HouseChargeVO>(null);
const room=ref<RoomVO>();
const costItem=ref<CostItemSettingVO>();
async function handleOpenChange(open: boolean) {
if (!open) {
return null;
}
modalApi.modalLoading(true);
const {id} = modalApi.getData() as { id: number | string };
houseChargeDetail.value = await houseChargeInfo(id);
if(houseChargeDetail.value){
room.value=houseChargeDetail.value.roomVo
costItem.value=houseChargeDetail.value.costItemsVo
}
modalApi.modalLoading(false);
}
</script>
<template>
<BasicModal :footer="false" :fullscreen-button="false" title="房屋收费详情" class="w-[70%]">
<Descriptions v-if="houseChargeDetail" size="small" :column="2" bordered
:labelStyle="{width:'100px'}">
<DescriptionsItem label="费用编号">
{{ costItem.id }}
</DescriptionsItem>
<DescriptionsItem label="费用项目">
{{ costItem.chargeItem }}
</DescriptionsItem>
<DescriptionsItem label="费用类型">
<component
:is="renderDict(costItem.costType,'pro_expense_type')"
/>
</DescriptionsItem>
<DescriptionsItem label="计费时间">
{{ houseChargeDetail.startTime+' 至 '+houseChargeDetail.endTime }}
</DescriptionsItem>
<DescriptionsItem label="房间">
{{room.roomNumber}}
</DescriptionsItem>
<DescriptionsItem label="房间面积">
{{`建筑面积:${room.area} 套内面积:${room.insideInArea}`}}
</DescriptionsItem>
<DescriptionsItem label="单价">
{{ costItem.unitPrice }}
</DescriptionsItem>
<DescriptionsItem label="附加费">
{{costItem.surcharge}}
</DescriptionsItem>
<DescriptionsItem label="应收金额">
<span style="font-size: 16px;font-weight: 600;color: red" v-if="houseChargeDetail.amountReceivable">
{{houseChargeDetail.amountReceivable}}
</span>
</DescriptionsItem>
<DescriptionsItem label="支付方式" v-if="houseChargeDetail.payType">
<component
:is="renderDict(houseChargeDetail.payType,'wy_zffs')"
/>
</DescriptionsItem>
<DescriptionsItem label="缴费周期" v-if="houseChargeDetail.chargeCycle">
{{houseChargeDetail.chargeCycle}}
</DescriptionsItem>
<DescriptionsItem label="说明" :span="2">
{{houseChargeDetail.remark}}
</DescriptionsItem>
</Descriptions>
</BasicModal>
</template>

View File

@@ -0,0 +1,201 @@
<script setup lang="ts">
import {computed, ref} from 'vue';
import {useVbenModal} from '@vben/common-ui';
import {$t} from '@vben/locales';
import {cloneDeep, handleNode} from '@vben/utils';
import {useVbenForm} from '#/adapter/form';
import {
houseChargeAdd,
houseChargeInfo,
houseChargeUpdate
} from '#/api/property/costManagement/houseCharge';
import {defaultFormValueGetter, useBeforeCloseDiff} from '#/utils/popup';
import {modalSchema} from './data';
import {communityTree} from "#/api/property/community";
import {costItemSettingList} from "#/api/property/costManagement/costItemSetting";
import type {CostItemSettingVO} from "#/api/property/costManagement/costItemSetting/model";
import {getDictOptions} from "#/utils/dict";
const emit = defineEmits<{ reload: [] }>();
const isUpdate = ref(false);
const costItemOptions = ref<CostItemSettingVO[]>([]);
const title = computed(() => {
return isUpdate.value ? $t('pages.common.edit') : $t('pages.common.add');
});
const [BasicForm, formApi] = useVbenForm({
commonConfig: {
// 默认占满两列
formItemClass: 'col-span-2',
// 默认label宽度 px
labelWidth: 80,
// 通用配置项 会影响到所有表单项
componentProps: {
class: 'w-full',
}
},
schema: modalSchema(),
showDefaultActions: false,
wrapperClass: 'grid-cols-2',
});
const {onBeforeClose, markInitialized, resetInitialized} = useBeforeCloseDiff(
{
initializedGetter: defaultFormValueGetter(formApi),
currentGetter: defaultFormValueGetter(formApi),
},
);
const [BasicModal, modalApi] = useVbenModal({
// 在这里更改宽度
class: 'w-[550px]',
fullscreenButton: false,
onBeforeClose,
onClosed: handleClosed,
onConfirm: handleConfirm,
onOpenChange: async (isOpen) => {
if (!isOpen) {
return null;
}
modalApi.modalLoading(true);
await initRoomOptions()
await queryCostItemOptions()
await initCostTypeOptions()
const {id} = modalApi.getData() as { id?: number | string };
isUpdate.value = !!id;
if (isUpdate.value && id) {
const record = await houseChargeInfo(id);
record.chargeTime = [record.startTime, record.endTime]
await formApi.setValues(record);
}
await markInitialized();
modalApi.modalLoading(false);
},
});
async function handleConfirm() {
try {
modalApi.lock(true);
const {valid} = await formApi.validate();
if (!valid) {
return;
}
// getValues获取为一个readonly的对象 需要修改必须先深拷贝一次
const data = cloneDeep(await formApi.getValues());
if (data.chargeTime && data.chargeTime.length) {
data.startTime = data.chargeTime[0]
data.endTime = data.chargeTime[1]
}
await (isUpdate.value ? houseChargeUpdate(data) : houseChargeAdd(data));
resetInitialized();
emit('reload');
modalApi.close();
} catch (error) {
console.error(error);
} finally {
modalApi.lock(false);
}
}
async function handleClosed() {
await formApi.resetForm();
resetInitialized();
}
/**
* 房间数据
*/
async function initRoomOptions() {
const roomList = await communityTree(5);
const splitStr = '/';
handleNode(roomList, 'label', splitStr, function (node: any) {
if (node.level != 5) {
node.disabled = true;
}
});
formApi.updateSchema([
{
componentProps: () => ({
class: 'w-full',
fieldNames: {
key: 'id',
label: 'label',
value: 'code',
children: 'children',
},
placeholder: '请选择房间',
showSearch: true,
treeData: roomList,
treeDefaultExpandAll: true,
treeLine: {showLeafIcon: false},
// 筛选的字段
treeNodeFilterProp: 'label',
// 选中后显示在输入框的值
treeNodeLabelProp: 'fullName',
}),
fieldName: 'roomId',
},
]);
}
/**
* 查询费用项设置
*/
async function queryCostItemOptions(costType:string) {
let params = {
pageSize: 1000,
pageNum: 1,
costType
}
const res = await costItemSettingList(params)
costItemOptions.value = res.rows
formApi.updateSchema([{
componentProps: {
options: costItemOptions.value,
fieldNames: {label: 'chargeItem', value: 'id'},
showSearch: true,
optionFilterProp: 'chargeItem',
onChange: async (value: string) => {
if (value) {
const costItem = costItemOptions.value.find(item => item.id == value)
if(costItem){
await formApi.setFieldValue('remark', `单价:${costItem.unitPrice} 附加费:${costItem.surcharge}`);
}
}
}
},
fieldName: 'costItemsId'
}])
}
/**
* 初始化费用类型
*/
async function initCostTypeOptions(){
formApi.updateSchema([{
componentProps: {
options: getDictOptions('pro_expense_type'),
onChange: async (value: string) => {
if (value) {
await queryCostItemOptions(value)
await formApi.setFieldValue('costItemsId', null)
}
}
},
fieldName: 'costType'
}])
}
</script>
<template>
<BasicModal :title="title">
<BasicForm/>
</BasicModal>
</template>

View File

@@ -0,0 +1,149 @@
<script setup lang="ts">
import {ref} from 'vue';
import {useVbenModal} from '@vben/common-ui';
import {cloneDeep} from '@vben/utils';
import {useVbenForm} from '#/adapter/form';
import {
houseChargeAdd,
houseChargeInfo,
houseChargeUpdate
} from '#/api/property/costManagement/houseCharge';
import {defaultFormValueGetter, useBeforeCloseDiff} from '#/utils/popup';
import {modalSchemaUpdate} from './data';
import {renderDict} from "#/utils/render";
import {Descriptions, DescriptionsItem, Divider} from "ant-design-vue";
import type {HouseChargeVO} from "#/api/property/costManagement/houseCharge/model";
import type {RoomVO} from "#/api/property/room/model";
import type {CostItemSettingVO} from "#/api/property/costManagement/costItemSetting/model";
const emit = defineEmits<{ reload: [] }>();
const isUpdate = ref(false);
const record = ref<HouseChargeVO>();
const room = ref<RoomVO>();
const costItem = ref<CostItemSettingVO>();
const [BasicForm, formApi] = useVbenForm({
commonConfig: {
// 默认占满两列
formItemClass: 'col-span-2',
// 默认label宽度 px
labelWidth: 80,
// 通用配置项 会影响到所有表单项
componentProps: {
class: 'w-full',
}
},
schema: modalSchemaUpdate(),
showDefaultActions: false,
wrapperClass: 'grid-cols-2',
});
const {onBeforeClose, markInitialized, resetInitialized} = useBeforeCloseDiff(
{
initializedGetter: defaultFormValueGetter(formApi),
currentGetter: defaultFormValueGetter(formApi),
},
);
const [BasicModal, modalApi] = useVbenModal({
// 在这里更改宽度
class: 'w-[550px]',
fullscreenButton: false,
onBeforeClose,
onClosed: handleClosed,
onConfirm: handleConfirm,
onOpenChange: async (isOpen) => {
if (!isOpen) {
return null;
}
modalApi.modalLoading(true);
const {id} = modalApi.getData() as { id?: number | string };
isUpdate.value = !!id;
if (isUpdate.value && id) {
record.value = await houseChargeInfo(id);
if (record.value) {
room.value = record.value.roomVo
costItem.value = record.value.costItemsVo
}
await formApi.setValues(record.value);
}
await markInitialized();
modalApi.modalLoading(false);
},
});
async function handleConfirm() {
try {
modalApi.lock(true);
const {valid} = await formApi.validate();
if (!valid) {
return;
}
// getValues获取为一个readonly的对象 需要修改必须先深拷贝一次
const data = cloneDeep(await formApi.getValues());
record.value.payType = data.payType
record.value.chargeCycle = data.chargeCycle
await houseChargeUpdate(record.value) ;
resetInitialized();
emit('reload');
modalApi.close();
} catch (error) {
console.error(error);
} finally {
modalApi.lock(false);
}
}
async function handleClosed() {
await formApi.resetForm();
resetInitialized();
}
</script>
<template>
<BasicModal title="缴费">
<Descriptions v-if="record" size="small" :column="2"
:labelStyle="{width:'80px'}">
<DescriptionsItem label="费用编号">
{{ costItem.id }}
</DescriptionsItem>
<DescriptionsItem label="费用项目">
{{ costItem.chargeItem }}
</DescriptionsItem>
<DescriptionsItem label="费用类型">
<component
:is="renderDict(costItem.costType,'pro_expense_type')"
/>
</DescriptionsItem>
<DescriptionsItem label="计费起始">
{{ record.startTime }}
</DescriptionsItem>
<DescriptionsItem label="房间">
{{ room.roomNumber }}
</DescriptionsItem>
<DescriptionsItem label="面积(㎡)">
{{ `${room.area} (套内面积:${room.insideInArea}` }}
</DescriptionsItem>
<DescriptionsItem label="单价">
{{ costItem.unitPrice }}
</DescriptionsItem>
<DescriptionsItem label="附加费">
{{ costItem.surcharge }}
</DescriptionsItem>
<DescriptionsItem label="应收金额" :span="2">
<span style="font-size: 16px;font-weight: 600;color: red" v-if="record.amountReceivable">
{{ record.amountReceivable }}
</span>
</DescriptionsItem>
</Descriptions>
<Divider/>
<BasicForm/>
</BasicModal>
</template>

View File

@@ -0,0 +1,189 @@
<script setup lang="ts">
import { Page, useVbenModal, type VbenFormProps } from '@vben/common-ui';
import { getVxePopupContainer } from '@vben/utils';
import { Modal, Popconfirm, Space } from 'ant-design-vue';
import {
useVbenVxeGrid,
vxeCheckboxChecked,
type VxeGridProps
} from '#/adapter/vxe-table';
import {
houseChargeExport,
houseChargeList,
houseChargeRemove,
} from '#/api/property/costManagement/houseCharge';
import type { HouseChargeForm } from '#/api/property/costManagement/houseCharge/model';
import { commonDownloadExcel } from '#/utils/file/download';
import houseChargeAdd from './houseCharge-add.vue';
import houseChargeUpdate from './houseCharge-update.vue';
import houseChargeDetail from './house-charge-detail.vue';
import { columns, querySchema } from './data';
const formOptions: VbenFormProps = {
commonConfig: {
labelWidth: 80,
componentProps: {
allowClear: true,
},
},
schema: querySchema(),
wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4',
};
const gridOptions: VxeGridProps = {
checkboxConfig: {
// 高亮
highlight: true,
// 翻页时保留选中状态
reserve: true,
// 点击行选中
// trigger: 'row',
},
// 需要使用i18n注意这里要改成getter形式 否则切换语言不会刷新
// columns: columns(),
columns,
height: 'auto',
keepSource: true,
pagerConfig: {},
proxyConfig: {
ajax: {
query: async ({ page }, formValues = {}) => {
return await houseChargeList({
pageNum: page.currentPage,
pageSize: page.pageSize,
...formValues,
});
},
},
},
rowConfig: {
keyField: 'id',
},
// 表格全局唯一表示 保存列配置需要用到
id: 'property-houseCharge-index'
};
const [BasicTable, tableApi] = useVbenVxeGrid({
formOptions,
gridOptions,
});
const [HouseChargeAdd, modalApi] = useVbenModal({
connectedComponent: houseChargeAdd,
});
const [HouseChargeUpdate, updateApi] = useVbenModal({
connectedComponent: houseChargeUpdate,
});
const [HouseChargeDetail, detailApi] = useVbenModal({
connectedComponent: houseChargeDetail,
});
function handleAdd() {
modalApi.setData({});
modalApi.open();
}
async function handleEdit(row: Required<HouseChargeForm>) {
updateApi.setData({ id: row.id });
updateApi.open();
}
async function handleInfo(row: Required<HouseChargeForm>) {
detailApi.setData({ id: row.id });
detailApi.open();
}
async function handleDelete(row: Required<HouseChargeForm>) {
await houseChargeRemove(row.id);
await tableApi.query();
}
function handleMultiDelete() {
const rows = tableApi.grid.getCheckboxRecords();
const ids = rows.map((row: Required<HouseChargeForm>) => row.id);
Modal.confirm({
title: '提示',
okType: 'danger',
content: `确认删除选中的${ids.length}条记录吗?`,
onOk: async () => {
await houseChargeRemove(ids);
await tableApi.query();
},
});
}
function handleDownloadExcel() {
commonDownloadExcel(houseChargeExport, '房屋收费数据', tableApi.formApi.form.values, {
fieldMappingTime: formOptions.fieldMappingTime,
});
}
</script>
<template>
<Page :auto-content-height="true">
<BasicTable table-title="房屋收费列表">
<template #toolbar-tools>
<Space>
<a-button
v-access:code="['property:houseCharge:export']"
@click="handleDownloadExcel"
>
{{ $t('pages.common.export') }}
</a-button>
<a-button
:disabled="!vxeCheckboxChecked(tableApi)"
danger
type="primary"
v-access:code="['property:houseCharge:remove']"
@click="handleMultiDelete">
取消
</a-button>
<a-button
type="primary"
v-access:code="['property:houseCharge:add']"
@click="handleAdd"
>
{{ $t('pages.common.add') }}
</a-button>
</Space>
</template>
<template #action="{ row }">
<Space>
<ghost-button
v-access:code="['property:houseCharge:info']"
@click.stop="handleInfo(row)"
>
{{ $t('pages.common.info') }}
</ghost-button>
<ghost-button
v-access:code="['property:houseCharge:edit']"
@click.stop="handleEdit(row)"
>
缴费
</ghost-button>
<Popconfirm
:get-popup-container="getVxePopupContainer"
placement="left"
title="确认取消?"
@confirm="handleDelete(row)"
>
<ghost-button
danger
v-access:code="['property:houseCharge:remove']"
@click.stop=""
>
取消
</ghost-button>
</Popconfirm>
</Space>
</template>
</BasicTable>
<HouseChargeAdd @reload="tableApi.query()" />
<HouseChargeUpdate @reload="tableApi.query()" />
<HouseChargeDetail/>
</Page>
</template>

View File

@@ -0,0 +1,103 @@
import type { FormSchemaGetter } from '#/adapter/form';
import type { VxeGridProps } from '#/adapter/vxe-table';
import {getDictOptions} from "#/utils/dict";
import {renderDict} from "#/utils/render";
export const querySchema: FormSchemaGetter = () => [
{
component: 'Input',
fieldName: 'returnNo',
label: '退款单号',
},
{
component: 'Select',
componentProps:{
options:getDictOptions('wy_tfshzt')
},
fieldName: 'state',
label: '审核状态',
},
];
export const columns: VxeGridProps['columns'] = [
{ type: 'checkbox', width: 60 },
{
title: '退款单号',
field: 'returnNo',
width: 180,
},
{
title: '支付单号',
field: 'payNo',
width: 180,
},
{
title: '支付金额',
field: 'payAcount',
width: 100,
},
{
title: '支付时间',
field: 'payTime',
width: 180,
},
{
title: '退款原因',
field: 'reason',
minWidth: 180,
},
{
title: '审核状态',
field: 'state',
width: 150,
slots:{
default:({row})=>{
return renderDict(row.state,'wy_tfshzt')
}
}
},
{
title: '审核意见',
field: 'remark',
width: 180,
},
{
field: 'action',
fixed: 'right',
slots: { default: 'action' },
title: '操作',
width: 180,
},
];
export const stateOptions = [
{ label: '审核通过', value: '1' },
{ label: '审核不通过', value: '2' },
];
export const modalSchema: FormSchemaGetter = () => [
{
label: '主键',
fieldName: 'id',
component: 'Input',
dependencies: {
show: () => false,
triggerFields: [''],
},
},
{
label: '审核状态',
fieldName: 'state',
component: 'RadioGroup',
componentProps: {
buttonStyle: 'solid',
options: stateOptions,
},
rules:'required'
},
{
label: '审核意见',
fieldName: 'remark',
component: 'Textarea',
},
];

View File

@@ -0,0 +1,172 @@
<script setup lang="ts">
import {Page, useVbenModal, type VbenFormProps} from '@vben/common-ui';
import {Modal, Popconfirm, Space} from 'ant-design-vue';
import {
useVbenVxeGrid,
vxeCheckboxChecked,
type VxeGridProps
} from '#/adapter/vxe-table';
import {
returnPayFeeExport,
returnPayFeeList,
returnPayFeeRemove,
} from '#/api/property/costManagement/returnPayFee';
import type {ReturnPayFeeForm} from '#/api/property/costManagement/returnPayFee/model';
import {commonDownloadExcel} from '#/utils/file/download';
import returnPayFeeModal from './returnPayFee-modal.vue';
import returnPayFeeDetail from './return-pay-detail.vue';
import {columns, querySchema} from './data';
const formOptions: VbenFormProps = {
commonConfig: {
labelWidth: 80,
componentProps: {
allowClear: true,
},
},
schema: querySchema(),
wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4',
};
const gridOptions: VxeGridProps = {
checkboxConfig: {
// 高亮
highlight: true,
// 翻页时保留选中状态
reserve: true,
// 点击行选中
// trigger: 'row',
},
// 需要使用i18n注意这里要改成getter形式 否则切换语言不会刷新
// columns: columns(),
columns,
height: 'auto',
keepSource: true,
pagerConfig: {},
proxyConfig: {
ajax: {
query: async ({page}, formValues = {}) => {
return await returnPayFeeList({
pageNum: page.currentPage,
pageSize: page.pageSize,
...formValues,
});
},
},
},
rowConfig: {
keyField: 'id',
},
// 表格全局唯一表示 保存列配置需要用到
id: 'property-returnPayFee-index'
};
const [BasicTable, tableApi] = useVbenVxeGrid({
formOptions,
gridOptions,
});
const [ReturnPayFeeModal, modalApi] = useVbenModal({
connectedComponent: returnPayFeeModal,
});
const [ReturnPayFeeDetail, detailApi] = useVbenModal({
connectedComponent: returnPayFeeDetail,
});
function handleAdd() {
modalApi.setData({});
modalApi.open();
}
async function handleEdit(row: Required<ReturnPayFeeForm>) {
modalApi.setData({id: row.id});
modalApi.open();
}
async function handleInfo(row: Required<ReturnPayFeeForm>) {
detailApi.setData({id: row.id});
detailApi.open();
}
async function handleDelete(row: Required<ReturnPayFeeForm>) {
await returnPayFeeRemove(row.id);
await tableApi.query();
}
function handleMultiDelete() {
const rows = tableApi.grid.getCheckboxRecords();
const ids = rows.map((row: Required<ReturnPayFeeForm>) => row.id);
Modal.confirm({
title: '提示',
okType: 'danger',
content: `确认删除选中的${ids.length}条记录吗?`,
onOk: async () => {
await returnPayFeeRemove(ids);
await tableApi.query();
},
});
}
function handleDownloadExcel() {
commonDownloadExcel(returnPayFeeExport, '退费审核数据', tableApi.formApi.form.values, {
fieldMappingTime: formOptions.fieldMappingTime,
});
}
</script>
<template>
<Page :auto-content-height="true">
<BasicTable table-title="退费审核列表">
<template #toolbar-tools>
<Space>
<a-button
v-access:code="['property:returnPayFee:export']"
@click="handleDownloadExcel"
>
{{ $t('pages.common.export') }}
</a-button>
<!-- <a-button-->
<!-- :disabled="!vxeCheckboxChecked(tableApi)"-->
<!-- danger-->
<!-- type="primary"-->
<!-- v-access:code="['property:returnPayFee:remove']"-->
<!-- @click="handleMultiDelete">-->
<!-- {{ $t('pages.common.delete') }}-->
<!-- </a-button>-->
<!-- <a-button-->
<!-- type="primary"-->
<!-- v-access:code="['property:returnPayFee:add']"-->
<!-- @click="handleAdd"-->
<!-- >-->
<!-- {{ $t('pages.common.add') }}-->
<!-- </a-button>-->
</Space>
</template>
<template #action="{ row }">
<Space>
<ghost-button
:disabled="row.state!='0'"
v-access:code="['property:returnPayFee:edit']"
@click.stop="handleEdit(row)"
>
审核
</ghost-button>
<ghost-button
v-access:code="['property:returnPayFee:info']"
@click.stop="handleInfo(row)"
>
{{ $t('pages.common.info') }}
</ghost-button>
</Space>
</template>
</BasicTable>
<ReturnPayFeeModal @reload="tableApi.query()"/>
<ReturnPayFeeDetail/>
</Page>
</template>

View File

@@ -0,0 +1,64 @@
<script setup lang="ts">
import {shallowRef} from 'vue';
import {useVbenModal} from '@vben/common-ui';
import {Descriptions, DescriptionsItem} from 'ant-design-vue';
import dayjs from 'dayjs';
import duration from 'dayjs/plugin/duration';
import relativeTime from 'dayjs/plugin/relativeTime';
import {renderDict} from "#/utils/render";
dayjs.extend(duration);
dayjs.extend(relativeTime);
import {returnPayFeeInfo} from "#/api/property/costManagement/returnPayFee";
import type {ReturnPayFeeVO} from "#/api/property/costManagement/returnPayFee/model";
const [BasicModal, modalApi] = useVbenModal({
onOpenChange: handleOpenChange,
onClosed() {
returnPayDetail.value = null;
},
});
const returnPayDetail = shallowRef<null | ReturnPayFeeVO>(null);
async function handleOpenChange(open: boolean) {
if (!open) {
return null;
}
modalApi.modalLoading(true);
const {id} = modalApi.getData() as { id: number | string };
returnPayDetail.value = await returnPayFeeInfo(id);
modalApi.modalLoading(false);
}
</script>
<template>
<BasicModal :footer="false" :fullscreen-button="false" title="退费审核详情" class="w-[70%]">
<Descriptions v-if="returnPayDetail" size="small" :column="2" bordered
:labelStyle="{width:'100px'}">
<DescriptionsItem label="退款单号">
{{ returnPayDetail.returnNo }}
</DescriptionsItem>
<DescriptionsItem label="支付单号">
{{ returnPayDetail.payNo }}
</DescriptionsItem>
<DescriptionsItem label="支付金额">
{{returnPayDetail.payAcount}}
</DescriptionsItem>
<DescriptionsItem label="支付时间">
{{ returnPayDetail.payTime }}
</DescriptionsItem>
<DescriptionsItem label="退款原因" :span="2">
{{returnPayDetail.reason}}
</DescriptionsItem>
<DescriptionsItem label="审核状态" :span="2">
<component
:is="renderDict(returnPayDetail.state,'wy_tfshzt')"
/>
</DescriptionsItem>
<DescriptionsItem label="审核意见" :span="2">
{{returnPayDetail.remark}}
</DescriptionsItem>
</Descriptions>
</BasicModal>
</template>

View File

@@ -0,0 +1,108 @@
<script setup lang="ts">
import {ref} from 'vue';
import {useVbenModal} from '@vben/common-ui';
import {cloneDeep} from '@vben/utils';
import {useVbenForm} from '#/adapter/form';
import {
returnPayFeeInfo,
returnPayFeeUpdate
} from '#/api/property/costManagement/returnPayFee';
import {defaultFormValueGetter, useBeforeCloseDiff} from '#/utils/popup';
import {modalSchema} from './data';
import type {ReturnPayFeeVO} from "#/api/property/costManagement/returnPayFee/model";
const emit = defineEmits<{ reload: [] }>();
const isUpdate = ref(false);
const record = ref<ReturnPayFeeVO>({
id:'',
state:'1',
remark:''
})
const [BasicForm, formApi] = useVbenForm({
commonConfig: {
// 默认占满两列
formItemClass: 'col-span-2',
// 默认label宽度 px
labelWidth: 80,
// 通用配置项 会影响到所有表单项
componentProps: {
class: 'w-full',
}
},
schema: modalSchema(),
showDefaultActions: false,
wrapperClass: 'grid-cols-2',
});
const {onBeforeClose, markInitialized, resetInitialized} = useBeforeCloseDiff(
{
initializedGetter: defaultFormValueGetter(formApi),
currentGetter: defaultFormValueGetter(formApi),
},
);
const [BasicModal, modalApi] = useVbenModal({
// 在这里更改宽度
class: 'w-[550px]',
fullscreenButton: false,
onBeforeClose,
onClosed: handleClosed,
onConfirm: handleConfirm,
onOpenChange: async (isOpen) => {
if (!isOpen) {
return null;
}
modalApi.modalLoading(true);
const {id} = modalApi.getData() as { id?: number | string };
isUpdate.value = !!id;
if (isUpdate.value && id) {
record.value = await returnPayFeeInfo(id);
record.value.state = '1'
await formApi.setValues(record.value);
}
await markInitialized();
modalApi.modalLoading(false);
},
});
async function handleConfirm() {
try {
modalApi.lock(true);
const {valid} = await formApi.validate();
if (!valid) {
return;
}
// getValues获取为一个readonly的对象 需要修改必须先深拷贝一次
const data = cloneDeep(await formApi.getValues());
record.value.state=data.state
record.value.remark=data.remark
await returnPayFeeUpdate(record.value);
resetInitialized();
emit('reload');
modalApi.close();
} catch (error) {
console.error(error);
} finally {
modalApi.lock(false);
}
}
async function handleClosed() {
await formApi.resetForm();
resetInitialized();
}
</script>
<template>
<BasicModal title="审核">
<BasicForm/>
</BasicModal>
</template>

View File

@@ -42,16 +42,16 @@ export const columns: VxeGridProps['columns'] = [
field: 'machineBrand',
width:100
},
{
title: '设备类型',
field: 'machineTypeId',
width:100
},
{
title: '位置详情',
field: 'locationId',
width:100
},
// {
// title: '设备类型',
// field: 'machineTypeId',
// width:100
// },
// {
// title: '位置详情',
// field: 'locationId',
// width:100
// },
{
title: '采购价格',
field: 'purchasePrice',
@@ -92,11 +92,11 @@ export const columns: VxeGridProps['columns'] = [
}
}
},
{
title: '责任人',
field: 'personId',
width:100
},
// {
// title: '责任人',
// field: 'personId',
// width:100
// },
{
field: 'action',
fixed: 'right',

View File

@@ -24,7 +24,7 @@ import MachineTypeTree from '../components/machine-type-tree.vue'
import machineDetail from './machine-detail.vue'
import {ref} from "vue";
const selectTypeId = ref('')
const selectTypeId = ref([])
const formOptions: VbenFormProps = {
commonConfig: {
@@ -36,7 +36,7 @@ const formOptions: VbenFormProps = {
schema: querySchema(),
wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4',
handleReset: async () => {
selectTypeId.value = '';
selectTypeId.value = [];
const {formApi, reload} = tableApi;
await formApi.resetForm();
const formValues = formApi.form.values;
@@ -61,7 +61,11 @@ const gridOptions: VxeGridProps = {
proxyConfig: {
ajax: {
query: async ({page}, formValues = {}) => {
formValues.machineTypeId = selectTypeId.value
if (selectTypeId.value && selectTypeId.value.length) {
formValues.machineTypeId = selectTypeId.value[0]
}else {
formValues.machineTypeId =[]
}
return await machineList({
pageNum: page.currentPage,
pageSize: page.pageSize,

View File

@@ -2,6 +2,7 @@ import type {FormSchemaGetter} from '#/adapter/form';
import type {VxeGridProps} from '#/adapter/vxe-table';
import {getDictOptions} from "#/utils/dict";
import {renderDict} from "#/utils/render";
import {z} from "#/adapter/form";
export const querySchema: FormSchemaGetter = () => [
@@ -22,10 +23,6 @@ export const querySchema: FormSchemaGetter = () => [
export const columns: VxeGridProps['columns'] = [
{type: 'checkbox', width: 60},
{
title: '主键',
field: 'id',
},
{
title: '计划名称',
field: 'planName',
@@ -34,10 +31,10 @@ export const columns: VxeGridProps['columns'] = [
title: '计划编号',
field: 'planNo',
},
{
title: '设备类型',
field: 'machineTypeId',
},
// {
// title: '设备类型',
// field: 'machineTypeId',
// },
{
title: '保养周期',
field: 'planPeriod',
@@ -179,31 +176,14 @@ export const modalSchema: FormSchemaGetter = () => [
},
rules:'required'
},
// {
// label: '开始时间',
// fieldName: 'startDate',
// component: 'DatePicker',
// componentProps: {
// showTime: true,
// format: 'YYYY-MM-DD HH:mm:ss',
// valueFormat: 'YYYY-MM-DD HH:mm:ss',
// },
// rules:'required'
// },
// {
// label: '结束时间',
// fieldName: 'endDate',
// component: 'DatePicker',
// componentProps: {
// showTime: true,
// format: 'YYYY-MM-DD HH:mm:ss',
// valueFormat: 'YYYY-MM-DD HH:mm:ss',
// },
// rules:'required'
// },
// {
// label: '状态(0启用,1停用)',
// fieldName: 'state',
// component: 'Input',
// },
{
label: '巡检人员',
fieldName: 'userId',
component: 'ApiSelect',
componentProps:{
mode: 'multiple',
},
rules: z.array(z.string()).min(1, { message: '请选择巡检人员' }),
formItemClass: 'col-span-2'
},
];

View File

@@ -182,8 +182,8 @@ function handleDownloadExcel() {
</template>
<template #state="{row}">
<TableSwitch
:checkedValue="1"
:unCheckedValue="0"
checkedValue="1"
unCheckedValue="0"
v-model:value="row.state"
:api="() => maintainPlanUpdate(row)"
:disabled="!hasAccessByCodes(['property:depot:edit'])"

View File

@@ -15,6 +15,8 @@ import {defaultFormValueGetter, useBeforeCloseDiff} from '#/utils/popup';
import {modalSchema} from './data';
import {getMachineTypeTree} from "#/api/property/machineType";
import {personList} from "#/api/property/resident/person";
import {renderDictValue} from "#/utils/render";
const emit = defineEmits<{ reload: [] }>();
@@ -62,12 +64,16 @@ const [BasicModal, modalApi] = useVbenModal({
const {id} = modalApi.getData() as { id?: number | string };
isUpdate.value = !!id;
await setupTypeSelect();
await queryPersonData()
if (isUpdate.value && id) {
const record = await maintainPlanInfo(id);
record.planDate = [record.startDate, record.endDate]
if (record.planPeriod == '1') {
record.maintainanceMonth = record.maintainanceMonth?.split(',')
record.maintainanceDay = record.maintainanceDay?.split(',')
record.maintainMonth = record.maintainMonth?.split(',')
record.maintainDay = record.maintainDay?.split(',')
}
if (record.machineMaintainPlanStaffBoList) {
record.userId = record.machineMaintainPlanStaffBoList.map(item=>item.userId)
}
await formApi.setValues(record);
}
@@ -91,13 +97,19 @@ async function handleConfirm() {
data.endDate = data.planDate[1]
}
if (data.planPeriod == '1') {
data.maintainanceMonth = data.maintainanceMonth?.join(',')
data.maintainanceDay = data.maintainanceDay?.join(',')
data.maintainanceEveryday = undefined
data.maintainMonth = data.maintainMonth?.join(',')
data.maintainDay = data.maintainDay?.join(',')
data.maintainEveryday = undefined
} else {
data.maintainanceMonth = undefined
data.maintainanceDay = undefined
data.maintainMonth = undefined
data.maintainDay = undefined
}
data.machineMaintainPlanStaffBoList = []
data.userId.forEach((item: string) => {
data.machineMaintainPlanStaffBoList.push({
userId: item
})
})
await (isUpdate.value ? maintainPlanUpdate(data) : maintainPlanAdd(data));
resetInitialized();
emit('reload');
@@ -143,6 +155,29 @@ async function setupTypeSelect() {
},
]);
}
async function queryPersonData() {
let params = {
pageSize: 1000,
pageNum: 1,
}
const res = await personList(params);
const options = res.rows.map((user) => ({
label: user.userName + '-' + renderDictValue(user.gender, 'sys_user_sex')
+ '-' + user.phone + '-' + user.unitName,
value: user.id,
}));
formApi.updateSchema([{
componentProps: () => ({
options: options,
showSearch: true,
optionFilterProp: 'label',
optionLabelProp: 'label',
mode: 'multiple',
}),
fieldName: 'userId',
}])
}
</script>
<template>

View File

@@ -43,9 +43,13 @@ export const columns: VxeGridProps['columns'] = [
field: 'roomTypeName',
},
{
title: '面积',
title: '建筑面积',
field: 'area',
},
{
title: '套内面积',
field: 'insideInArea',
},
{
title: '户型',
field: 'layout',
@@ -110,10 +114,18 @@ export const modalSchema: FormSchemaGetter = () => [
},
},
{
label: '面积',
label: '建筑面积',
fieldName: 'area',
component: 'Input',
},
{
label: '套内面积',
fieldName: 'insideInArea',
component: 'InputNumber',
componentProps:{
min:0,
}
},
{
label: '户型',
fieldName: 'layout',