mirror of
http://183.237.82.114:10086/kilo/leaser_front.git
synced 2026-10-23 20:36:02 +00:00
feat: 项目管理列表搜索、新增/编辑
This commit is contained in:
parent
40a28f38cb
commit
52102d2e72
17 changed files with 843 additions and 68 deletions
|
|
@ -65,6 +65,7 @@ module.exports = {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
|
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
|
||||||
|
'no-plusplus': ['error', { allowForLoopAfterthoughts: true }],
|
||||||
'no-param-reassign': 0,
|
'no-param-reassign': 0,
|
||||||
'prefer-regex-literals': 0,
|
'prefer-regex-literals': 0,
|
||||||
'import/no-extraneous-dependencies': 0,
|
'import/no-extraneous-dependencies': 0,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import {
|
||||||
CreateProjectParam,
|
CreateProjectParam,
|
||||||
ProjectDetailRes,
|
ProjectDetailRes,
|
||||||
} from '@/types/business';
|
} from '@/types/business';
|
||||||
import { PaginationParams } from '@/types/comm';
|
import { ProjectPaginationParams } from '@/types/comm';
|
||||||
import { HttpResponse } from './interceptor';
|
import { HttpResponse } from './interceptor';
|
||||||
// import type { RouteRecordNormalized } from 'vue-router';
|
// import type { RouteRecordNormalized } from 'vue-router';
|
||||||
// import { UserState } from '@/store/modules/user/types';
|
// import { UserState } from '@/store/modules/user/types';
|
||||||
|
|
@ -22,7 +22,7 @@ export function getAllProjectList(): Promise<AllProjectListRes> {
|
||||||
}
|
}
|
||||||
// 项目列表(分页)
|
// 项目列表(分页)
|
||||||
export function getProjectList(
|
export function getProjectList(
|
||||||
params: PaginationParams
|
params: ProjectPaginationParams
|
||||||
): Promise<ProjectListRes> {
|
): Promise<ProjectListRes> {
|
||||||
return axios.post('project/getProjectList', params);
|
return axios.post('project/getProjectList', params);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
74
src/api/common.ts
Normal file
74
src/api/common.ts
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
import axios from 'axios';
|
||||||
|
import {
|
||||||
|
CommonProvinceRes,
|
||||||
|
CommonCityRes,
|
||||||
|
CommonDistrictRes,
|
||||||
|
AllProjectIdNameListRes,
|
||||||
|
AllBuildingIdNameListRes,
|
||||||
|
AllFloorIdNameListRes,
|
||||||
|
AllInvoiceTypeIdNameListRes,
|
||||||
|
AllFilingTypeIdNameListRes,
|
||||||
|
UploadParam,
|
||||||
|
BankListRes,
|
||||||
|
} from '@/types/comm';
|
||||||
|
import { HttpResponse } from './interceptor';
|
||||||
|
|
||||||
|
export interface ProjectRecord {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 省份列表
|
||||||
|
export function getProvinceList(): Promise<CommonProvinceRes> {
|
||||||
|
return axios.get(`common/getProvinceList`);
|
||||||
|
}
|
||||||
|
// 城市列表
|
||||||
|
export function getCityListByProvinceId(id: number): Promise<CommonCityRes> {
|
||||||
|
return axios.get(`common/getCityList?province_id=${id}`);
|
||||||
|
}
|
||||||
|
// 区域列表
|
||||||
|
export function getDistrictListByCityId(
|
||||||
|
id: number
|
||||||
|
): Promise<CommonDistrictRes> {
|
||||||
|
return axios.get(`common/getDistrictList?city_id=${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 全部项目(id,名称)列表
|
||||||
|
export function getAllProjectIdNameList(): Promise<AllProjectIdNameListRes> {
|
||||||
|
return axios.get(`common/getAllProjectList`);
|
||||||
|
}
|
||||||
|
// 全部楼栋(id,名称)列表(根据项目id查询)
|
||||||
|
export function getAllBuildingIdNameList(
|
||||||
|
id: number
|
||||||
|
): Promise<AllBuildingIdNameListRes> {
|
||||||
|
return axios.get(`common/getAllBuildingList?project_id=${id}`);
|
||||||
|
}
|
||||||
|
// 全部楼层(id,名称)列表(根据楼栋id查询)
|
||||||
|
export function getAllFloorIdNameList(
|
||||||
|
id: number
|
||||||
|
): Promise<AllFloorIdNameListRes> {
|
||||||
|
return axios.get(`common/getAllFloorList?building_id=${id}`);
|
||||||
|
}
|
||||||
|
// 发票类型列表
|
||||||
|
export function getInvoiceTypeList(): Promise<AllInvoiceTypeIdNameListRes> {
|
||||||
|
return axios.get(`common/getInvoiceTypeList`);
|
||||||
|
}
|
||||||
|
// 租客归档类型列表
|
||||||
|
export function getFilingTypeList(): Promise<AllFilingTypeIdNameListRes> {
|
||||||
|
return axios.get(`common/getFilingTypeList`);
|
||||||
|
}
|
||||||
|
// 上传文档
|
||||||
|
export function ossUpload(
|
||||||
|
params: UploadParam | FormData
|
||||||
|
): Promise<HttpResponse> {
|
||||||
|
return axios.post('common/ossUpload', params);
|
||||||
|
}
|
||||||
|
// 下载文档
|
||||||
|
export function ossDownload(key: string): Promise<HttpResponse> {
|
||||||
|
return axios.get(`common/ossDownload?key=${key}`);
|
||||||
|
}
|
||||||
|
// 银行列表
|
||||||
|
export function getBanksList(keyword: string): Promise<BankListRes> {
|
||||||
|
return axios.post('common/getBanksList', { keyword });
|
||||||
|
}
|
||||||
115
src/components/common/ImageUpload.md
Normal file
115
src/components/common/ImageUpload.md
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
# ImageUpload 图片上传组件
|
||||||
|
|
||||||
|
一个可复用的图片上传组件,支持自定义上传类型、文件大小限制等功能。
|
||||||
|
|
||||||
|
## 功能特性
|
||||||
|
|
||||||
|
- 支持图片格式:JPG、PNG、GIF、WEBP
|
||||||
|
- 可自定义文件大小限制
|
||||||
|
- 支持上传进度显示
|
||||||
|
- 支持图片预览
|
||||||
|
- 支持多文件上传
|
||||||
|
- 上传成功后自动回传URL
|
||||||
|
|
||||||
|
## Props
|
||||||
|
|
||||||
|
| 参数 | 类型 | 默认值 | 说明 |
|
||||||
|
|------|------|--------|------|
|
||||||
|
| modelValue | UploadFile[] | [] | 文件列表,支持v-model |
|
||||||
|
| uploadType | UploadType | - | 上传类型:1合同模版,2图片 |
|
||||||
|
| maxSize | number | 10 * 1024 * 1024 | 最大文件大小(字节) |
|
||||||
|
| maxCount | number | - | 最大上传数量 |
|
||||||
|
|
||||||
|
## Events
|
||||||
|
|
||||||
|
| 事件名 | 参数 | 说明 |
|
||||||
|
|--------|------|------|
|
||||||
|
| update:modelValue | (value: UploadFile[]) | 文件列表更新时触发 |
|
||||||
|
| upload-success | (file: UploadFile, response: UploadResponse) | 上传成功时触发 |
|
||||||
|
| upload-error | (error: Error) | 上传失败时触发 |
|
||||||
|
|
||||||
|
## 类型定义
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface UploadFile {
|
||||||
|
uid: string;
|
||||||
|
name: string;
|
||||||
|
url?: string;
|
||||||
|
status: 'done' | 'uploading' | 'error';
|
||||||
|
percent?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UploadResponse {
|
||||||
|
filename: string;
|
||||||
|
size: number;
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type UploadType = 1 | 2; // 1合同模版,2图片
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用示例
|
||||||
|
|
||||||
|
### 基础用法
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<ImageUpload
|
||||||
|
v-model="images"
|
||||||
|
:upload-type="2"
|
||||||
|
@upload-success="handleUploadSuccess"
|
||||||
|
@upload-error="handleUploadError"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import ImageUpload from '@/components/common/ImageUpload.vue';
|
||||||
|
|
||||||
|
const images = ref([]);
|
||||||
|
|
||||||
|
const handleUploadSuccess = (file, response) => {
|
||||||
|
console.log('上传成功:', response.url);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleUploadError = (error) => {
|
||||||
|
console.error('上传失败:', error.message);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 自定义文件大小限制
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<ImageUpload
|
||||||
|
v-model="images"
|
||||||
|
:upload-type="2"
|
||||||
|
:max-size="5 * 1024 * 1024"
|
||||||
|
@upload-success="handleUploadSuccess"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 在表单中使用
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<a-form-item label="项目图片" field="images">
|
||||||
|
<ImageUpload
|
||||||
|
v-model="form.images"
|
||||||
|
:upload-type="2"
|
||||||
|
:max-size="10 * 1024 * 1024"
|
||||||
|
@upload-success="handleUploadSuccess"
|
||||||
|
@upload-error="handleUploadError"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. 组件会自动处理文件验证,包括文件大小和格式检查
|
||||||
|
2. 上传成功后,文件对象会自动更新URL和状态
|
||||||
|
3. 支持v-model双向绑定,父组件可以直接操作文件列表
|
||||||
|
4. 上传类型必须从父组件传入,用于区分不同的上传场景
|
||||||
165
src/components/common/ImageUpload.vue
Normal file
165
src/components/common/ImageUpload.vue
Normal file
|
|
@ -0,0 +1,165 @@
|
||||||
|
<template>
|
||||||
|
<a-upload
|
||||||
|
:list-type="listType"
|
||||||
|
:accept="'image/*'"
|
||||||
|
:custom-request="customRequest"
|
||||||
|
:file-list="fileList"
|
||||||
|
:multiple="maxCount > 1"
|
||||||
|
:limit="maxCount"
|
||||||
|
image-preview
|
||||||
|
@change="handleChange"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, watch } from 'vue';
|
||||||
|
import { ossUpload } from '@/api/common';
|
||||||
|
import type { UploadType } from '@/types/comm';
|
||||||
|
|
||||||
|
interface UploadFile {
|
||||||
|
uid: string;
|
||||||
|
name: string;
|
||||||
|
url?: string;
|
||||||
|
status: 'done' | 'uploading' | 'error';
|
||||||
|
percent?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UploadResponse {
|
||||||
|
filename: string;
|
||||||
|
size: number;
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
modelValue: string[]; // 只暴露图片url数组
|
||||||
|
uploadType: UploadType;
|
||||||
|
maxSize?: number; // 默认10MB
|
||||||
|
maxCount?: number; // 最大上传数量,默认1(单图)
|
||||||
|
listType?: 'text' | 'picture' | 'picture-card'; // 上传样式
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'update:modelValue', value: string[]): void;
|
||||||
|
(e: 'upload-success', url: string): void;
|
||||||
|
(e: 'upload-error', error: Error): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const fileList = ref<UploadFile[]>([]);
|
||||||
|
|
||||||
|
// 默认最大上传数:1(单图)
|
||||||
|
const maxCount = props.maxCount ?? 1;
|
||||||
|
|
||||||
|
// listType 默认值
|
||||||
|
const listType = props.listType ?? 'picture-card';
|
||||||
|
|
||||||
|
// 将外部 url[] 映射为 UploadFile[]
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
(newVal) => {
|
||||||
|
fileList.value = (newVal || []).map((url, index) => ({
|
||||||
|
uid: `${index}`,
|
||||||
|
name: `图片-${index + 1}`,
|
||||||
|
url,
|
||||||
|
status: 'done',
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
// 自定义上传请求
|
||||||
|
const customRequest = async (option: any) => {
|
||||||
|
const { onProgress, onError, onSuccess, fileItem } = option;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const file = fileItem.file;
|
||||||
|
const maxSize = props.maxSize || 10 * 1024 * 1024;
|
||||||
|
const allowedTypes = [
|
||||||
|
'image/jpeg',
|
||||||
|
'image/png',
|
||||||
|
'image/gif',
|
||||||
|
'image/webp',
|
||||||
|
];
|
||||||
|
|
||||||
|
// 文件大小校验
|
||||||
|
if (file.size > maxSize) {
|
||||||
|
onError(
|
||||||
|
new Error(`文件大小不能超过${Math.round(maxSize / 1024 / 1024)}MB`)
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文件类型校验
|
||||||
|
if (!allowedTypes.includes(file.type)) {
|
||||||
|
onError(new Error('只支持 JPG、PNG、GIF、WEBP 格式的图片'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 模拟上传进度
|
||||||
|
let progress = 0;
|
||||||
|
const progressInterval = setInterval(() => {
|
||||||
|
progress += Math.random() * 30;
|
||||||
|
if (progress >= 90) {
|
||||||
|
clearInterval(progressInterval);
|
||||||
|
progress = 90;
|
||||||
|
}
|
||||||
|
onProgress({ percent: Math.round(progress) });
|
||||||
|
}, 200);
|
||||||
|
|
||||||
|
// 上传到服务器
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('file', file);
|
||||||
|
formData.append('type', props.uploadType.toString());
|
||||||
|
|
||||||
|
const { code, data, message } = await ossUpload(formData);
|
||||||
|
clearInterval(progressInterval);
|
||||||
|
|
||||||
|
if (code === 200 && data) {
|
||||||
|
const response = data as UploadResponse;
|
||||||
|
|
||||||
|
fileItem.status = 'done';
|
||||||
|
fileItem.url = response.url;
|
||||||
|
onProgress({ percent: 100 });
|
||||||
|
onSuccess(response);
|
||||||
|
|
||||||
|
// 新的图片 url
|
||||||
|
let updatedUrls = [...props.modelValue];
|
||||||
|
if (maxCount === 1) {
|
||||||
|
updatedUrls = [response.url];
|
||||||
|
} else {
|
||||||
|
updatedUrls.push(response.url);
|
||||||
|
// 截取至 maxCount
|
||||||
|
updatedUrls = updatedUrls.slice(0, maxCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit('update:modelValue', updatedUrls);
|
||||||
|
emit('upload-success', response.url);
|
||||||
|
} else {
|
||||||
|
const error = new Error(message || '上传失败');
|
||||||
|
onError(error);
|
||||||
|
emit('upload-error', error);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
const err = error instanceof Error ? error : new Error('上传失败');
|
||||||
|
onError(err);
|
||||||
|
emit('upload-error', err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 同步 fileList(删除时触发)
|
||||||
|
const handleChange = (newFileList: UploadFile[]) => {
|
||||||
|
const validUrls = newFileList
|
||||||
|
.filter((f) => f.status === 'done' && f.url)
|
||||||
|
.map((f) => f.url!);
|
||||||
|
|
||||||
|
emit('update:modelValue', validUrls);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
:deep(.arco-upload-list-picture-card) {
|
||||||
|
.arco-upload-list-item {
|
||||||
|
width: 104px;
|
||||||
|
height: 104px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -12,6 +12,7 @@ import {
|
||||||
import Chart from './chart/index.vue';
|
import Chart from './chart/index.vue';
|
||||||
import Breadcrumb from './breadcrumb/index.vue';
|
import Breadcrumb from './breadcrumb/index.vue';
|
||||||
import DataTable from './common/DataTable.vue';
|
import DataTable from './common/DataTable.vue';
|
||||||
|
import ImageUpload from './common/ImageUpload.vue';
|
||||||
|
|
||||||
// Manually introduce ECharts modules to reduce packing size
|
// Manually introduce ECharts modules to reduce packing size
|
||||||
|
|
||||||
|
|
@ -33,5 +34,6 @@ export default {
|
||||||
Vue.component('Chart', Chart);
|
Vue.component('Chart', Chart);
|
||||||
Vue.component('Breadcrumb', Breadcrumb);
|
Vue.component('Breadcrumb', Breadcrumb);
|
||||||
Vue.component('DataTable', DataTable);
|
Vue.component('DataTable', DataTable);
|
||||||
|
Vue.component('ImageUpload', ImageUpload);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,11 @@ import {
|
||||||
} from './comm';
|
} from './comm';
|
||||||
|
|
||||||
export interface BuildingItem {
|
export interface BuildingItem {
|
||||||
|
customId?: string;
|
||||||
id?: string; // 楼栋id
|
id?: string; // 楼栋id
|
||||||
name: string; // 楼栋名称
|
name: string; // 楼栋名称
|
||||||
up_floor?: number; // 地上层数
|
up_floor?: number | null; // 地上层数
|
||||||
down_floor?: number; // 地下层数
|
down_floor?: number | null; // 地下层数
|
||||||
}
|
}
|
||||||
export interface AllProjectsItem {
|
export interface AllProjectsItem {
|
||||||
id: number; // 项目id
|
id: number; // 项目id
|
||||||
|
|
@ -57,9 +58,9 @@ export interface CreateProjectParam {
|
||||||
id?: number;
|
id?: number;
|
||||||
type: ProjectType; // 项目类型:1写字楼,2商业综合体,3园区
|
type: ProjectType; // 项目类型:1写字楼,2商业综合体,3园区
|
||||||
name: string; // 楼栋名称
|
name: string; // 楼栋名称
|
||||||
province_id: number; // 省份id
|
province_id: number | null; // 省份id
|
||||||
city_id: number; // 城市id
|
city_id: number | null; // 城市id
|
||||||
district_id: number; // 区域id
|
district_id: number | null; // 区域id
|
||||||
address: string; // 详细地址
|
address: string; // 详细地址
|
||||||
images: string[]; // 图片数组
|
images: string[]; // 图片数组
|
||||||
buildings: BuildingItem[]; // 楼栋数据
|
buildings: BuildingItem[]; // 楼栋数据
|
||||||
|
|
@ -93,4 +94,6 @@ export interface ProjectInfo extends BaseEntity {
|
||||||
buildings: BuildingInfo[]; // 楼栋数组
|
buildings: BuildingInfo[]; // 楼栋数组
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProjectDetailRes = ProjectInfo;
|
export type ProjectDetailRes = HttpResponse<{
|
||||||
|
project: ProjectInfo;
|
||||||
|
}>;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { HttpResponse } from '@/api/interceptor';
|
||||||
// ==================== 通用类型定义 ====================
|
// ==================== 通用类型定义 ====================
|
||||||
export interface BaseEntity {
|
export interface BaseEntity {
|
||||||
id: number;
|
id: number;
|
||||||
|
|
@ -30,3 +31,66 @@ export type CommStatus = 1 | 2; // 1:是 2否
|
||||||
export type FeeManagementType = 1 | 2; // 费项属性:1收入费项,2支出费项
|
export type FeeManagementType = 1 | 2; // 费项属性:1收入费项,2支出费项
|
||||||
export type FeeType = 1 | 2 | 3 | 4; // 费项类型:1租赁类,2物业类,3特殊类,4其他类
|
export type FeeType = 1 | 2 | 3 | 4; // 费项类型:1租赁类,2物业类,3特殊类,4其他类
|
||||||
export type ProjectType = 1 | 2 | 3; // 项目类型:1写字楼,2商业综合体,3园区
|
export type ProjectType = 1 | 2 | 3; // 项目类型:1写字楼,2商业综合体,3园区
|
||||||
|
export type UploadType = 1 | 2; // 1合同模版,2图片
|
||||||
|
|
||||||
|
// ==================== 公共接口类型定义 ====================
|
||||||
|
|
||||||
|
export interface CommonProvinceItem {
|
||||||
|
ID?: number;
|
||||||
|
id?: number;
|
||||||
|
name?: string; // 省份
|
||||||
|
Name?: string; // 省份
|
||||||
|
code: string; // 编码
|
||||||
|
}
|
||||||
|
export interface CommonCityItem extends CommonProvinceItem {
|
||||||
|
ProvinceId: number; // 所属省份id
|
||||||
|
}
|
||||||
|
export interface CommonDistrictItem extends CommonProvinceItem {
|
||||||
|
CityId: number; // 所属城市id
|
||||||
|
}
|
||||||
|
// 省份
|
||||||
|
export type CommonProvinceRes = HttpResponse<{
|
||||||
|
provinceList?: CommonProvinceItem[];
|
||||||
|
}>;
|
||||||
|
// 城市
|
||||||
|
export type CommonCityRes = HttpResponse<{
|
||||||
|
cityList?: CommonCityItem[];
|
||||||
|
}>;
|
||||||
|
// 区域
|
||||||
|
export type CommonDistrictRes = HttpResponse<{
|
||||||
|
districtList?: CommonDistrictItem[];
|
||||||
|
}>;
|
||||||
|
// 全部项目(id、名称的列表)
|
||||||
|
export interface AllIdNameItem {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
export interface ProjectPaginationParams extends PaginationParams {
|
||||||
|
type?: ProjectType[] | null;
|
||||||
|
province_ids?: number[] | null;
|
||||||
|
rent_rate_min?: number | null;
|
||||||
|
rent_rate_max?: number | null;
|
||||||
|
}
|
||||||
|
export type AllProjectIdNameListRes = HttpResponse<{
|
||||||
|
projectsList?: AllIdNameItem[];
|
||||||
|
}>;
|
||||||
|
export type AllBuildingIdNameListRes = HttpResponse<{
|
||||||
|
buildingList?: AllIdNameItem[];
|
||||||
|
}>;
|
||||||
|
export type AllFloorIdNameListRes = HttpResponse<{
|
||||||
|
floorList?: AllIdNameItem[];
|
||||||
|
}>;
|
||||||
|
export type AllInvoiceTypeIdNameListRes = HttpResponse<{
|
||||||
|
invoiceTypeList?: AllIdNameItem[];
|
||||||
|
}>;
|
||||||
|
export type AllFilingTypeIdNameListRes = HttpResponse<{
|
||||||
|
filingTypeList?: AllIdNameItem[];
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export interface UploadParam {
|
||||||
|
file: any;
|
||||||
|
type: UploadType;
|
||||||
|
}
|
||||||
|
export type BankListRes = HttpResponse<{
|
||||||
|
banksList?: AllIdNameItem[];
|
||||||
|
}>;
|
||||||
|
|
|
||||||
|
|
@ -34,5 +34,15 @@ export function debounce<T extends (...args: any[]) => any>(
|
||||||
}, delay);
|
}, delay);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
export const generateRandomId = (length = 8) => {
|
||||||
|
const chars =
|
||||||
|
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||||
|
let result = '';
|
||||||
|
const charsLength = chars.length;
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
result += chars.charAt(Math.floor(Math.random() * charsLength));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
export default null;
|
export default null;
|
||||||
|
|
|
||||||
|
|
@ -14,10 +14,10 @@
|
||||||
auto-label-width
|
auto-label-width
|
||||||
layout="vertical"
|
layout="vertical"
|
||||||
>
|
>
|
||||||
<a-row>
|
<a-row :gutter="24">
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<!-- 项目名称 -->
|
<!-- 项目名称 -->
|
||||||
<a-form-item label="条款名称" field="name" required>
|
<a-form-item label="项目名称" field="name" required>
|
||||||
<a-input
|
<a-input
|
||||||
v-model="form.name"
|
v-model="form.name"
|
||||||
:placeholder="$t('please.enter', { placeholder: '项目名称' })"
|
:placeholder="$t('please.enter', { placeholder: '项目名称' })"
|
||||||
|
|
@ -26,7 +26,7 @@
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<!-- 项目类型 -->
|
<!-- 项目类型 -->
|
||||||
<a-form-item label="项目类型" field="name" required>
|
<a-form-item label="项目类型" field="type" required>
|
||||||
<a-select v-model="form.type" placeholder="请选择项目类型">
|
<a-select v-model="form.type" placeholder="请选择项目类型">
|
||||||
<a-option
|
<a-option
|
||||||
v-for="item in projectTypeList"
|
v-for="item in projectTypeList"
|
||||||
|
|
@ -40,16 +40,131 @@
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
<!-- 项目地址 -->
|
<!-- 项目地址 -->
|
||||||
<a-form-item label="项目地址" field="type" required>
|
<a-form-item label="项目地址" field="address" required>
|
||||||
<!-- <a-space>
|
<a-row :gutter="12" wrap :style="{ width: '100%' }">
|
||||||
<a-select v-model="form.finance_fee_id" :style="{ width: '200px' }" placeholder="请选择省份...">
|
<a-col :span="12">
|
||||||
<a-option v-for="item in financeFeeList" :key="item.id" :value="item.id">
|
<a-select
|
||||||
|
v-model="form.province_id"
|
||||||
|
:style="{ width: '100%' }"
|
||||||
|
placeholder="请选择省份..."
|
||||||
|
@change="handleChangeProvince"
|
||||||
|
>
|
||||||
|
<a-option
|
||||||
|
v-for="item in provinceList"
|
||||||
|
:key="item.id"
|
||||||
|
:value="item.id"
|
||||||
|
>
|
||||||
{{ item.name }}
|
{{ item.name }}
|
||||||
</a-option>
|
</a-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
<a-select v-model="form.finance_fee_periodicity" :style="{ width: '200px' }"
|
</a-col>
|
||||||
:options="financeFeeFeriodicityList" placeholder="请选择..." />
|
<a-col :span="12">
|
||||||
</a-space> -->
|
<a-row :gutter="12">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-select
|
||||||
|
v-model="form.city_id"
|
||||||
|
:disabled="cityList.length === 0"
|
||||||
|
:style="{ width: '100%' }"
|
||||||
|
placeholder="请选择城市..."
|
||||||
|
@change="handleChangeCity"
|
||||||
|
>
|
||||||
|
<a-option
|
||||||
|
v-for="item in cityList"
|
||||||
|
:key="item.id"
|
||||||
|
:value="item.id"
|
||||||
|
>
|
||||||
|
{{ item.name }}
|
||||||
|
</a-option>
|
||||||
|
</a-select>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-select
|
||||||
|
v-model="form.district_id"
|
||||||
|
:disabled="districtList.length === 0"
|
||||||
|
:style="{ width: '100%' }"
|
||||||
|
placeholder="请选择区域..."
|
||||||
|
>
|
||||||
|
<a-option
|
||||||
|
v-for="item in districtList"
|
||||||
|
:key="item.id"
|
||||||
|
:value="item.id"
|
||||||
|
>
|
||||||
|
{{ item.name }}
|
||||||
|
</a-option>
|
||||||
|
</a-select>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="24" style="margin-top: 10px">
|
||||||
|
<a-input
|
||||||
|
v-model="form.address"
|
||||||
|
:disabled="!form.district_id"
|
||||||
|
placeholder="请输入详细地址..."
|
||||||
|
/>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="项目图片" field="images">
|
||||||
|
<ImageUpload
|
||||||
|
v-model="form.images"
|
||||||
|
:upload-type="2"
|
||||||
|
:max-count="5"
|
||||||
|
:list-type="'picture-card'"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item>
|
||||||
|
<template #label>
|
||||||
|
<div class="flex_center_between">
|
||||||
|
<p class="left">楼栋管理</p>
|
||||||
|
<div class="right">
|
||||||
|
<a-button type="outline" @click="handleAddBuilding">
|
||||||
|
<icon-plus /> 新增楼栋
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<draggable
|
||||||
|
v-model="form.buildings"
|
||||||
|
:animation="200"
|
||||||
|
:item-key="props.isEdit ? 'id' : 'customId'"
|
||||||
|
@end="handleDragEnd"
|
||||||
|
>
|
||||||
|
<template #item="{ element }">
|
||||||
|
<a-row align="center" :gutter="12" style="margin-top: 10px">
|
||||||
|
<a-col :span="2">
|
||||||
|
<icon-unordered-list :size="24" />
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="8">
|
||||||
|
<a-input
|
||||||
|
v-model="element.name"
|
||||||
|
placeholder="输入楼栋号(如A座)"
|
||||||
|
/>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-input-number
|
||||||
|
v-model="element.up_floor"
|
||||||
|
prepend="地面"
|
||||||
|
append="层"
|
||||||
|
/>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-input-number
|
||||||
|
v-model="element.down_floor"
|
||||||
|
prepend="地下"
|
||||||
|
append="层"
|
||||||
|
/>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="2">
|
||||||
|
<icon-delete
|
||||||
|
:size="16"
|
||||||
|
style="cursor: pointer"
|
||||||
|
@click="handleDelBuilding(element.customId)"
|
||||||
|
/>
|
||||||
|
</a-col>
|
||||||
|
<a-divider />
|
||||||
|
</a-row>
|
||||||
|
</template>
|
||||||
|
</draggable>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
</a-drawer>
|
</a-drawer>
|
||||||
|
|
@ -58,10 +173,25 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, watch, reactive, onMounted } from 'vue';
|
import { ref, watch, reactive, onMounted } from 'vue';
|
||||||
import type { FormInstance } from '@arco-design/web-vue';
|
import type { FormInstance } from '@arco-design/web-vue';
|
||||||
// import { Message } from '@arco-design/web-vue';
|
import { Message } from '@arco-design/web-vue';
|
||||||
import { projectTypeList } from '@/utils/constant';
|
import { projectTypeList } from '@/utils/constant';
|
||||||
// import { CreateTermSettingParam, FeeManagement } from '@/types/setting';
|
import {
|
||||||
// import { FeeManagementType } from '@/types/comm';
|
CommonProvinceItem,
|
||||||
|
CommonCityItem,
|
||||||
|
CommonDistrictItem,
|
||||||
|
ProjectType,
|
||||||
|
} from '@/types/comm';
|
||||||
|
import {
|
||||||
|
getProvinceList,
|
||||||
|
getCityListByProvinceId,
|
||||||
|
getDistrictListByCityId,
|
||||||
|
} from '@/api/common';
|
||||||
|
import { createProject, updateProject } from '@/api/business';
|
||||||
|
import { CreateProjectParam } from '@/types/business';
|
||||||
|
import ImageUpload from '@/components/common/ImageUpload.vue';
|
||||||
|
import draggable from 'vuedraggable';
|
||||||
|
import { generateRandomId } from '@/utils';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
isEdit: boolean;
|
isEdit: boolean;
|
||||||
|
|
@ -71,34 +201,128 @@
|
||||||
(e: 'update:visible', val: boolean): void;
|
(e: 'update:visible', val: boolean): void;
|
||||||
(e: 'submitted'): void;
|
(e: 'submitted'): void;
|
||||||
}>();
|
}>();
|
||||||
|
const createEmptyBuilding = () => {
|
||||||
|
return {
|
||||||
|
customId: generateRandomId(),
|
||||||
|
name: '',
|
||||||
|
up_floor: null,
|
||||||
|
down_floor: null,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const isLoading = ref<boolean>(false);
|
||||||
// 表单数据
|
// 表单数据
|
||||||
const formRef = ref<FormInstance>();
|
const formRef = ref<FormInstance>();
|
||||||
const defaultForm: any = {
|
|
||||||
name: '', // 名称
|
const defaultForm: CreateProjectParam = {
|
||||||
type: 1, // 条款类型:1租赁类,2物业类,3特殊类,4其他类
|
type: 1 as ProjectType, // 项目类型:1写字楼,2商业综合体,3园区
|
||||||
is_fee: 1, // 是否勾选费项:1是,2否
|
name: '', // 楼栋名称
|
||||||
finance_fee_id: null, // 财务费项id(除保证金外
|
province_id: null, // 省份id
|
||||||
finance_fee_periodicity: 1, // 周期性:1周期性付款,2一次性付款
|
city_id: null, // 城市id
|
||||||
is_bond: 2, // 是否勾选保证金:1是,2否
|
district_id: null, // 区域id
|
||||||
finance_bond_fee_id: null, // 财务保证金费项id
|
address: '', // 详细地址
|
||||||
is_incremental: 2, // 是否勾选递增项:1是,2否
|
images: [], // 图片数组
|
||||||
is_discount: 2, // 是否勾选优惠项:1是,2否
|
buildings: [createEmptyBuilding()], // 楼栋数据
|
||||||
is_late_fee: 2, // 是否勾选滞纳金:1是,2否
|
|
||||||
};
|
};
|
||||||
const form = reactive<any>({ ...defaultForm });
|
const form = reactive<CreateProjectParam>({ ...defaultForm });
|
||||||
// 校验规则
|
// 校验规则
|
||||||
const rules = reactive({
|
const rules = reactive({
|
||||||
name: [{ required: true, message: '请输入条款名称' }],
|
name: [{ required: true, message: '请输入项目名称' }],
|
||||||
type: [{ required: true, message: '请选择条款类别' }],
|
type: [{ required: true, message: '请选择项目类型' }],
|
||||||
|
address: [
|
||||||
|
{ required: true, message: '详细地址不能为空' },
|
||||||
|
{
|
||||||
|
'validator': (_, cb) => {
|
||||||
|
if (!form.province_id) {
|
||||||
|
cb('请选择项目地址所在省份');
|
||||||
|
} else if (!form.city_id) {
|
||||||
|
cb('请选择项目地址所在城市');
|
||||||
|
} else if (!form.district_id) {
|
||||||
|
cb('请选择项目地址所在区域');
|
||||||
|
} else {
|
||||||
|
cb();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'validate-trigger': 'blur',
|
||||||
|
},
|
||||||
|
],
|
||||||
});
|
});
|
||||||
|
const provinceList = ref<CommonProvinceItem[]>([]);
|
||||||
|
const cityList = ref<CommonCityItem[]>([]);
|
||||||
|
const districtList = ref<CommonDistrictItem[]>([]);
|
||||||
|
|
||||||
|
const getProvinceListData = async () => {
|
||||||
|
const { code, data } = await getProvinceList();
|
||||||
|
if (code === 200 && data) {
|
||||||
|
provinceList.value = Array.isArray(data?.provinceList)
|
||||||
|
? data?.provinceList
|
||||||
|
: [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const getCityListByProvinceIdData = async (provinceId: number) => {
|
||||||
|
const { code, data } = await getCityListByProvinceId(provinceId);
|
||||||
|
if (code === 200 && data) {
|
||||||
|
cityList.value = Array.isArray(data?.cityList) ? data?.cityList : [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const getDistrictListByCityIdData = async (cityId: number) => {
|
||||||
|
const { code, data } = await getDistrictListByCityId(cityId);
|
||||||
|
if (code === 200 && data) {
|
||||||
|
districtList.value = Array.isArray(data?.districtList)
|
||||||
|
? data?.districtList
|
||||||
|
: [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const handleChangeProvince = async (id) => {
|
||||||
|
form.city_id = null;
|
||||||
|
await getCityListByProvinceIdData(id);
|
||||||
|
};
|
||||||
|
const handleChangeCity = async (id) => {
|
||||||
|
form.district_id = null;
|
||||||
|
await getDistrictListByCityIdData(id);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAddBuilding = () => {
|
||||||
|
form.buildings.push(createEmptyBuilding());
|
||||||
|
};
|
||||||
|
const handleDelBuilding = (id: string) => {
|
||||||
|
if (form.buildings.length === 1) return;
|
||||||
|
const index = form.buildings.findIndex((b) => b.customId === id);
|
||||||
|
if (index !== -1) {
|
||||||
|
form.buildings.splice(index, 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
watch(
|
watch(
|
||||||
() => props.visible,
|
() => props.visible,
|
||||||
(val) => {
|
async (val) => {
|
||||||
if (val) {
|
if (val) {
|
||||||
|
console.log(props.record, 'props.record');
|
||||||
|
|
||||||
if (props.isEdit && props.record) {
|
if (props.isEdit && props.record) {
|
||||||
Object.assign(form, props.record);
|
const record = props.record;
|
||||||
|
if (record?.buildings?.length) {
|
||||||
|
record.buildings.forEach((element) => {
|
||||||
|
element.up_floor = element.upFloor;
|
||||||
|
element.down_floor = element.downFloor;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (record?.imageUrls?.length) {
|
||||||
|
record.images = record?.imageUrls;
|
||||||
|
}
|
||||||
|
form.name = record.name;
|
||||||
|
Object.keys(form).forEach((key) => {
|
||||||
|
form[key] = record[key];
|
||||||
|
});
|
||||||
|
form.province_id = record.provinceId;
|
||||||
|
form.city_id = record.cityId;
|
||||||
|
form.district_id = record.districtId;
|
||||||
|
form.id = record.id;
|
||||||
|
// Object.assign(form, record);
|
||||||
|
if (form.province_id) {
|
||||||
|
await getCityListByProvinceIdData(form.province_id);
|
||||||
|
}
|
||||||
|
if (form.city_id) {
|
||||||
|
await getDistrictListByCityIdData(form.city_id);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Object.assign(form, defaultForm);
|
Object.assign(form, defaultForm);
|
||||||
}
|
}
|
||||||
|
|
@ -107,12 +331,45 @@
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleOk = async () => {};
|
const handleOk = async () => {
|
||||||
|
if (isLoading.value) return;
|
||||||
|
try {
|
||||||
|
isLoading.value = true;
|
||||||
|
const valid = await formRef.value.validate();
|
||||||
|
if (valid) return;
|
||||||
|
form.buildings = form.buildings.map((item) => {
|
||||||
|
return {
|
||||||
|
name: item.name,
|
||||||
|
up_floor: item.up_floor,
|
||||||
|
down_floor: item.down_floor,
|
||||||
|
id: item?.id,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
const params = {
|
||||||
|
...form,
|
||||||
|
};
|
||||||
|
const res = props?.isEdit
|
||||||
|
? await updateProject(params)
|
||||||
|
: await createProject(params);
|
||||||
|
if (res?.code === 200) {
|
||||||
|
Message.success(res?.message);
|
||||||
|
emit('submitted');
|
||||||
|
emit('update:visible', false);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// 捕获异常,暂不处理
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
emit('update:visible', false);
|
emit('update:visible', false);
|
||||||
};
|
};
|
||||||
onMounted(() => {});
|
const handleDragEnd = () => {};
|
||||||
|
onMounted(() => {
|
||||||
|
getProvinceListData();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,10 @@
|
||||||
<div class="flex_center_between">
|
<div class="flex_center_between">
|
||||||
<a-space>
|
<a-space>
|
||||||
<a-input-search
|
<a-input-search
|
||||||
|
v-model="queryParams.keyword"
|
||||||
:style="{ width: '320px' }"
|
:style="{ width: '320px' }"
|
||||||
:placeholder="t('please.enter', { placeholder: t('content') })"
|
:placeholder="t('please.enter', { placeholder: t('content') })"
|
||||||
|
@search="handleSearch"
|
||||||
/>
|
/>
|
||||||
<a-button>
|
<a-button>
|
||||||
<template #icon>
|
<template #icon>
|
||||||
|
|
@ -23,7 +25,7 @@
|
||||||
</template>
|
</template>
|
||||||
{{ t('table.export') }}
|
{{ t('table.export') }}
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-button type="primary">
|
<a-button type="primary" @click="handleAddProject">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<icon-plus />
|
<icon-plus />
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -44,23 +46,37 @@
|
||||||
@page-change="onPageChange"
|
@page-change="onPageChange"
|
||||||
@page-size-change="onPageSizeChange"
|
@page-size-change="onPageSizeChange"
|
||||||
>
|
>
|
||||||
<!-- <template #type="{ record }">
|
<template #type="{ record }">
|
||||||
<a-tag checkable :color="getTypeColor(record.type)">{{
|
<a-tag checkable :color="getProjectTypeColor(record.type)">
|
||||||
getTypeText(record.type)
|
{{ record.typeName }}
|
||||||
}}</a-tag>
|
</a-tag>
|
||||||
</template>
|
</template>
|
||||||
<template #rate="{ record }">
|
<template #rate="{ record }">
|
||||||
<a-progress :percent="record.rate" color="green" :style="{ width: '100px' }" />
|
<a-progress
|
||||||
</template> -->
|
:percent="Number(record.rentRate)"
|
||||||
<template #operate>
|
color="green"
|
||||||
|
:style="{ width: '100px' }"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #operate="{ record }">
|
||||||
<a-space>
|
<a-space>
|
||||||
<a-button>查看</a-button>
|
<a-button>查看</a-button>
|
||||||
<a-button type="primary">编辑</a-button>
|
<a-button
|
||||||
|
:loading="isLoadingEdit"
|
||||||
|
type="primary"
|
||||||
|
@click="handleEditProject(record.id)"
|
||||||
|
>编辑</a-button
|
||||||
|
>
|
||||||
<a-button>更多</a-button>
|
<a-button>更多</a-button>
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
</DataTable>
|
</DataTable>
|
||||||
</a-card>
|
</a-card>
|
||||||
|
<projectFormDrawer
|
||||||
|
v-model:visible="showDrawer"
|
||||||
|
:is-edit="isEdit"
|
||||||
|
:record="currentProjectInfo"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -70,14 +86,18 @@
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import type { TableColumnData } from '@arco-design/web-vue';
|
import type { TableColumnData } from '@arco-design/web-vue';
|
||||||
import useDataTable from '@/composables/useDataTable';
|
import useDataTable from '@/composables/useDataTable';
|
||||||
import { ProjectItem } from '@/types/business';
|
import { ProjectItem, ProjectInfo } from '@/types/business';
|
||||||
import { getProjectList } from '@/api/business';
|
import { getProjectList, getProjectDetail } from '@/api/business';
|
||||||
|
import { ProjectPaginationParams, ProjectType } from '@/types/comm';
|
||||||
|
import projectFormDrawer from './drawer/project-form-drawer.vue';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { loading } = useLoading(false);
|
const { loading } = useLoading(false);
|
||||||
|
const showDrawer = ref<boolean>(false);
|
||||||
|
const isLoadingEdit = ref<boolean>(false);
|
||||||
|
const isEdit = ref<boolean>(false);
|
||||||
|
const currentProjectInfo = ref<ProjectInfo | null>(null);
|
||||||
const projectList = ref<ProjectItem[]>([]);
|
const projectList = ref<ProjectItem[]>([]);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: renderData,
|
data: renderData,
|
||||||
columns: projectColumn,
|
columns: projectColumn,
|
||||||
|
|
@ -100,28 +120,46 @@
|
||||||
const columns: TableColumnData[] = [
|
const columns: TableColumnData[] = [
|
||||||
{ title: '项目信息', dataIndex: 'name', key: 'name' },
|
{ title: '项目信息', dataIndex: 'name', key: 'name' },
|
||||||
{ title: '项目类型', dataIndex: 'type', key: 'type', slotName: 'type' },
|
{ title: '项目类型', dataIndex: 'type', key: 'type', slotName: 'type' },
|
||||||
{ title: '管理面积', dataIndex: 'size', key: 'size' },
|
{ title: '管理面积', dataIndex: 'RentAreaStr', key: 'RentAreaStr' },
|
||||||
{ title: '可租面积', dataIndex: 'kezu', key: 'kezu' },
|
{ title: '可租面积', dataIndex: 'RoomAreaStr', key: 'RoomAreaStr' },
|
||||||
{ title: '出租率', dataIndex: 'rate', key: 'rate', slotName: 'rate' },
|
{
|
||||||
{ title: '房源数量', dataIndex: 'count', key: 'count' },
|
title: '出租率',
|
||||||
{ title: '创建时间', dataIndex: 'create_time', key: 'create_time' },
|
dataIndex: 'RentRateStr',
|
||||||
|
key: 'RentRateStr',
|
||||||
|
slotName: 'rate',
|
||||||
|
},
|
||||||
|
{ title: '房源数量', dataIndex: 'roomNum', key: 'roomNum' },
|
||||||
|
{ title: '创建时间', dataIndex: 'createdAt', key: 'createdAt' },
|
||||||
{ title: '操作', key: 'operate', slotName: 'operate', width: 200 },
|
{ title: '操作', key: 'operate', slotName: 'operate', width: 200 },
|
||||||
];
|
];
|
||||||
|
const queryParams = ref<ProjectPaginationParams>({
|
||||||
const getProjectListData = async () => {
|
|
||||||
const params = {
|
|
||||||
page: current.value,
|
page: current.value,
|
||||||
page_size: pageSize.value,
|
page_size: pageSize.value,
|
||||||
};
|
keyword: '',
|
||||||
|
type: null,
|
||||||
|
province_ids: null,
|
||||||
|
rent_rate_min: null,
|
||||||
|
rent_rate_max: null,
|
||||||
|
});
|
||||||
|
const getProjectListData = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const { code, data } = await getProjectList(params);
|
const { code, data } = await getProjectList(queryParams.value);
|
||||||
if (code === 200 && data) {
|
if (code === 200 && data) {
|
||||||
const { projectsList, page } = data;
|
const { projectsList, page } = data;
|
||||||
projectList.value = Array.isArray(projectsList) ? projectsList : [];
|
projectList.value = Array.isArray(projectsList) ? projectsList : [];
|
||||||
|
setData(projectList.value);
|
||||||
setTotal(page?.total ? page.total : 0);
|
setTotal(page?.total ? page.total : 0);
|
||||||
}
|
}
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
};
|
};
|
||||||
|
const getProjectDetailData = async (id: number) => {
|
||||||
|
isLoadingEdit.value = true;
|
||||||
|
const { code, data } = await getProjectDetail(id);
|
||||||
|
if (code === 200 && data) {
|
||||||
|
currentProjectInfo.value = data.project;
|
||||||
|
}
|
||||||
|
isLoadingEdit.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
// 分页事件处理
|
// 分页事件处理
|
||||||
const onPageChange = (page: number) => {
|
const onPageChange = (page: number) => {
|
||||||
|
|
@ -132,6 +170,23 @@
|
||||||
setPageSize(size);
|
setPageSize(size);
|
||||||
pageSize.value = size;
|
pageSize.value = size;
|
||||||
};
|
};
|
||||||
|
const handleAddProject = () => {
|
||||||
|
isEdit.value = false;
|
||||||
|
currentProjectInfo.value = null;
|
||||||
|
showDrawer.value = true;
|
||||||
|
};
|
||||||
|
const handleEditProject = async (id: number) => {
|
||||||
|
isEdit.value = true;
|
||||||
|
await getProjectDetailData(id);
|
||||||
|
showDrawer.value = true;
|
||||||
|
};
|
||||||
|
const handleSearch = () => {
|
||||||
|
getProjectListData();
|
||||||
|
};
|
||||||
|
const getProjectTypeColor = (type: ProjectType) => {
|
||||||
|
const typeMap = { 1: 'green', 2: 'red', 3: 'orange' };
|
||||||
|
return typeMap[type];
|
||||||
|
};
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
setColumns(columns);
|
setColumns(columns);
|
||||||
getProjectListData();
|
getProjectListData();
|
||||||
|
|
|
||||||
|
|
@ -109,6 +109,7 @@
|
||||||
// import { CreateCodeSettingParam } from '@/types/setting'
|
// import { CreateCodeSettingParam } from '@/types/setting'
|
||||||
import draggable from 'vuedraggable';
|
import draggable from 'vuedraggable';
|
||||||
|
|
||||||
|
const isLoading = ref<boolean>(false);
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
isEdit: boolean;
|
isEdit: boolean;
|
||||||
|
|
@ -240,7 +241,9 @@
|
||||||
form.arrange = JSON.parse(JSON.stringify(arrangeList.value));
|
form.arrange = JSON.parse(JSON.stringify(arrangeList.value));
|
||||||
};
|
};
|
||||||
const handleOk = async () => {
|
const handleOk = async () => {
|
||||||
|
if (isLoading.value) return;
|
||||||
try {
|
try {
|
||||||
|
isLoading.value = true;
|
||||||
const valid = await formRef.value.validate();
|
const valid = await formRef.value.validate();
|
||||||
if (valid) return;
|
if (valid) return;
|
||||||
// 同步编号排序
|
// 同步编号排序
|
||||||
|
|
@ -265,6 +268,8 @@
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// 捕获异常,暂不处理
|
// 捕获异常,暂不处理
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,7 @@
|
||||||
(e: 'update:visible', val: boolean): void;
|
(e: 'update:visible', val: boolean): void;
|
||||||
(e: 'submitted'): void;
|
(e: 'submitted'): void;
|
||||||
}>();
|
}>();
|
||||||
|
const isLoading = ref<boolean>(false);
|
||||||
// 表单数据
|
// 表单数据
|
||||||
const formRef = ref<FormInstance>();
|
const formRef = ref<FormInstance>();
|
||||||
const defaultForm: CreateTermSettingParam = {
|
const defaultForm: CreateTermSettingParam = {
|
||||||
|
|
@ -182,7 +182,9 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOk = async () => {
|
const handleOk = async () => {
|
||||||
|
if (isLoading.value) return;
|
||||||
try {
|
try {
|
||||||
|
isLoading.value = true;
|
||||||
const valid = await formRef.value.validate();
|
const valid = await formRef.value.validate();
|
||||||
if (valid) return;
|
if (valid) return;
|
||||||
const { is_bond, is_fee, finance_bond_fee_id, finance_fee_id } = form;
|
const { is_bond, is_fee, finance_bond_fee_id, finance_fee_id } = form;
|
||||||
|
|
@ -208,6 +210,8 @@
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// 捕获异常,暂不处理
|
// 捕获异常,暂不处理
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,7 @@
|
||||||
import { CreateFeeParams } from '@/types/setting';
|
import { CreateFeeParams } from '@/types/setting';
|
||||||
import { feeTypeList } from '@/utils/constant';
|
import { feeTypeList } from '@/utils/constant';
|
||||||
|
|
||||||
|
const isLoading = ref<boolean>(false);
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
isEdit: boolean;
|
isEdit: boolean;
|
||||||
|
|
@ -132,7 +133,9 @@
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
);
|
);
|
||||||
const handleOk = async () => {
|
const handleOk = async () => {
|
||||||
|
if (isLoading.value) return;
|
||||||
try {
|
try {
|
||||||
|
isLoading.value = true;
|
||||||
const valid = await formRef.value.validate();
|
const valid = await formRef.value.validate();
|
||||||
if (valid) return;
|
if (valid) return;
|
||||||
const res = props?.isEdit ? await updateFee(form) : await createFee(form);
|
const res = props?.isEdit ? await updateFee(form) : await createFee(form);
|
||||||
|
|
@ -143,6 +146,8 @@
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// 捕获异常,暂不处理
|
// 捕获异常,暂不处理
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@
|
||||||
import { updateUserRole } from '@/api/setting';
|
import { updateUserRole } from '@/api/setting';
|
||||||
import { UpdateUserRoleParam, RoleItem } from '@/types/setting';
|
import { UpdateUserRoleParam, RoleItem } from '@/types/setting';
|
||||||
|
|
||||||
|
const isLoading = ref<boolean>(false);
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
roleId: number | null;
|
roleId: number | null;
|
||||||
|
|
@ -76,7 +77,9 @@
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
);
|
);
|
||||||
const handleOk = async () => {
|
const handleOk = async () => {
|
||||||
|
if (isLoading.value) return;
|
||||||
try {
|
try {
|
||||||
|
isLoading.value = true;
|
||||||
const valid = await formRef.value.validate();
|
const valid = await formRef.value.validate();
|
||||||
if (valid) return;
|
if (valid) return;
|
||||||
const params = { ...form };
|
const params = { ...form };
|
||||||
|
|
@ -88,6 +91,8 @@
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// 捕获异常,暂不处理
|
// 捕获异常,暂不处理
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@
|
||||||
import { updateRole, createRole } from '@/api/setting';
|
import { updateRole, createRole } from '@/api/setting';
|
||||||
import { RoleFormParam, RoleItem } from '@/types/setting';
|
import { RoleFormParam, RoleItem } from '@/types/setting';
|
||||||
|
|
||||||
|
const isLoading = ref<boolean>(false);
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
isEdit: boolean;
|
isEdit: boolean;
|
||||||
|
|
@ -92,7 +93,9 @@
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
);
|
);
|
||||||
const handleOk = async () => {
|
const handleOk = async () => {
|
||||||
|
if (isLoading.value) return;
|
||||||
try {
|
try {
|
||||||
|
isLoading.value = true;
|
||||||
const valid = await formRef.value.validate();
|
const valid = await formRef.value.validate();
|
||||||
if (valid) return;
|
if (valid) return;
|
||||||
const params = { ...form };
|
const params = { ...form };
|
||||||
|
|
@ -106,6 +109,8 @@
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// 捕获异常,暂不处理
|
// 捕获异常,暂不处理
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,7 @@
|
||||||
import { createUser, updateUser } from '@/api/setting';
|
import { createUser, updateUser } from '@/api/setting';
|
||||||
import { DepartmentItem, RoleItem, FormUserItem } from '@/types/setting';
|
import { DepartmentItem, RoleItem, FormUserItem } from '@/types/setting';
|
||||||
|
|
||||||
|
const isLoading = ref<boolean>(false);
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
isEdit: boolean;
|
isEdit: boolean;
|
||||||
|
|
@ -242,7 +243,9 @@
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
);
|
);
|
||||||
const handleOk = async () => {
|
const handleOk = async () => {
|
||||||
|
if (isLoading.value) return;
|
||||||
try {
|
try {
|
||||||
|
isLoading.value = true;
|
||||||
const valid = await formRef.value.validate();
|
const valid = await formRef.value.validate();
|
||||||
if (valid) return;
|
if (valid) return;
|
||||||
const params = { ...form };
|
const params = { ...form };
|
||||||
|
|
@ -255,6 +258,8 @@
|
||||||
emit('update:visible', false);
|
emit('update:visible', false);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// 处理错误
|
// 处理错误
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue