From 52102d2e72f4adb8e198ca0f54d9bd7d2ff797bb Mon Sep 17 00:00:00 2001 From: Zzz1zz Date: Wed, 6 Aug 2025 23:26:38 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=A1=B9=E7=9B=AE=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E5=88=97=E8=A1=A8=E6=90=9C=E7=B4=A2=E3=80=81=E6=96=B0=E5=A2=9E?= =?UTF-8?q?/=E7=BC=96=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .eslintrc.js | 1 + src/api/business.ts | 4 +- src/api/common.ts | 74 ++++ src/components/common/ImageUpload.md | 115 ++++++ src/components/common/ImageUpload.vue | 165 +++++++++ src/components/index.ts | 2 + src/types/business.ts | 15 +- src/types/comm.ts | 64 ++++ src/utils/index.ts | 10 + .../project/drawer/project-form-drawer.vue | 327 ++++++++++++++++-- src/views/business/project/index.vue | 103 ++++-- .../drawer/contract-number-form-drawer.vue | 5 + .../drawer/contract-terms-form-drawer.vue | 6 +- .../drawer/fee-management-drawer.vue | 5 + .../user/components/change-role-drawer.vue | 5 + .../user/components/role-form-drawer.vue | 5 + .../user/components/user-form-drawer.vue | 5 + 17 files changed, 843 insertions(+), 68 deletions(-) create mode 100644 src/api/common.ts create mode 100644 src/components/common/ImageUpload.md create mode 100644 src/components/common/ImageUpload.vue diff --git a/.eslintrc.js b/.eslintrc.js index a986ff1..689122b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -65,6 +65,7 @@ module.exports = { }, ], 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0, + 'no-plusplus': ['error', { allowForLoopAfterthoughts: true }], 'no-param-reassign': 0, 'prefer-regex-literals': 0, 'import/no-extraneous-dependencies': 0, diff --git a/src/api/business.ts b/src/api/business.ts index 9fb62f5..0e10439 100644 --- a/src/api/business.ts +++ b/src/api/business.ts @@ -5,7 +5,7 @@ import { CreateProjectParam, ProjectDetailRes, } from '@/types/business'; -import { PaginationParams } from '@/types/comm'; +import { ProjectPaginationParams } from '@/types/comm'; import { HttpResponse } from './interceptor'; // import type { RouteRecordNormalized } from 'vue-router'; // import { UserState } from '@/store/modules/user/types'; @@ -22,7 +22,7 @@ export function getAllProjectList(): Promise { } // 项目列表(分页) export function getProjectList( - params: PaginationParams + params: ProjectPaginationParams ): Promise { return axios.post('project/getProjectList', params); } diff --git a/src/api/common.ts b/src/api/common.ts new file mode 100644 index 0000000..8f04756 --- /dev/null +++ b/src/api/common.ts @@ -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 { + return axios.get(`common/getProvinceList`); +} +// 城市列表 +export function getCityListByProvinceId(id: number): Promise { + return axios.get(`common/getCityList?province_id=${id}`); +} +// 区域列表 +export function getDistrictListByCityId( + id: number +): Promise { + return axios.get(`common/getDistrictList?city_id=${id}`); +} + +// 全部项目(id,名称)列表 +export function getAllProjectIdNameList(): Promise { + return axios.get(`common/getAllProjectList`); +} +// 全部楼栋(id,名称)列表(根据项目id查询) +export function getAllBuildingIdNameList( + id: number +): Promise { + return axios.get(`common/getAllBuildingList?project_id=${id}`); +} +// 全部楼层(id,名称)列表(根据楼栋id查询) +export function getAllFloorIdNameList( + id: number +): Promise { + return axios.get(`common/getAllFloorList?building_id=${id}`); +} +// 发票类型列表 +export function getInvoiceTypeList(): Promise { + return axios.get(`common/getInvoiceTypeList`); +} +// 租客归档类型列表 +export function getFilingTypeList(): Promise { + return axios.get(`common/getFilingTypeList`); +} +// 上传文档 +export function ossUpload( + params: UploadParam | FormData +): Promise { + return axios.post('common/ossUpload', params); +} +// 下载文档 +export function ossDownload(key: string): Promise { + return axios.get(`common/ossDownload?key=${key}`); +} +// 银行列表 +export function getBanksList(keyword: string): Promise { + return axios.post('common/getBanksList', { keyword }); +} diff --git a/src/components/common/ImageUpload.md b/src/components/common/ImageUpload.md new file mode 100644 index 0000000..36f2aea --- /dev/null +++ b/src/components/common/ImageUpload.md @@ -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 + + + +``` + +### 自定义文件大小限制 + +```vue + +``` + +### 在表单中使用 + +```vue + +``` + +## 注意事项 + +1. 组件会自动处理文件验证,包括文件大小和格式检查 +2. 上传成功后,文件对象会自动更新URL和状态 +3. 支持v-model双向绑定,父组件可以直接操作文件列表 +4. 上传类型必须从父组件传入,用于区分不同的上传场景 \ No newline at end of file diff --git a/src/components/common/ImageUpload.vue b/src/components/common/ImageUpload.vue new file mode 100644 index 0000000..cbc5fac --- /dev/null +++ b/src/components/common/ImageUpload.vue @@ -0,0 +1,165 @@ + + + + + diff --git a/src/components/index.ts b/src/components/index.ts index 30c0459..797268f 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -12,6 +12,7 @@ import { import Chart from './chart/index.vue'; import Breadcrumb from './breadcrumb/index.vue'; import DataTable from './common/DataTable.vue'; +import ImageUpload from './common/ImageUpload.vue'; // Manually introduce ECharts modules to reduce packing size @@ -33,5 +34,6 @@ export default { Vue.component('Chart', Chart); Vue.component('Breadcrumb', Breadcrumb); Vue.component('DataTable', DataTable); + Vue.component('ImageUpload', ImageUpload); }, }; diff --git a/src/types/business.ts b/src/types/business.ts index ee02374..e26d0e9 100644 --- a/src/types/business.ts +++ b/src/types/business.ts @@ -9,10 +9,11 @@ import { } from './comm'; export interface BuildingItem { + customId?: string; id?: string; // 楼栋id name: string; // 楼栋名称 - up_floor?: number; // 地上层数 - down_floor?: number; // 地下层数 + up_floor?: number | null; // 地上层数 + down_floor?: number | null; // 地下层数 } export interface AllProjectsItem { id: number; // 项目id @@ -57,9 +58,9 @@ export interface CreateProjectParam { id?: number; type: ProjectType; // 项目类型:1写字楼,2商业综合体,3园区 name: string; // 楼栋名称 - province_id: number; // 省份id - city_id: number; // 城市id - district_id: number; // 区域id + province_id: number | null; // 省份id + city_id: number | null; // 城市id + district_id: number | null; // 区域id address: string; // 详细地址 images: string[]; // 图片数组 buildings: BuildingItem[]; // 楼栋数据 @@ -93,4 +94,6 @@ export interface ProjectInfo extends BaseEntity { buildings: BuildingInfo[]; // 楼栋数组 } -export type ProjectDetailRes = ProjectInfo; +export type ProjectDetailRes = HttpResponse<{ + project: ProjectInfo; +}>; diff --git a/src/types/comm.ts b/src/types/comm.ts index 8e2e4de..0f80590 100644 --- a/src/types/comm.ts +++ b/src/types/comm.ts @@ -1,3 +1,4 @@ +import { HttpResponse } from '@/api/interceptor'; // ==================== 通用类型定义 ==================== export interface BaseEntity { id: number; @@ -30,3 +31,66 @@ export type CommStatus = 1 | 2; // 1:是 2否 export type FeeManagementType = 1 | 2; // 费项属性:1收入费项,2支出费项 export type FeeType = 1 | 2 | 3 | 4; // 费项类型:1租赁类,2物业类,3特殊类,4其他类 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[]; +}>; diff --git a/src/utils/index.ts b/src/utils/index.ts index cdfe720..5027482 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -34,5 +34,15 @@ export function debounce any>( }, 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; diff --git a/src/views/business/project/drawer/project-form-drawer.vue b/src/views/business/project/drawer/project-form-drawer.vue index 3dceef6..0cc9ef1 100644 --- a/src/views/business/project/drawer/project-form-drawer.vue +++ b/src/views/business/project/drawer/project-form-drawer.vue @@ -14,10 +14,10 @@ auto-label-width layout="vertical" > - + - + - + - - + + + + + + {{ item.name }} + + + + + + + + + {{ item.name }} + + + + + + + {{ item.name }} + + + + + + + + + + + + + + + + + + @@ -58,10 +173,25 @@