diff --git a/src/api/common.ts b/src/api/common.ts index 8f04756..05332bb 100644 --- a/src/api/common.ts +++ b/src/api/common.ts @@ -72,3 +72,7 @@ export function ossDownload(key: string): Promise { export function getBanksList(keyword: string): Promise { return axios.post('common/getBanksList', { keyword }); } +// 获取勾选部门的项目列表 +export function getDeptProjectListFromDeptId(id: number): Promise { + return axios.get(`common/getDeptProjectList?departmentId=${id}`); +} diff --git a/src/api/setting.ts b/src/api/setting.ts index 59407a4..23069e5 100644 --- a/src/api/setting.ts +++ b/src/api/setting.ts @@ -35,6 +35,10 @@ import { CreateTermSettingParam, SettingContractTermListRes, SettingContractTermInfo, + SettingContractTemplateListRes, + CreateTemplateSettingParam, + ContractWordFormParam, + ContractWordSettingListRes, } from '@/types/setting'; import { PaginationParams, TenantDictionaryType } from '@/types/comm'; import { HttpResponse } from './interceptor'; @@ -71,7 +75,14 @@ export function departmentSetDown(id: number): Promise { export function delDepartment(id: number): Promise { return axios.get(`/department/delDepartment?id=${id}`); } - +// 保存部门项目数据 +export function saveDataPermission(params: any): Promise { + return axios.post('/department/saveDataPermission', params); +} +// 获取部门项目数据 +export function getDataPermission(id: number): Promise { + return axios.get(`/department/getDataPermission?id=${id}`); +} // ==================== 用户相关 API ==================== export function getAllUserList( params: PaginationParams @@ -405,3 +416,81 @@ export function getUseFeeList( ): Promise { return axios.post(`financeSetting/getUseFeeList`, params); } + +// 合同设置-合同模板管理-全部启用列表 +export function getAllTemplateSettingList(): Promise { + return axios.get(`contractSetting/getAllTemplateSettingList`); +} +// 合同设置-合同模板管理-新增 +export function createTemplateSetting( + params: CreateTemplateSettingParam +): Promise { + return axios.post(`contractSetting/createTemplateSetting`, params); +} +// 合同设置-合同模板管理-编辑 +export function updateTemplateSetting( + params: CreateTemplateSettingParam +): Promise { + return axios.post(`contractSetting/updateTemplateSetting`, params); +} +// 合同设置-合同模板管理-删除 +export function delTemplateSetting(id: number): Promise { + return axios.get(`contractSetting/delTemplateSetting?id=${id}`); +} +// 合同设置-合同模板管理-单个详情 +export function getTemplateSettingFromId( + id: number +): Promise { + return axios.get(`contractSetting/getTemplateSetting?id=${id}`); +} +// 合同设置-合同模板管理-启用/禁用 +export function disableTemplateSetting( + params: DisableUserParam +): Promise { + return axios.post(`contractSetting/disableTemplateSetting`, params); +} +// 合同设置-合同模板管理-列表(带分页) +export function getTemplateSettingList( + params: PaginationParams +): Promise { + return axios.post(`contractSetting/getTemplateSettingList`, params); +} + +// 合同设置-合同文档模板管理-全部启用列表 +export function getAllWordSettingList(): Promise { + return axios.get(`contractSetting/getAllWordSettingList`); +} +// 合同设置-合同文档模板管理-新增 +export function createWordSetting( + params: ContractWordFormParam +): Promise { + return axios.post(`contractSetting/createWordSetting`, params); +} +// 合同设置-合同文档模板管理-编辑(更新) +export function updateWordSetting( + params: ContractWordFormParam +): Promise { + return axios.post(`contractSetting/updateWordSetting`, params); +} +// 合同设置-合同文档模板管理-删除 +export function delWordSetting(id: number): Promise { + return axios.get(`contractSetting/delWordSetting?id=${id}`); +} +// 合同设置-合同文档模板管理-单个详情 +export function getWordSettingFromId( + id: number +): Promise { + return axios.get(`contractSetting/getWordSetting?id=${id}`); +} +// 合同设置-合同文档模板管理-启用/禁用 +export function disableWordSetting( + params: DisableUserParam +): Promise { + return axios.post(`contractSetting/disableWordSetting`, params); +} +// 合同设置-合同文档模板管理-列表(带分页) +export function getWordSettingList( + params: PaginationParams +): Promise { + return axios.post(`contractSetting/getWordSettingList`, params); +} diff --git a/src/components/common/FileUpload.vue b/src/components/common/FileUpload.vue new file mode 100644 index 0000000..ee123e4 --- /dev/null +++ b/src/components/common/FileUpload.vue @@ -0,0 +1,165 @@ + + + diff --git a/src/composables/useDepartmentList.ts b/src/composables/useDepartmentList.ts index 153d1e1..7b2e575 100644 --- a/src/composables/useDepartmentList.ts +++ b/src/composables/useDepartmentList.ts @@ -45,13 +45,11 @@ export default function useDepartmentList(autoFetch = true) { } }; - const getDepartmentDetailData = async ( - id: number - ): Promise => { + const getDepartmentDetailData = async (id: number): Promise => { try { const { code, data } = await getDepartmentDetail(id); - if (code === 200 && data?.department) { - return data.department; + if (code === 200 && data) { + return data; } return null; } catch (error) { diff --git a/src/types/business.ts b/src/types/business.ts index 7575088..b5b2c13 100644 --- a/src/types/business.ts +++ b/src/types/business.ts @@ -5,7 +5,6 @@ import { BaseEntity, DeleteStatus, UserStatus, - ProjectType, } from './comm'; export interface BuildingItem { @@ -73,7 +72,7 @@ export type ProjectListRes = HttpResponse<{ }>; export interface CreateProjectParam { id?: number | null; - type: ProjectType; // 项目类型:1写字楼,2商业综合体,3园区 + type: number | null; // 项目类型 name: string; // 楼栋名称 province_id: number | null; // 省份id city_id: number | null; // 城市id @@ -95,7 +94,7 @@ export interface BuildingInfo extends BaseEntity { export interface ProjectInfo extends BaseEntity { systemTenantId: 17; // 租户id name: string; // 项目名称 - type: ProjectType; // 类型:1写字楼,2商业综合体,3园区 + type: number; // 类型:1写字楼,2商业综合体,3园区 provinceId: number; // 省份id cityId: number; // 城市id districtId: number; // 区id diff --git a/src/types/comm.ts b/src/types/comm.ts index f526c87..0febee1 100644 --- a/src/types/comm.ts +++ b/src/types/comm.ts @@ -30,7 +30,6 @@ export type cancelCodeRecycling = 0 | 1; // 作废合同编号回收:0不回 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图片 // ==================== 公共接口类型定义 ==================== @@ -66,7 +65,7 @@ export interface AllIdNameItem { name: string; } export interface ProjectPaginationParams extends PaginationParams { - type?: ProjectType[] | null; + type?: number[] | null; province_ids?: number[] | null; rent_rate_min?: number | null; rent_rate_max?: number | null; diff --git a/src/types/setting.ts b/src/types/setting.ts index 7603dd8..4f07b28 100644 --- a/src/types/setting.ts +++ b/src/types/setting.ts @@ -49,6 +49,8 @@ export interface DepartmentItem extends BaseEntity { export interface DepartmentDetailData { department: DepartmentItem; + projects?: any; + users?: any; } // ==================== 用户相关类型 ==================== @@ -284,6 +286,65 @@ export interface FeeSelectListParams { fee_type: FeeType; // 费项类型:1租赁类,2物业类,3特殊类,4其他类 is_bond?: CommStatus; // 是否保证金:1是,2否 } +export interface ContractTemplateSettingItem extends BaseEntity { + systemTenantId: number; + departmentId: number; + contractCodeSettingId: number; + name: string; + type: number; + remark: string; + createUserId: number; + status: string; + isDelete?: DeleteStatus; // 是否选择:1是,0否 + statusStr: string; +} + +export interface CreateTemplateSettingParam { + id?: number; + name: string; + departmentId: number | null; +} +export interface ContractWordItem extends BaseEntity { + systemTenantId: number; // 系统租户id + name: string; // 名称 + departmentId: number; // 部门id + isAllProjects: number; // 是否所有项目:1是,2否 + url: string; // 模版url + createUserId: number; + status: number; + isDelete: number; // 是否删除:0否,1是 + statusStr: string; + projectName: string; + departmentName: string; +} +export interface ContractWordFormParam { + name: string; // 名称 + departmentId: number; // 适用范围 + url: string; // 模板url + projects: number[]; // 适用项目 + id?: number; +} +export interface CreateFileSettingProjectItem { + id: number; // 项目id + name: string; // 名称 + contractWordSettingId: number; +} +export interface CreateFileSettingParam { + name: string; + departmentId: number | null; + url: string | null; + projects: CreateFileSettingProjectItem[] | []; // 项目ids数组(若为空数组则为全部项目) + systemTenantId?: number; + isAllProjects?: number; // 是否所有项目:1是,2否 + createUserId?: number; + status?: number; + isDelete?: number; // 是否删除:0否,1是 + statusStr?: string; + departmentName?: string; + createdAt?: string; + updatedAt?: string; + id?: number | null; +} // ==================== 响应类型别名 ==================== export type DepartmentFormRes = HttpResponse; export type DepartmentList = DepartmentItem[]; @@ -338,3 +399,17 @@ export type SettingContractTermListRes = HttpResponse<{ export type SettingContractTermInfo = HttpResponse<{ contractTermsSetting: TermSettingItem; }>; +export type SettingContractTemplateListRes = HttpResponse<{ + contractTemplateSetting: ContractTemplateSettingItem[]; + page?: PaginationParams & PaginationResult; +}>; +export type CreateTemplateSettingRes = HttpResponse<{ + contractTemplateSettingId: number; +}>; +export type contractWordSettingRes = HttpResponse<{ + contractTermSettingId: number; +}>; +export type ContractWordSettingListRes = HttpResponse<{ + contractWordSetting: ContractWordItem[]; + page?: PaginationParams & PaginationResult; +}>; diff --git a/src/views/business/project/drawer/project-form-drawer.vue b/src/views/business/project/drawer/project-form-drawer.vue index 2badfc9..53dfbeb 100644 --- a/src/views/business/project/drawer/project-form-drawer.vue +++ b/src/views/business/project/drawer/project-form-drawer.vue @@ -2,7 +2,7 @@ - {{ item.label }} + {{ item.name }} @@ -175,12 +175,10 @@ import { ref, watch, reactive, onMounted } from 'vue'; import type { FormInstance } from '@arco-design/web-vue'; import { Message } from '@arco-design/web-vue'; - import { projectTypeList } from '@/utils/constant'; import { CommonProvinceItem, CommonCityItem, CommonDistrictItem, - ProjectType, } from '@/types/comm'; import { getProvinceList, @@ -189,7 +187,9 @@ } from '@/api/common'; import { createProject, updateProject } from '@/api/business'; import { CreateProjectParam } from '@/types/business'; + import { projectTypeInfo } from '@/types/setting'; import ImageUpload from '@/components/common/ImageUpload.vue'; + import { getAllProjectTypeList } from '@/api/setting'; import draggable from 'vuedraggable'; import { generateRandomId } from '@/utils'; @@ -210,13 +210,14 @@ down_floor: null, }; }; + const projectTypeList = ref([]); const isLoading = ref(false); // 表单数据 const formRef = ref(); const defaultForm: CreateProjectParam = { id: null, - type: 1 as ProjectType, // 项目类型:1写字楼,2商业综合体,3园区 + type: null, name: '', // 楼栋名称 province_id: null, // 省份id city_id: null, // 城市id @@ -283,7 +284,12 @@ form.district_id = null; await getDistrictListByCityIdData(id); }; - + const getAllProjectTypeListData = async () => { + const { code, data } = await getAllProjectTypeList(); + if (code === 200 && data) { + projectTypeList.value = data?.projectTypesList; + } + }; const handleAddBuilding = () => { form.buildings.push(createEmptyBuilding()); }; @@ -298,8 +304,6 @@ () => props.visible, async (val) => { if (val) { - console.log(props.record, 'props.record'); - if (props.isEdit && props.record) { const record = props.record; if (record?.buildings?.length) { @@ -369,9 +373,12 @@ const handleCancel = () => { emit('update:visible', false); }; - const handleDragEnd = () => {}; + const handleDragEnd = () => { + console.log('handleDragEnd'); + }; onMounted(() => { getProvinceListData(); + getAllProjectTypeListData(); }); diff --git a/src/views/business/project/index.vue b/src/views/business/project/index.vue index 55c9e84..82d1e9f 100644 --- a/src/views/business/project/index.vue +++ b/src/views/business/project/index.vue @@ -47,7 +47,7 @@ @page-size-change="onPageSizeChange" > @@ -60,15 +60,8 @@ - - + + {{ departmentDetailInfo.name }} @@ -83,6 +78,39 @@ {{ departmentDetailInfo.remark }} + + +
    +
  • + {{ item.name }} +
      +
    • + {{ subItem.name }} +
    • +
    +
  • +
+
([]); const { t } = useI18n(); - + const projectIdCheck = ref([]); // 已勾选的项目 + const projectBuildingIdCheck = ref([]); // 已勾选的项目楼栋 watch( allDepartmentKeys, (val) => { @@ -133,7 +164,7 @@ const showModal = ref(false); const isEdit = ref(false); const departmentDetailInfo = ref(null); - + const allProjetList = ref(null); interface TreeNode { id: string | number; isShowEditIcon?: boolean; @@ -150,22 +181,137 @@ }); }; + /** + * 通用切换器 + * @param type "project" | "building" + * @param project 当前项目对象 { id, buildings } + * @param targetId 被点击的 id(项目 id 或楼栋 id) + */ + const toggleCheck = ( + type: 'project' | 'building', + project: any, + targetId: number + ) => { + const projectId = project.id; + const buildingIds = + project.buildings?.map((b: any) => b.id).filter(Boolean) || []; + + if (type === 'project') { + if (projectIdCheck.value.includes(projectId)) { + // 取消项目 → 去掉该项目和所有楼栋 + projectIdCheck.value = projectIdCheck.value.filter( + (id) => id !== projectId + ); + projectBuildingIdCheck.value = projectBuildingIdCheck.value.filter( + (id) => !buildingIds.includes(id) + ); + } else { + // 勾选项目 → 加上项目和所有楼栋 + projectIdCheck.value.push(projectId); + projectBuildingIdCheck.value.push( + ...buildingIds.filter( + (id) => !projectBuildingIdCheck.value.includes(id) + ) + ); + } + } + + if (type === 'building') { + if (projectBuildingIdCheck.value.includes(targetId)) { + // 取消楼栋 + projectBuildingIdCheck.value = projectBuildingIdCheck.value.filter( + (id) => id !== targetId + ); + + // 如果该项目没有任何楼栋被选中 → 去掉项目 + const stillChecked = buildingIds.some((id) => + projectBuildingIdCheck.value.includes(id) + ); + if (!stillChecked) { + projectIdCheck.value = projectIdCheck.value.filter( + (id) => id !== projectId + ); + } + } else { + // 勾选楼栋 + projectBuildingIdCheck.value.push(targetId); + + // 确保项目也勾选 + if (!projectIdCheck.value.includes(projectId)) { + projectIdCheck.value.push(projectId); + } + } + } + }; + // 勾选/取消项目 + const onCheckChangeProject = (_: any, project: any) => { + toggleCheck('project', project, project.id); + }; + + // 勾选/取消楼栋 + const handleChooseBuildingId = (project: any, buildingId: number) => { + toggleCheck('building', project, buildingId); + }; + + // 生成保存所需的 projects + const getSaveProjects = () => { + const result: { project_id: number; building_id: number }[] = []; + // 遍历所有勾选的项目 + projectIdCheck.value.forEach((pid: number) => { + const project = allProjetList.value?.find((p: any) => p.id === pid); + if (!project) return; + + const buildingIds = + project.buildings?.map((b: any) => b.id).filter(Boolean) || []; + + // 过滤出该项目下被选中的楼栋 + const selectedBuildings = buildingIds.filter((bid) => + projectBuildingIdCheck.value.includes(bid) + ); + + // 没有楼栋时,不生成数据(保证数据干净) + selectedBuildings.forEach((bid) => { + result.push({ + project_id: project.id, + building_id: bid, + }); + }); + }); + + return result; + }; + const currentNode = ref(null); const onSelect = debounce( async (_: (string | number)[], event: { node: TreeNode }) => { const { node } = event; - if (node.id === currentNode.value?.id) return; - currentNode.value = node; - // 递归重置所有节点的 isShowEditIcon - resetEditIcon(departmentListData.value); - // 只设置当前节点 - if (currentNode.value.level !== 1) { - node.isShowEditIcon = true; + if (node.id !== currentNode.value?.id) { + currentNode.value = node; + // 递归重置所有节点的 isShowEditIcon + resetEditIcon(departmentListData.value); + // 只设置当前节点 + if (currentNode.value.level !== 1) { + node.isShowEditIcon = true; + } + const data = await getDepartmentDetailData( + Number(currentNode.value.id) + ); + departmentDetailInfo.value = data.department; + if (Array.isArray(data?.projects)) { + // 楼栋勾选 + projectBuildingIdCheck.value = data.projects.map( + (item) => item.building_id + ); + // 项目勾选(去重) + projectIdCheck.value = [ + ...new Set(data.projects.map((item) => item.project_id)), + ]; + } else { + projectIdCheck.value = []; + projectBuildingIdCheck.value = []; + } } - departmentDetailInfo.value = await getDepartmentDetailData( - Number(currentNode.value.id) - ); }, 300 ); @@ -174,32 +320,36 @@ const allUserList = ref([]); const userKeyword = ref(''); - const getAllUserListData = async () => { - try { - const { code, data } = await getAllUserList({ - keyword: userKeyword.value, - }); - if (code === 200) { - const { user } = data; - allUserList.value = Array.isArray(user) ? user : []; - } - } catch (error) { - // 处理错误 + async function getAllUserListData() { + const { code, data } = await getAllUserList({ + keyword: userKeyword.value, + }); + if (code === 200) { + const { user } = data; + allUserList.value = Array.isArray(user) ? user : []; } - }; + } - const handleAdd = () => { + // 所有项目信息 + async function getAllProjectListData() { + const { code, data } = await getAllProjectList(); + if (code === 200) { + allProjetList.value = data?.projectsList || []; + } + } + + function handleAdd() { isEdit.value = false; departmentDetailInfo.value = null; showModal.value = true; - }; + } - const handleEdit = () => { + function handleEdit() { showModal.value = true; isEdit.value = true; - }; + } - const handleDelete = async (item: { id: string | number }) => { + async function handleDelete(item: { id: string | number }) { const { id } = item; try { const { code, message } = await delDepartment(Number(id)); @@ -210,9 +360,9 @@ } catch (error) { // 处理错误 } - }; + } - const handleMoveUp = async (item: { id: string | number }) => { + async function handleMoveUp(item: { id: string | number }) { const { id } = item; try { const { code, message } = await departmentSetUp(Number(id)); @@ -225,25 +375,34 @@ } catch (error) { // 处理错误 } - }; + } - const handleMoveDown = async (item: { id: string | number }) => { + async function handleMoveDown(item: { id: string | number }) { const { id } = item; - try { - const { code, message } = await departmentSetDown(Number(id)); - if (code === 200) { - Message.success(message); - getDepartmentListData(); - } else { - Message.info(message); - } - } catch (error) { - // 处理错误 + const { code, message } = await departmentSetDown(Number(id)); + if (code === 200) { + Message.success(message); + getDepartmentListData(); + } else { + Message.info(message); } + } + const handleSaveProject = async () => { + if (!currentNode?.value?.id) return Message.warning('请先选择部门'); + const saveProjects = getSaveProjects(); + if (saveProjects.length === 0) return Message.warning('请先选择项目'); + const params = { + id: currentNode.value?.id, + projects: saveProjects, + }; + const { code, message } = await saveDataPermission(params); + if (code === 200) Message.success(message); + return code; }; onMounted(() => { getAllUserListData(); + getAllProjectListData(); }); @@ -253,4 +412,32 @@ height: 100%; padding: 15px 0; } + + .flex { + display: flex; + align-items: center; + margin-top: 10px; + + .building_item { + background-color: #efefef; + color: rgba(154, 154, 154, 1); + width: 100px; + height: 28px; + line-height: 28px; + border: 1px solid #efefef; + margin-right: 16px; + text-align: center; + cursor: pointer; + + &:last-child { + margin-right: 0; + } + + &.choose { + background-color: #eaf3fe; + color: rgba(22, 93, 255, 1); + border-color: rgba(22, 93, 255, 1); + } + } + }