mirror of
http://183.237.82.114:10086/kilo/leaser_front.git
synced 2026-10-23 18:05:07 +00:00
feat: 设置->合同设置->合同文档模板设置
This commit is contained in:
parent
ddb35b10f1
commit
fc07ed06d5
16 changed files with 1348 additions and 95 deletions
|
|
@ -72,3 +72,7 @@ export function ossDownload(key: string): Promise<HttpResponse> {
|
|||
export function getBanksList(keyword: string): Promise<BankListRes> {
|
||||
return axios.post('common/getBanksList', { keyword });
|
||||
}
|
||||
// 获取勾选部门的项目列表
|
||||
export function getDeptProjectListFromDeptId(id: number): Promise<any> {
|
||||
return axios.get(`common/getDeptProjectList?departmentId=${id}`);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<HttpResponse> {
|
|||
export function delDepartment(id: number): Promise<HttpResponse> {
|
||||
return axios.get(`/department/delDepartment?id=${id}`);
|
||||
}
|
||||
|
||||
// 保存部门项目数据
|
||||
export function saveDataPermission(params: any): Promise<HttpResponse> {
|
||||
return axios.post('/department/saveDataPermission', params);
|
||||
}
|
||||
// 获取部门项目数据
|
||||
export function getDataPermission(id: number): Promise<HttpResponse> {
|
||||
return axios.get(`/department/getDataPermission?id=${id}`);
|
||||
}
|
||||
// ==================== 用户相关 API ====================
|
||||
export function getAllUserList(
|
||||
params: PaginationParams
|
||||
|
|
@ -405,3 +416,81 @@ export function getUseFeeList(
|
|||
): Promise<FeeManagementListRes> {
|
||||
return axios.post(`financeSetting/getUseFeeList`, params);
|
||||
}
|
||||
|
||||
// 合同设置-合同模板管理-全部启用列表
|
||||
export function getAllTemplateSettingList(): Promise<SettingContractNumbeRes> {
|
||||
return axios.get(`contractSetting/getAllTemplateSettingList`);
|
||||
}
|
||||
// 合同设置-合同模板管理-新增
|
||||
export function createTemplateSetting(
|
||||
params: CreateTemplateSettingParam
|
||||
): Promise<HttpResponse> {
|
||||
return axios.post(`contractSetting/createTemplateSetting`, params);
|
||||
}
|
||||
// 合同设置-合同模板管理-编辑
|
||||
export function updateTemplateSetting(
|
||||
params: CreateTemplateSettingParam
|
||||
): Promise<HttpResponse> {
|
||||
return axios.post(`contractSetting/updateTemplateSetting`, params);
|
||||
}
|
||||
// 合同设置-合同模板管理-删除
|
||||
export function delTemplateSetting(id: number): Promise<HttpResponse> {
|
||||
return axios.get(`contractSetting/delTemplateSetting?id=${id}`);
|
||||
}
|
||||
// 合同设置-合同模板管理-单个详情
|
||||
export function getTemplateSettingFromId(
|
||||
id: number
|
||||
): Promise<SettingContractNumbeRes> {
|
||||
return axios.get(`contractSetting/getTemplateSetting?id=${id}`);
|
||||
}
|
||||
// 合同设置-合同模板管理-启用/禁用
|
||||
export function disableTemplateSetting(
|
||||
params: DisableUserParam
|
||||
): Promise<HttpResponse> {
|
||||
return axios.post(`contractSetting/disableTemplateSetting`, params);
|
||||
}
|
||||
// 合同设置-合同模板管理-列表(带分页)
|
||||
export function getTemplateSettingList(
|
||||
params: PaginationParams
|
||||
): Promise<SettingContractTemplateListRes> {
|
||||
return axios.post(`contractSetting/getTemplateSettingList`, params);
|
||||
}
|
||||
|
||||
// 合同设置-合同文档模板管理-全部启用列表
|
||||
export function getAllWordSettingList(): Promise<ContractWordSettingListRes> {
|
||||
return axios.get(`contractSetting/getAllWordSettingList`);
|
||||
}
|
||||
// 合同设置-合同文档模板管理-新增
|
||||
export function createWordSetting(
|
||||
params: ContractWordFormParam
|
||||
): Promise<HttpResponse> {
|
||||
return axios.post(`contractSetting/createWordSetting`, params);
|
||||
}
|
||||
// 合同设置-合同文档模板管理-编辑(更新)
|
||||
export function updateWordSetting(
|
||||
params: ContractWordFormParam
|
||||
): Promise<HttpResponse> {
|
||||
return axios.post(`contractSetting/updateWordSetting`, params);
|
||||
}
|
||||
// 合同设置-合同文档模板管理-删除
|
||||
export function delWordSetting(id: number): Promise<HttpResponse> {
|
||||
return axios.get(`contractSetting/delWordSetting?id=${id}`);
|
||||
}
|
||||
// 合同设置-合同文档模板管理-单个详情
|
||||
export function getWordSettingFromId(
|
||||
id: number
|
||||
): Promise<SettingContractNumbeRes> {
|
||||
return axios.get(`contractSetting/getWordSetting?id=${id}`);
|
||||
}
|
||||
// 合同设置-合同文档模板管理-启用/禁用
|
||||
export function disableWordSetting(
|
||||
params: DisableUserParam
|
||||
): Promise<HttpResponse> {
|
||||
return axios.post(`contractSetting/disableWordSetting`, params);
|
||||
}
|
||||
// 合同设置-合同文档模板管理-列表(带分页)
|
||||
export function getWordSettingList(
|
||||
params: PaginationParams
|
||||
): Promise<ContractWordSettingListRes> {
|
||||
return axios.post(`contractSetting/getWordSettingList`, params);
|
||||
}
|
||||
|
|
|
|||
165
src/components/common/FileUpload.vue
Normal file
165
src/components/common/FileUpload.vue
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
<template>
|
||||
<a-upload
|
||||
draggable
|
||||
:accept="accept"
|
||||
:custom-request="customRequest"
|
||||
:multiple="maxCount > 1"
|
||||
:limit="maxCount"
|
||||
@change="handleChange"
|
||||
>
|
||||
<div style="padding: 20px; text-align: center">
|
||||
<icon-upload style="font-size: 32px; color: #666" />
|
||||
<div style="margin-top: 8px">
|
||||
<p>点击或拖拽文件到此处上传</p>
|
||||
<p v-if="accept">支持的格式:{{ accept }}</p>
|
||||
<p>最大支持 {{ maxSizeText }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</a-upload>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
import { ossUpload } from '@/api/common';
|
||||
import type { UploadType } from '@/types/comm';
|
||||
|
||||
interface UploadResponse {
|
||||
filename: string;
|
||||
size: number;
|
||||
signedPutURL: string;
|
||||
objectKey: string;
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
objectKeys: string[];
|
||||
signedPutURL: string[];
|
||||
uploadType: UploadType;
|
||||
maxSize?: number; // 单位字节
|
||||
maxCount?: number;
|
||||
accept?: string; // 限制类型,例如 "application/pdf" 或 ".doc,.docx,.xls" 或 "application/pdf,.docx"
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:objectKeys', value: string[]): void;
|
||||
(e: 'update:signedPutURL', value: string[]): void;
|
||||
(
|
||||
e: 'upload-success',
|
||||
payload: { objectKey: string; signedPutURL: string }
|
||||
): void;
|
||||
(e: 'upload-error', error: Error): void;
|
||||
}>();
|
||||
|
||||
const maxCount = props.maxCount ?? 1;
|
||||
const accept = props.accept ?? '*/*'; // 默认支持所有文件
|
||||
const maxSizeText = computed(() => {
|
||||
const size = props.maxSize || 10 * 1024 * 1024;
|
||||
return `${Math.round(size / 1024 / 1024)}MB`;
|
||||
});
|
||||
|
||||
// 上传逻辑
|
||||
const customRequest = async (option: any) => {
|
||||
const { onProgress, onError, onSuccess, fileItem } = option;
|
||||
try {
|
||||
const file = fileItem.file;
|
||||
const maxSize = props.maxSize || 10 * 1024 * 1024;
|
||||
|
||||
// 校验大小
|
||||
if (file.size > maxSize) {
|
||||
onError(
|
||||
new Error(`文件大小不能超过 ${Math.round(maxSize / 1024 / 1024)}MB`)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// 校验格式
|
||||
if (props.accept && props.accept !== '*/*') {
|
||||
const acceptList = props.accept.split(',').map((x) => x.trim());
|
||||
const isValid = acceptList.some((type) => {
|
||||
if (type.includes('/')) {
|
||||
// MIME 类型,例如 application/pdf
|
||||
return file.type === type;
|
||||
}
|
||||
if (type.startsWith('.')) {
|
||||
// 扩展名,例如 .pdf,.docx
|
||||
return file.name.toLowerCase().endsWith(type.toLowerCase());
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
if (!isValid) {
|
||||
onError(new Error(`文件格式不支持,只允许:${props.accept}`));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 模拟上传进度
|
||||
let progress = 0;
|
||||
const progressInterval = setInterval(() => {
|
||||
progress += Math.random() * 30;
|
||||
if (progress >= 90) {
|
||||
clearInterval(progressInterval);
|
||||
progress = 90;
|
||||
}
|
||||
// onProgress({ percent: Math.round(progress) })
|
||||
onProgress(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 res = data as UploadResponse;
|
||||
onProgress(100);
|
||||
onSuccess(res);
|
||||
|
||||
// 更新父组件
|
||||
let newKeys = [...props.objectKeys];
|
||||
let newUrls = [...props.signedPutURL];
|
||||
|
||||
if (maxCount === 1) {
|
||||
newKeys = [res.objectKey];
|
||||
newUrls = [res.signedPutURL];
|
||||
} else {
|
||||
newKeys.push(res.objectKey);
|
||||
newUrls.push(res.signedPutURL);
|
||||
newKeys = newKeys.slice(0, maxCount);
|
||||
newUrls = newUrls.slice(0, maxCount);
|
||||
}
|
||||
|
||||
emit('update:objectKeys', newKeys);
|
||||
emit('update:signedPutURL', newUrls);
|
||||
emit('upload-success', {
|
||||
objectKey: res.objectKey,
|
||||
signedPutURL: res.signedPutURL,
|
||||
});
|
||||
} else {
|
||||
const error = new Error(message || '上传失败');
|
||||
onError(error);
|
||||
emit('upload-error', error);
|
||||
}
|
||||
} catch (e) {
|
||||
const err = e instanceof Error ? e : new Error('上传失败');
|
||||
onError(err);
|
||||
emit('upload-error', err);
|
||||
}
|
||||
};
|
||||
|
||||
// 删除文件时更新父组件
|
||||
const handleChange = (newFileList: any[]) => {
|
||||
const newKeys = newFileList
|
||||
.filter((f) => f.status === 'done' && f.response?.objectKey)
|
||||
.map((f) => f.response.objectKey);
|
||||
|
||||
const newUrls = newFileList
|
||||
.filter((f) => f.status === 'done' && f.response?.signedPutURL)
|
||||
.map((f) => f.response.signedPutURL);
|
||||
|
||||
emit('update:objectKeys', newKeys);
|
||||
emit('update:signedPutURL', newUrls);
|
||||
};
|
||||
</script>
|
||||
|
|
@ -45,13 +45,11 @@ export default function useDepartmentList(autoFetch = true) {
|
|||
}
|
||||
};
|
||||
|
||||
const getDepartmentDetailData = async (
|
||||
id: number
|
||||
): Promise<DepartmentItem | null> => {
|
||||
const getDepartmentDetailData = async (id: number): Promise<any> => {
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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<DepartmentIdData>;
|
||||
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;
|
||||
}>;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<a-drawer
|
||||
:width="670"
|
||||
:visible="visible"
|
||||
:title="isEdit ? '编辑条款设置' : '编辑条款设置'"
|
||||
:title="isEdit ? '编辑条款设置' : '新增条款设置'"
|
||||
unmount-on-close
|
||||
@ok="handleOk"
|
||||
@cancel="handleCancel"
|
||||
|
|
@ -30,10 +30,10 @@
|
|||
<a-select v-model="form.type" placeholder="请选择项目类型">
|
||||
<a-option
|
||||
v-for="item in projectTypeList"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
:key="item.id"
|
||||
:value="item.id"
|
||||
>
|
||||
{{ item.label }}
|
||||
{{ item.name }}
|
||||
</a-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
|
|
@ -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<projectTypeInfo[]>([]);
|
||||
const isLoading = ref<boolean>(false);
|
||||
// 表单数据
|
||||
const formRef = ref<FormInstance>();
|
||||
|
||||
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();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@
|
|||
@page-size-change="onPageSizeChange"
|
||||
>
|
||||
<template #type="{ record }">
|
||||
<a-tag checkable :color="getProjectTypeColor(record.type)">
|
||||
<a-tag checkable :color="record.color">
|
||||
{{ record.typeName }}
|
||||
</a-tag>
|
||||
</template>
|
||||
|
|
@ -60,15 +60,8 @@
|
|||
</template>
|
||||
<template #operate="{ record }">
|
||||
<a-space>
|
||||
<a-button
|
||||
:loading="isLoadingDetail"
|
||||
@click="handleDetail(record.id)"
|
||||
>查看</a-button
|
||||
>
|
||||
<a-button
|
||||
:loading="isLoadingEdit"
|
||||
type="primary"
|
||||
@click="handleEditProject(record.id)"
|
||||
<a-button @click="handleDetail(record.id)">查看</a-button>
|
||||
<a-button type="primary" @click="handleEditProject(record.id)"
|
||||
>编辑</a-button
|
||||
>
|
||||
<a-popover trigger="click" position="bottom">
|
||||
|
|
@ -114,7 +107,7 @@
|
|||
import useDataTable from '@/composables/useDataTable';
|
||||
import { ProjectItem } from '@/types/business';
|
||||
import { getProjectList, getProjectDetail } from '@/api/business';
|
||||
import { ProjectPaginationParams, ProjectType } from '@/types/comm';
|
||||
import { ProjectPaginationParams } from '@/types/comm';
|
||||
import projectFormDrawer from './drawer/project-form-drawer.vue';
|
||||
import projectInfoDrawer from './drawer/project-info-drawer.vue';
|
||||
import projectListFilterDrawer from './drawer/project-list-filter-drawer.vue';
|
||||
|
|
@ -185,7 +178,12 @@
|
|||
setLoading(false);
|
||||
};
|
||||
const getProjectDetailData = async (id: number, edit: boolean) => {
|
||||
if (currentProjectInfo.value && currentProjectInfo.value?.id === id) return;
|
||||
if (
|
||||
currentProjectInfo.value &&
|
||||
currentProjectInfo.value?.id === id &&
|
||||
!edit
|
||||
)
|
||||
return;
|
||||
edit ? (isLoadingEdit.value = true) : (isLoadingDetail.value = true);
|
||||
const { code, data } = await getProjectDetail(id);
|
||||
if (code === 200 && data) {
|
||||
|
|
@ -226,11 +224,13 @@
|
|||
showDrawer.value = true;
|
||||
};
|
||||
const handleEditProject = async (id: number) => {
|
||||
if (isLoadingEdit.value) return;
|
||||
isEdit.value = true;
|
||||
await getProjectDetailData(id, true);
|
||||
showDrawer.value = true;
|
||||
};
|
||||
const handleDetail = async (id: number) => {
|
||||
if (isLoadingDetail.value) return;
|
||||
await getProjectDetailData(id, false);
|
||||
showProjectInfoDrawer.value = true;
|
||||
};
|
||||
|
|
@ -240,10 +240,6 @@
|
|||
const handleFilter = () => {
|
||||
showListFilterDrawer.value = true;
|
||||
};
|
||||
const getProjectTypeColor = (type: ProjectType) => {
|
||||
const typeMap = { 1: 'green', 2: 'red', 3: 'orange' };
|
||||
return typeMap[type];
|
||||
};
|
||||
onMounted(() => {
|
||||
setColumns(columns);
|
||||
getProjectListData();
|
||||
|
|
|
|||
230
src/views/setting/system/contract-settings/contract-file.vue
Normal file
230
src/views/setting/system/contract-settings/contract-file.vue
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
<template>
|
||||
<a-card title="合同文档模板设置">
|
||||
<template #extra>
|
||||
<a-button type="primary" @click="handleAddContractNumber"
|
||||
><icon-plus />新增</a-button
|
||||
>
|
||||
</template>
|
||||
<DataTable
|
||||
:data="renderData"
|
||||
:columns="projectColumn"
|
||||
:loading="loading"
|
||||
:total="total"
|
||||
:current="current"
|
||||
:page-size="pageSize"
|
||||
pagination
|
||||
@page-change="onPageChange"
|
||||
@page-size-change="onPageSizeChange"
|
||||
>
|
||||
<template #realname="{ record }">
|
||||
<span>{{ record.realname }}</span>
|
||||
<icon-idcard v-if="record.isManager === 1" />
|
||||
</template>
|
||||
<template #status="{ record }">
|
||||
<a-tag :color="record.status == 1 ? 'green' : 'red'">
|
||||
{{ record.status == 1 ? $t('enable') : $t('disable') }}</a-tag
|
||||
>
|
||||
</template>
|
||||
<template #operate="{ record }">
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleEditContractNumber(record.id)">
|
||||
{{ $t('edit') }}</a-button
|
||||
>
|
||||
<a-popover trigger="click" position="bottom">
|
||||
<a-button>{{ $t('more') }}</a-button>
|
||||
<template #content>
|
||||
<a-space direction="vertical">
|
||||
<a-button @click="handleToggleStatus(record.id, record.status)">
|
||||
{{ record.status == 1 ? $t('disable') : $t('enable') }}
|
||||
</a-button>
|
||||
<a-button status="danger" @click="handleDel(record.id)">
|
||||
{{ $t('delete') }}
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-popover>
|
||||
</a-space>
|
||||
</template>
|
||||
</DataTable>
|
||||
<contractFileForm
|
||||
v-model:visible="showModal"
|
||||
:department-list-data="departmentListData"
|
||||
:is-edit="isEdit"
|
||||
@submitted="getWordSettingListData"
|
||||
/>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import type { TableColumnData } from '@arco-design/web-vue';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import useDataTable from '@/composables/useDataTable';
|
||||
import {
|
||||
getWordSettingFromId,
|
||||
delWordSetting,
|
||||
getWordSettingList,
|
||||
disableWordSetting,
|
||||
getDepartmentList,
|
||||
} from '@/api/setting';
|
||||
import { ContractWordItem, DepartmentItem } from '@/types/setting';
|
||||
import { UserStatus } from '@/types/comm';
|
||||
import { camelToSnake } from '@/utils/caseConvert';
|
||||
// import contractNumberFormDrawer from './drawer/contract-number-form-drawer.vue';
|
||||
import contractFileForm from './modal/contract-file-form.vue';
|
||||
|
||||
const {
|
||||
data: renderData,
|
||||
columns: projectColumn,
|
||||
loading,
|
||||
total,
|
||||
current,
|
||||
pageSize,
|
||||
setData,
|
||||
setColumns,
|
||||
setLoading,
|
||||
setTotal,
|
||||
setCurrent,
|
||||
setPageSize,
|
||||
} = useDataTable({
|
||||
initialPage: 1,
|
||||
initialPageSize: 10,
|
||||
pageSizeOptions: [10, 20, 50, 100],
|
||||
});
|
||||
|
||||
const contractWordList = ref<ContractWordItem[]>([]);
|
||||
const departmentListData = ref<DepartmentItem[]>([]);
|
||||
const showModal = ref<boolean>(false);
|
||||
const isEdit = ref<boolean>(false);
|
||||
const currentNumberInfo = ref<any>(null);
|
||||
// 列配置
|
||||
const columns: TableColumnData[] = [
|
||||
{
|
||||
title: '模板名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
width: 120,
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
title: '适用范围',
|
||||
dataIndex: 'departmentName',
|
||||
key: 'departmentName',
|
||||
render: ({ record }) => record.departmentName || '-',
|
||||
},
|
||||
{
|
||||
title: '适用项目',
|
||||
dataIndex: 'projectName',
|
||||
key: 'projectName',
|
||||
render: ({ record }) => record.projectName || '-',
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
slotName: 'status',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'operate',
|
||||
slotName: 'operate',
|
||||
width: 200,
|
||||
},
|
||||
];
|
||||
|
||||
// 获取列表数据
|
||||
const getWordSettingListData = async () => {
|
||||
const params = {
|
||||
page: current.value,
|
||||
page_size: pageSize.value,
|
||||
};
|
||||
setLoading(true);
|
||||
const { code, data } = await getWordSettingList(params);
|
||||
if (code === 200 && data) {
|
||||
const { contractWordSetting, page } = data;
|
||||
contractWordList.value = Array.isArray(contractWordSetting)
|
||||
? contractWordSetting
|
||||
: [];
|
||||
setData(contractWordList.value);
|
||||
if (page?.total) setTotal(page?.total);
|
||||
}
|
||||
setLoading(false);
|
||||
};
|
||||
// 部门列表
|
||||
const getDepartmentListData = async () => {
|
||||
try {
|
||||
const { code, data } = await getDepartmentList();
|
||||
if (code === 200) {
|
||||
departmentListData.value = data.departmentList;
|
||||
}
|
||||
} catch (error) {
|
||||
// console.error('获取部门列表失败:', error);
|
||||
}
|
||||
};
|
||||
// 分页事件处理
|
||||
const onPageChange = (page: number) => {
|
||||
setCurrent(page);
|
||||
current.value = page;
|
||||
getWordSettingListData();
|
||||
};
|
||||
const onPageSizeChange = (size: number) => {
|
||||
setPageSize(size);
|
||||
pageSize.value = size;
|
||||
getWordSettingListData();
|
||||
};
|
||||
const handleToggleStatus = async (id: number, currentStatus: UserStatus) => {
|
||||
try {
|
||||
const status: UserStatus = currentStatus === 1 ? 2 : 1;
|
||||
const param = { id, status };
|
||||
const { code, message } = await disableWordSetting(param);
|
||||
if (code === 200) {
|
||||
Message.success(message);
|
||||
getWordSettingListData();
|
||||
} else {
|
||||
Message.error(message);
|
||||
}
|
||||
} catch (error) {
|
||||
// 处理错误
|
||||
}
|
||||
};
|
||||
const handleDel = async (id: number) => {
|
||||
try {
|
||||
const { code, message } = await delWordSetting(id);
|
||||
if (code === 200) {
|
||||
Message.success(message);
|
||||
getWordSettingListData();
|
||||
} else {
|
||||
Message.error(message);
|
||||
}
|
||||
} catch (error) {
|
||||
// 处理错误
|
||||
}
|
||||
};
|
||||
const getWordSettingFromIdData = async (id: number) => {
|
||||
const { code, data } = await getWordSettingFromId(id);
|
||||
if (code === 200 && data) {
|
||||
const { contractCodeSetting } = data;
|
||||
currentNumberInfo.value = contractCodeSetting || null;
|
||||
currentNumberInfo.value.arrange = camelToSnake(
|
||||
currentNumberInfo.value.arrange
|
||||
);
|
||||
}
|
||||
};
|
||||
const handleAddContractNumber = () => {
|
||||
// currentNumberInfo.value = null;
|
||||
isEdit.value = false;
|
||||
showModal.value = true;
|
||||
};
|
||||
const handleEditContractNumber = async (id: number) => {
|
||||
await getWordSettingFromIdData(id);
|
||||
isEdit.value = true;
|
||||
// currentNumberInfo.value = info
|
||||
showModal.value = true;
|
||||
};
|
||||
// 初始化数据
|
||||
onMounted(() => {
|
||||
setColumns(columns);
|
||||
getWordSettingListData();
|
||||
getDepartmentListData();
|
||||
});
|
||||
</script>
|
||||
|
|
@ -1,3 +1,231 @@
|
|||
<template>
|
||||
<div>123</div>
|
||||
<a-card title="创建合同模板设置">
|
||||
<template #extra>
|
||||
<a-button type="primary" @click="handleAddContractNumber"
|
||||
><icon-plus />新增</a-button
|
||||
>
|
||||
</template>
|
||||
<DataTable
|
||||
:data="renderData"
|
||||
:columns="projectColumn"
|
||||
:loading="loading"
|
||||
:total="total"
|
||||
:current="current"
|
||||
:page-size="pageSize"
|
||||
pagination
|
||||
@page-change="onPageChange"
|
||||
@page-size-change="onPageSizeChange"
|
||||
>
|
||||
<template #realname="{ record }">
|
||||
<span>{{ record.realname }}</span>
|
||||
<icon-idcard v-if="record.isManager === 1" />
|
||||
</template>
|
||||
<template #status="{ record }">
|
||||
<a-tag :color="record.status == 1 ? 'green' : 'red'">
|
||||
{{ record.status == 1 ? $t('enable') : $t('disable') }}</a-tag
|
||||
>
|
||||
</template>
|
||||
<template #operate="{ record }">
|
||||
<a-space>
|
||||
<a-button type="primary" @click="handleEditContractNumber(record.id)">
|
||||
{{ $t('edit') }}</a-button
|
||||
>
|
||||
<a-popover trigger="click" position="bottom">
|
||||
<a-button>{{ $t('more') }}</a-button>
|
||||
<template #content>
|
||||
<a-space direction="vertical">
|
||||
<a-button @click="handleToggleStatus(record.id, record.status)">
|
||||
{{ record.status == 1 ? $t('disable') : $t('enable') }}
|
||||
</a-button>
|
||||
<a-button status="danger" @click="handleDel(record.id)">
|
||||
{{ $t('delete') }}
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-popover>
|
||||
</a-space>
|
||||
</template>
|
||||
</DataTable>
|
||||
<contractTemplateForm
|
||||
v-model:visible="showModal"
|
||||
:department-list-data="departmentListData"
|
||||
:is-edit="isEdit"
|
||||
@submitted="getTemplateSettingListData"
|
||||
/>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import type { TableColumnData } from '@arco-design/web-vue';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import useDataTable from '@/composables/useDataTable';
|
||||
import {
|
||||
getTemplateSettingFromId,
|
||||
delTemplateSetting,
|
||||
getTemplateSettingList,
|
||||
getDepartmentList,
|
||||
disableTemplateSetting,
|
||||
} from '@/api/setting';
|
||||
import { ContractTemplateSettingItem, DepartmentItem } from '@/types/setting';
|
||||
import { UserStatus } from '@/types/comm';
|
||||
import { camelToSnake } from '@/utils/caseConvert';
|
||||
// import contractNumberFormDrawer from './drawer/contract-number-form-drawer.vue';
|
||||
import contractTemplateForm from './modal/contract-template-form.vue';
|
||||
|
||||
const {
|
||||
data: renderData,
|
||||
columns: projectColumn,
|
||||
loading,
|
||||
total,
|
||||
current,
|
||||
pageSize,
|
||||
setData,
|
||||
setColumns,
|
||||
setLoading,
|
||||
setTotal,
|
||||
setCurrent,
|
||||
setPageSize,
|
||||
} = useDataTable({
|
||||
initialPage: 1,
|
||||
initialPageSize: 10,
|
||||
pageSizeOptions: [10, 20, 50, 100],
|
||||
});
|
||||
|
||||
const contractTemplateList = ref<ContractTemplateSettingItem[]>([]);
|
||||
const departmentListData = ref<DepartmentItem[]>([]);
|
||||
const showModal = ref<boolean>(false);
|
||||
const isEdit = ref<boolean>(false);
|
||||
const currentNumberInfo = ref<any>(null);
|
||||
// 列配置
|
||||
const columns: TableColumnData[] = [
|
||||
{
|
||||
title: '模板名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
width: 120,
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
title: '合同类型',
|
||||
dataIndex: 'contractType',
|
||||
key: 'contractType',
|
||||
width: 250,
|
||||
render: ({ record }) => record.contractType || '-',
|
||||
},
|
||||
{
|
||||
title: '适用范围',
|
||||
dataIndex: 'departmentName',
|
||||
key: 'departmentName',
|
||||
render: ({ record }) => record.departmentName || '-',
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
slotName: 'status',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'operate',
|
||||
slotName: 'operate',
|
||||
width: 200,
|
||||
},
|
||||
];
|
||||
|
||||
// 获取列表数据
|
||||
const getTemplateSettingListData = async () => {
|
||||
const params = {
|
||||
page: current.value,
|
||||
page_size: pageSize.value,
|
||||
};
|
||||
setLoading(true);
|
||||
const { code, data } = await getTemplateSettingList(params);
|
||||
if (code === 200 && data) {
|
||||
const { contractTemplateSetting, page } = data;
|
||||
contractTemplateList.value = Array.isArray(contractTemplateSetting)
|
||||
? contractTemplateSetting
|
||||
: [];
|
||||
setData(contractTemplateList.value);
|
||||
if (page?.total) setTotal(page?.total);
|
||||
}
|
||||
setLoading(false);
|
||||
};
|
||||
// 部门列表
|
||||
const getDepartmentListData = async () => {
|
||||
try {
|
||||
const { code, data } = await getDepartmentList();
|
||||
if (code === 200) {
|
||||
departmentListData.value = data.departmentList;
|
||||
}
|
||||
} catch (error) {
|
||||
// console.error('获取部门列表失败:', error);
|
||||
}
|
||||
};
|
||||
// 分页事件处理
|
||||
const onPageChange = (page: number) => {
|
||||
setCurrent(page);
|
||||
current.value = page;
|
||||
getTemplateSettingListData();
|
||||
};
|
||||
const onPageSizeChange = (size: number) => {
|
||||
setPageSize(size);
|
||||
pageSize.value = size;
|
||||
getTemplateSettingListData();
|
||||
};
|
||||
const handleToggleStatus = async (id: number, currentStatus: UserStatus) => {
|
||||
try {
|
||||
const status: UserStatus = currentStatus === 1 ? 2 : 1;
|
||||
const param = { id, status };
|
||||
const { code, message } = await disableTemplateSetting(param);
|
||||
if (code === 200) {
|
||||
Message.success(message);
|
||||
getTemplateSettingListData();
|
||||
} else {
|
||||
Message.error(message);
|
||||
}
|
||||
} catch (error) {
|
||||
// 处理错误
|
||||
}
|
||||
};
|
||||
const handleDel = async (id: number) => {
|
||||
try {
|
||||
const { code, message } = await delTemplateSetting(id);
|
||||
if (code === 200) {
|
||||
Message.success(message);
|
||||
getTemplateSettingListData();
|
||||
} else {
|
||||
Message.error(message);
|
||||
}
|
||||
} catch (error) {
|
||||
// 处理错误
|
||||
}
|
||||
};
|
||||
const getTemplateSettingFromIdData = async (id: number) => {
|
||||
const { code, data } = await getTemplateSettingFromId(id);
|
||||
if (code === 200 && data) {
|
||||
const { contractCodeSetting } = data;
|
||||
currentNumberInfo.value = contractCodeSetting || null;
|
||||
currentNumberInfo.value.arrange = camelToSnake(
|
||||
currentNumberInfo.value.arrange
|
||||
);
|
||||
}
|
||||
};
|
||||
const handleAddContractNumber = () => {
|
||||
// currentNumberInfo.value = null;
|
||||
isEdit.value = false;
|
||||
showModal.value = true;
|
||||
};
|
||||
const handleEditContractNumber = async (id: number) => {
|
||||
await getTemplateSettingFromIdData(id);
|
||||
isEdit.value = true;
|
||||
// currentNumberInfo.value = info
|
||||
showModal.value = true;
|
||||
};
|
||||
// 初始化数据
|
||||
onMounted(() => {
|
||||
setColumns(columns);
|
||||
getTemplateSettingListData();
|
||||
getDepartmentListData();
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
<template>
|
||||
<div>123</div>
|
||||
</template>
|
||||
|
|
@ -23,8 +23,8 @@
|
|||
<a-col :span="18">
|
||||
<contractNumber v-if="currentSettingId === 1" />
|
||||
<contractTerms v-if="currentSettingId === 2" />
|
||||
<ContractTemplate v-if="currentSettingId === 3" />
|
||||
<createContractTemplate v-if="currentSettingId === 4" />
|
||||
<contractFile v-if="currentSettingId === 3" />
|
||||
<ContractTemplate v-if="currentSettingId === 4" />
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-card>
|
||||
|
|
@ -37,7 +37,7 @@
|
|||
import contractNumber from './contract-number.vue';
|
||||
import contractTerms from './contract-terms.vue';
|
||||
import ContractTemplate from './contract-template.vue';
|
||||
import createContractTemplate from './create-contract-template.vue';
|
||||
import contractFile from './contract-file.vue';
|
||||
// import { } from '@/api/setting';
|
||||
// import { Message } from '@arco-design/web-vue';
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,167 @@
|
|||
<template>
|
||||
<a-modal
|
||||
:visible="visible"
|
||||
:title="isEdit ? '编辑创建合同模板' : '新增创建合同模板'"
|
||||
unmount-on-close
|
||||
@ok="handleSubmit"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
auto-label-width
|
||||
layout="vertical"
|
||||
>
|
||||
<!-- 模版名称 -->
|
||||
<a-form-item label="模版名称" field="name" required>
|
||||
<a-input v-model="form.name" placeholder="请输入模板名称" />
|
||||
</a-form-item>
|
||||
<!-- 适用范围(部门) -->
|
||||
<a-form-item label="适用范围" field="departmentId">
|
||||
<a-tree-select
|
||||
v-model="form.departmentId"
|
||||
:data="props.departmentListData"
|
||||
:field-names="{ key: 'id', title: 'name', children: 'children' }"
|
||||
placeholder="请选择适用范围"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="适用项目" field="projects">
|
||||
<a-select
|
||||
v-model="form.projects"
|
||||
multiple
|
||||
:disabled="!form.departmentId"
|
||||
placeholder="请选择适用项目"
|
||||
>
|
||||
<template v-if="deptProjectList.length">
|
||||
<a-option
|
||||
v-for="item in deptProjectList"
|
||||
:key="item.id"
|
||||
:value="item.id"
|
||||
>
|
||||
{{ item.name }}
|
||||
</a-option>
|
||||
</template>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="上传文档" field="urlArr">
|
||||
<FileUpload
|
||||
v-model:object-keys="form.urlArr"
|
||||
v-model:signed-put-u-r-l="form.urlArr"
|
||||
:max-count="1"
|
||||
:upload-type="1"
|
||||
accept="doc,.docx"
|
||||
/>
|
||||
<!-- <a-upload draggable action="/" /> -->
|
||||
<!-- <FileUpload tip="仅支持.doc / .docx格式,最大文件尺寸10MB" v-model:object-keys="form.url"
|
||||
v-model:signed-put-u-r-l="form.url" list-type="picture-card" /> -->
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, watch } from 'vue';
|
||||
import type { FormInstance } from '@arco-design/web-vue';
|
||||
import { createWordSetting, updateWordSetting } from '@/api/setting';
|
||||
import { DepartmentItem } from '@/types/setting';
|
||||
import { getDeptProjectListFromDeptId } from '@/api/common';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import FileUpload from '@/components/common/FileUpload.vue';
|
||||
|
||||
interface TreeNode {
|
||||
id: string | number;
|
||||
name: string;
|
||||
children?: TreeNode[];
|
||||
}
|
||||
const props = defineProps<{
|
||||
visible: boolean;
|
||||
isEdit: boolean;
|
||||
record?: DepartmentItem | null;
|
||||
departmentListData: TreeNode[]; // 修复类型
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', val: boolean): void;
|
||||
(e: 'submitted'): void;
|
||||
}>();
|
||||
|
||||
// 表单数据
|
||||
const formRef = ref<FormInstance>();
|
||||
const form = reactive<any>({
|
||||
name: '',
|
||||
departmentId: null,
|
||||
projects: [],
|
||||
url: '',
|
||||
urlArr: [],
|
||||
id: null,
|
||||
});
|
||||
const deptProjectList = ref<any>([]);
|
||||
// 校验规则
|
||||
const rules = {
|
||||
name: [{ required: true, message: '请输入模板名称' }],
|
||||
departmentId: [{ required: true, message: '请选择适用范围' }],
|
||||
projects: [{ required: true, message: '请选择适用项目' }],
|
||||
urlArr: [{ required: true, message: '请上传合同文档' }],
|
||||
};
|
||||
const getDeptProjectListFromDeptIdData = async (id) => {
|
||||
const { code, data } = await getDeptProjectListFromDeptId(id);
|
||||
if (code === 200) {
|
||||
deptProjectList.value = data?.deptProjectList || [];
|
||||
}
|
||||
};
|
||||
|
||||
// 表单事件
|
||||
const handleCancel = () => {
|
||||
emit('update:visible', false);
|
||||
};
|
||||
|
||||
// Watch 弹窗控制 & 初始化
|
||||
watch(
|
||||
() => props.visible,
|
||||
(val) => {
|
||||
if (val) {
|
||||
if (props.record) {
|
||||
// form.id = props.record?.id;
|
||||
// form.name = props.record?.name;
|
||||
// form.parent_id = props.record?.parentId ?? '';
|
||||
// form.user_id = props.record?.UserId ?? '';
|
||||
// form.remark = props.record?.remark ?? '';
|
||||
// Object.assign(form, props.record);
|
||||
} else {
|
||||
Object.assign(form, {
|
||||
id: undefined,
|
||||
name: '',
|
||||
departmentId: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
watch(
|
||||
() => form.departmentId,
|
||||
(id) => {
|
||||
if (id) {
|
||||
deptProjectList.value = [];
|
||||
getDeptProjectListFromDeptIdData(id);
|
||||
}
|
||||
}
|
||||
);
|
||||
const handleSubmit = async () => {
|
||||
await formRef.value?.validate();
|
||||
const params = JSON.parse(JSON.stringify(form));
|
||||
params.url = form.urlArr[0];
|
||||
delete params.urlArr;
|
||||
const { code, message } = props.isEdit
|
||||
? await updateWordSetting(params)
|
||||
: await createWordSetting(params);
|
||||
if (code === 200) {
|
||||
Message.success(message);
|
||||
handleCancel();
|
||||
// 通知父组件更新部门列表数据
|
||||
emit('submitted');
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
<template>
|
||||
<a-modal
|
||||
:visible="visible"
|
||||
:title="isEdit ? '编辑创建合同模板' : '新增创建合同模板'"
|
||||
unmount-on-close
|
||||
@ok="handleSubmit"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
auto-label-width
|
||||
layout="vertical"
|
||||
>
|
||||
<!-- 模版名称 -->
|
||||
<a-form-item label="模版名称" field="name" required>
|
||||
<a-input v-model="form.name" placeholder="请输入模板名称" />
|
||||
</a-form-item>
|
||||
<!-- 适用范围(部门) -->
|
||||
<a-form-item label="适用范围" field="user_id">
|
||||
<a-tree-select
|
||||
v-model="form.departmentId"
|
||||
:data="props.departmentListData"
|
||||
:field-names="{ key: 'id', title: 'name', children: 'children' }"
|
||||
placeholder="请选择适用范围"
|
||||
allow-clear
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, watch } from 'vue';
|
||||
import type { FormInstance } from '@arco-design/web-vue';
|
||||
import { createTemplateSetting, updateTemplateSetting } from '@/api/setting';
|
||||
import { DepartmentItem, CreateTemplateSettingParam } from '@/types/setting';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
|
||||
interface TreeNode {
|
||||
id: string | number;
|
||||
name: string;
|
||||
children?: TreeNode[];
|
||||
}
|
||||
const props = defineProps<{
|
||||
visible: boolean;
|
||||
isEdit: boolean;
|
||||
record?: DepartmentItem | null;
|
||||
departmentListData: TreeNode[]; // 修复类型
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:visible', val: boolean): void;
|
||||
(e: 'submitted'): void;
|
||||
}>();
|
||||
|
||||
// 表单数据
|
||||
const formRef = ref<FormInstance>();
|
||||
const form = reactive<CreateTemplateSettingParam>({
|
||||
name: '',
|
||||
departmentId: null,
|
||||
// id: undefined,
|
||||
});
|
||||
|
||||
// 校验规则
|
||||
const rules = {
|
||||
name: [{ required: true, message: '请输入模板名称' }],
|
||||
departmentId: [{ required: true, message: '请选择适用范围' }],
|
||||
};
|
||||
|
||||
// Watch 弹窗控制 & 初始化
|
||||
watch(
|
||||
() => props.visible,
|
||||
(val) => {
|
||||
if (val) {
|
||||
if (props.record) {
|
||||
// form.id = props.record?.id;
|
||||
// form.name = props.record?.name;
|
||||
// form.parent_id = props.record?.parentId ?? '';
|
||||
// form.user_id = props.record?.UserId ?? '';
|
||||
// form.remark = props.record?.remark ?? '';
|
||||
// Object.assign(form, props.record);
|
||||
} else {
|
||||
Object.assign(form, {
|
||||
id: undefined,
|
||||
name: '',
|
||||
departmentId: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
// 表单事件
|
||||
const handleCancel = () => {
|
||||
emit('update:visible', false);
|
||||
};
|
||||
const handleSubmit = async () => {
|
||||
await formRef.value?.validate();
|
||||
const { code, message } = props.isEdit
|
||||
? await updateTemplateSetting(form)
|
||||
: await createTemplateSetting(form);
|
||||
if (code === 200) {
|
||||
Message.success(message);
|
||||
handleCancel();
|
||||
// 通知父组件更新部门列表数据
|
||||
emit('submitted');
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
@ -63,13 +63,8 @@
|
|||
</template>
|
||||
</a-tree>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-descriptions
|
||||
v-if="departmentDetailInfo"
|
||||
:column="1"
|
||||
layout="horizontal"
|
||||
bordered
|
||||
>
|
||||
<a-col v-if="departmentDetailInfo" :span="12">
|
||||
<a-descriptions :column="1" layout="horizontal" bordered>
|
||||
<a-descriptions-item :label="t('department-name')">
|
||||
{{ departmentDetailInfo.name }}</a-descriptions-item
|
||||
>
|
||||
|
|
@ -83,6 +78,39 @@
|
|||
{{ departmentDetailInfo.remark }}</a-descriptions-item
|
||||
>
|
||||
</a-descriptions>
|
||||
<a-card title="选择的项目" style="margin-top: 15px">
|
||||
<template #extra>
|
||||
<a-button type="primary" @click="handleSaveProject"
|
||||
>保存</a-button
|
||||
>
|
||||
</template>
|
||||
<ul>
|
||||
<li
|
||||
v-for="item in allProjetList"
|
||||
:key="item.id"
|
||||
style="margin-top: 10px"
|
||||
>
|
||||
<a-checkbox
|
||||
:model-value="projectIdCheck.includes(item.id)"
|
||||
@change="(val) => onCheckChangeProject(val, item)"
|
||||
>{{ item.name }}</a-checkbox
|
||||
>
|
||||
<ul v-if="item?.buildings?.length" class="flex">
|
||||
<li
|
||||
v-for="subItem in item.buildings"
|
||||
:key="subItem.id"
|
||||
:class="[
|
||||
'building_item',
|
||||
{ choose: projectBuildingIdCheck.includes(subItem.id) },
|
||||
]"
|
||||
@click="handleChooseBuildingId(item, subItem.id)"
|
||||
>
|
||||
{{ subItem.name }}
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<departmentFormModal
|
||||
|
|
@ -106,8 +134,10 @@
|
|||
departmentSetUp,
|
||||
departmentSetDown,
|
||||
delDepartment,
|
||||
saveDataPermission,
|
||||
} from '@/api/setting';
|
||||
import { DepartmentItem, UserList } from '@/types/setting';
|
||||
import { getAllProjectList } from '@/api/business';
|
||||
import { debounce } from '@/utils';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import departmentFormModal from './components/department-form-modal.vue';
|
||||
|
|
@ -121,7 +151,8 @@
|
|||
|
||||
const expandedKeys = ref<(string | number)[]>([]);
|
||||
const { t } = useI18n();
|
||||
|
||||
const projectIdCheck = ref<any>([]); // 已勾选的项目
|
||||
const projectBuildingIdCheck = ref<any>([]); // 已勾选的项目楼栋
|
||||
watch(
|
||||
allDepartmentKeys,
|
||||
(val) => {
|
||||
|
|
@ -133,7 +164,7 @@
|
|||
const showModal = ref<boolean>(false);
|
||||
const isEdit = ref<boolean>(false);
|
||||
const departmentDetailInfo = ref<DepartmentItem | null>(null);
|
||||
|
||||
const allProjetList = ref<any>(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<TreeNode | null>(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<UserList>([]);
|
||||
const userKeyword = ref<string>('');
|
||||
|
||||
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();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue