feat: 设置->合同设置->合同文档模板设置
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user