合同相对人目录

This commit is contained in:
z9130 2024-07-30 10:45:59 +08:00
parent 36bf4c4ab3
commit e5d161b8b1
13 changed files with 1052 additions and 258 deletions

View File

@ -53,8 +53,8 @@ export function setupElegantRouter() {
return '/contract/archive/edit/:id?'
}
if (key === 'contract_perform_edit') {
return '/contract/perform/edit/:id?'
if (key === 'contract_company_edit') {
return '/contract/company/edit/:id?'
}
return routePath

View File

@ -35,6 +35,8 @@ export const views: Record<LastLevelRouteKey, RouteComponent | (() => Promise<Ro
contract_archive_list: () => import("@/views/contract/archive/list/index.vue"),
contract_business_edit: () => import("@/views/contract/business/edit/[id].vue"),
contract_business_list: () => import("@/views/contract/business/list/index.vue"),
contract_company_edit: () => import("@/views/contract/company/edit/[id].vue"),
contract_company_list: () => import("@/views/contract/company/list/index.vue"),
contract_config: () => import("@/views/contract/config/index.vue"),
contract_declaration_edit: () => import("@/views/contract/declaration/edit/[id].vue"),
contract_declaration_list: () => import("@/views/contract/declaration/list/index.vue"),

View File

@ -270,6 +270,40 @@ export const generatedRoutes: GeneratedRoute[] = [
}
]
},
{
name: 'contract_company',
path: '/contract/company',
component: 'view.contract_company_list',
meta: {
title: '合同相对人',
icon: 'icon-park-outline:lens-alignment',
order: 99
},
children: [
{
name: 'contract_company_edit',
path: '/contract/company/edit/:id?',
component: 'view.contract_company_edit',
meta: {
hideInMenu: true,
title: '合同相对人编辑',
activeMenu: 'contract_company',
order: 99
}
},
{
name: 'contract_company_list',
path: '/contract/company/list',
component: 'view.contract_company_list',
meta: {
hideInMenu: true,
title: '合同相对人',
activeMenu: 'contract_company',
order: 20
}
}
]
},
{
name: 'contract_config',
path: '/contract/config',
@ -326,7 +360,7 @@ export const generatedRoutes: GeneratedRoute[] = [
children: [
{
name: 'contract_perform_edit',
path: '/contract/perform/edit/:id?',
path: '/contract/perform/edit/:id',
component: 'view.contract_perform_edit',
meta: {
hideInMenu: true,

View File

@ -197,12 +197,15 @@ const routeMap: RouteMap = {
"contract_business": "/contract/business",
"contract_business_edit": "/contract/business/edit/:id?",
"contract_business_list": "/contract/business/list",
"contract_company": "/contract/company",
"contract_company_edit": "/contract/company/edit/:id?",
"contract_company_list": "/contract/company/list",
"contract_config": "/contract/config",
"contract_declaration": "/contract/declaration",
"contract_declaration_edit": "/contract/declaration/edit/:id?",
"contract_declaration_list": "/contract/declaration/list",
"contract_perform": "/contract/perform",
"contract_perform_edit": "/contract/perform/edit/:id?",
"contract_perform_edit": "/contract/perform/edit/:id",
"contract_perform_list": "/contract/perform/list",
"duty": "/duty",
"function": "/function",

View File

@ -53,12 +53,15 @@ declare module "@elegant-router/types" {
"contract_business": "/contract/business";
"contract_business_edit": "/contract/business/edit/:id?";
"contract_business_list": "/contract/business/list";
"contract_company": "/contract/company";
"contract_company_edit": "/contract/company/edit/:id?";
"contract_company_list": "/contract/company/list";
"contract_config": "/contract/config";
"contract_declaration": "/contract/declaration";
"contract_declaration_edit": "/contract/declaration/edit/:id?";
"contract_declaration_list": "/contract/declaration/list";
"contract_perform": "/contract/perform";
"contract_perform_edit": "/contract/perform/edit/:id?";
"contract_perform_edit": "/contract/perform/edit/:id";
"contract_perform_list": "/contract/perform/list";
"duty": "/duty";
"function": "/function";
@ -202,6 +205,8 @@ declare module "@elegant-router/types" {
| "contract_archive_list"
| "contract_business_edit"
| "contract_business_list"
| "contract_company_edit"
| "contract_company_list"
| "contract_config"
| "contract_declaration_edit"
| "contract_declaration_list"

View File

@ -0,0 +1,605 @@
<script setup lang="ts">
import { reactive, ref, computed, onMounted } from "vue";
import { c, useDialog, useMessage } from "naive-ui";
import type { UploadCustomRequestOptions, UploadFileInfo, UploadInst } from "naive-ui";
import {
getMeetingList,
getAddressorList,
saveMeeting,
} from "@/api/office/meeting";
import { useVxeTable } from "@/hooks/common/vxeTable";
import { useRoute } from "vue-router";
import { useRequest } from "alova/client";
import { DICT_TYPE, getDictObj, getDictOptions } from '@/utils/dict';
import { uploadFile, uploadFiles, downloadFile } from "@/api/system/file";
import ChooseUserModal from "@/views/user-center/ChooseUserModal.vue";
import { router } from "@/router";
import { useTabStore } from "@/store/modules/tab";
import { FileUploader } from "@/utils/file";
import { FileSource } from "@/enums";
const { xGridRef, gridProps, triggerProxy } = useVxeTable({ ref: 'xGridRef' });
const PrimaryKey = "guid"
const message = useMessage();
const dialog = useDialog();
const route = useRoute();
const id = route.params.id as string;
const tabStore = useTabStore();
const contractData = ref({});
const isContractBaseLoading = ref(false)
const uploadRef = ref<UploadInst | null>(null);
const fileUploader = new FileUploader({
uploadRef,
data: { source: FileSource.Ht },
onSuccess: (fileList: any[]) => {
console.log('[ fileList ] >', fileList)
handleSubmit('submit')
},
onError: (err: any) => {
console.log('[ err ] >', err)
},
})
/** 必填字段校验 */
const requiredFieldRules: any = {
"contractName": { required: true, message: '合同名称不得为空' },
"ctrType": { required: true, message: '合同类别不得为空' },
"ctrTwoType": { required: true, message: '合同二级类别不得为空' },
"fundAllocation": { required: true, message: '资金流向不得为空' },
"fundDitch": { required: true, message: '资金渠道不得为空' },
"organiza": { required: true, message: '组织形式不得为空' },
}
/** 必填字段校验,如果是必填字段则返回对应message */
function isRequired(field: string): string {
let rule = requiredFieldRules[field];
if (rule && rule.required) {
return rule.message || `${field}不可为空`;
}
return ""
}
/** Hooks 数据请求 - 选商数据 */
const currData = ref<any>({
fileUuid: "",
isBid: 0,
});
const { send, onSuccess, loading } = useRequest(params => Apis.ht.get_selectmerchantsbasicinfo_getone(
{
params: params
}
), {
immediate: false,
});
onSuccess((res: any) => {
console.log(res);
if (res.data && res.data?.rows && res.data?.rows.length > 0) {
currData.value = res.data?.rows[0] || {};
//
if (currData.value?.fileUuid) {
Apis.common.get_attachment_list({
params: {
uuid: currData.value?.fileUuid,
},
}).then((res: any) => {
console.log(res);
fileUploader.setFileList(res.data.rows);
});
}
console.log("当前数据:", currData.value);
}
});
/** Hooks 数据请求 - 合同类型 */
const {
data: contractTypeData,
onSuccess: onContractTypeSuccess,
loading: addressLoading,
} = useRequest(Apis.ht.get_contractrefertype_list({}), { immediate: true, initialData: { rows: [] } });
onContractTypeSuccess((res: any) => {
console.log(res);
currData.value.htlb = contractTypeData.value.rows[0].contrLevelId
});
/** Hooks - 表格 */
const gridOptions = reactive(gridProps({
columns: [
{ type: 'seq', title: '序号', width: 60 },
{ field: 'referenceName', title: '签约依据名称', minWidth: 150 },
{ field: 'referenceId', title: '签约依据编号', width: 200 },
{ field: 'attachments', title: '附件', width: 120 },
{
title: '操作',
width: 120,
slots: { default: 'operation' }
}
],
data: [],
toolbarConfig: {
enabled: false
},
pagerConfig: {
enabled: false
}
}));
/**
* 文件下载
* @param file
*/
function handleDownload(file: UploadFileInfo) {
message.loading("下载中");
if (file.file) {
return true;
} else {
downloadFile(file.batchId).then((res) => {
message.destroyAll();
message.success(`下载成功:${file.name}`);
});
return false;
}
}
/**
* 打开选择签约依据弹窗
*/
async function handleOpenChooseBasisContractModal(status: boolean) {
}
/**
* 筛选合同列表数据
*
*/
function filterContractTypes(parentId: string) {
return contractTypeData.value.rows.map((item) => {
item.label = item.contrLevelName;
item.value = item.contrLevelId;
return item
}).filter((item) => item.parentId === parentId);
}
/**
* 提交
*/
async function handleSubmit(type: "submit" | "upload") {
console.log(currData.value);
if (type === "submit") {
//
if (fileUploader.fileList.value && fileUploader.fileList.value.length) {
let fileNames = fileUploader.fileList.value.map((item) => item.name);
currData.value.fileName = JSON.stringify(fileNames);
let fileUuids = fileUploader.fileList.value.map((item) => item.batchId);
currData.value.fileUuid = JSON.stringify(fileUuids);
} else {
currData.value.fileName = JSON.stringify([]);
currData.value.fileUuid = JSON.stringify([]);
}
if (
currData.value.custom_otherEquipment &&
currData.value.custom_otherEquipment.length > 0
) {
currData.value.otherEquipment = currData.value.custom_otherEquipment.join(",");
}
await Apis.ht.post_contractbaseinfo_apply({
data: currData.value
});
message.success("提交成功");
back();
} else {
fileUploader.initiateUpload();
}
}
/**
* 页面返回并关闭tab
*/
function back() {
router.back();
tabStore.removeActiveTab();
}
onMounted(async () => {
if (id) {
//
isContractBaseLoading.value = true;
Apis.ht.get_contractbaseinfo_getone({
params: {
guid: id
}
}).then(async (data: any) => {
console.log(data)
if (data && data.contractId) {
contractData.value = data;
//
await send({ contractId: data.contractId });
} else {
dialog.error({
title: '提示',
content: '当前合同不存在',
positiveText: '返回上一层',
closable: false,
maskClosable: false,
onPositiveClick: () => {
back()
}
})
}
}).finally(() => {
isContractBaseLoading.value = false
})
// await addressorSend();
}
});
const containerRef = ref<HTMLElement | undefined>(undefined);
function handleBack() {
dialog.warning({
title: "提示",
content: `是否确认返回上一页面?`,
positiveText: "确认",
negativeText: "取消",
onPositiveClick: async () => {
back();
},
});
}
</script>
<template>
<div ref="containerRef"
class="flex-col-stretch relative gap-16px overflow-hidden bg-white px-4 py-2 lt-sm:overflow-auto">
<div>
<n-affix class="z-20 w-full bg-white py-1" :trigger-top="10" position="absolute" :listen-to="() => containerRef">
<n-space>
<n-button type="primary" size="small" @click="handleSubmit('upload')">
<template #icon>
<icon-mdi-content-save-outline />
</template>
保存</n-button>
<n-button type="primary" size="small" @click="handleSubmit('upload')">
<template #icon>
<icon-mdi-send />
</template>
提交</n-button>
<n-button v-if="id" type="error" size="small">
<template #icon>
<icon-mdi-delete-outline />
</template>
废除
</n-button>
<n-button size="small" @click="handleBack()">
<template #icon>
<icon-mdi-undo-variant />
</template>
返回
</n-button>
</n-space>
</n-affix>
</div>
<n-space vertical class="overflow-auto">
<div class="h-20px"></div>
<n-card size="small">
<template #header>
<div class="flex flex-row items-center">
<span>合同立项基本信息</span>
</div>
</template>
<n-spin :show="isContractBaseLoading">
<n-descriptions label-placement="left" bordered :column="6" label-class="w-120px!" size="small">
<n-descriptions-item :span="6">
<template #label>
合同名称
<span v-if="isRequired('contractName')" class="text-red">*</span>
</template>
<n-input v-model:value="contractData.contractName" placeholder="请输入"></n-input>
</n-descriptions-item>
<n-descriptions-item :span="6">
<template #label>
合同类别
<span v-if="isRequired('ctrType')" class="text-red">*</span>
</template>
<div class="h-80px overflow-y-auto">
<DictTag v-model:value="contractData.ctrType" :options="filterContractTypes('-1')" selectable>
</DictTag>
</div>
</n-descriptions-item>
<n-descriptions-item :span="6">
<template #label>
二级类别
<span v-if="isRequired('ctrTwoType')" class="text-red">*</span>
</template>
<div class="h-80px overflow-y-auto">
<DictTag v-model:value="contractData.ctrTwoType" :options="filterContractTypes(contractData.ctrType)"
selectable placeholder="请选择二级类别">
</DictTag>
</div>
</n-descriptions-item>
<n-descriptions-item :span="6">
<template #label>
资金流向
<span v-if="isRequired('fundAllocation')" class="text-red">*</span>
</template>
<DictTag v-model:value="contractData.fundAllocation" :options="getDictOptions(DICT_TYPE.contractFundFlow)"
selectable> </DictTag>
</n-descriptions-item>
<n-descriptions-item :span="6">
<template #label>
资金渠道
<span v-if="isRequired('fundDitch')" class="text-red">*</span>
</template>
<DictTag v-model:value="contractData.fundDitch" :options="getDictOptions(DICT_TYPE.contractFundingSource)"
selectable> </DictTag>
</n-descriptions-item>
<n-descriptions-item :span="6">
<template #label>
组织形式
<span v-if="isRequired('organiza')" class="text-red">*</span>
</template>
<div>
<DictTag v-model:value="contractData.organiza"
:options="getDictOptions(DICT_TYPE.contractOrganizationForm)" selectable> </DictTag>
</div>
</n-descriptions-item>
<n-descriptions-item :span="2">
<template #label>
预算金额
<span v-if="isRequired('budgetSum')" class="text-red">*</span>
</template>
<div class="flex flex-row">
<n-input-number class="flex-1" v-model:value="contractData.budgetSum"
placeholder="请输入"></n-input-number>
<n-select class="w-100px ml-2" v-model:value="contractData.priceType"
:options="getDictOptions(DICT_TYPE.contractCurrencyUnit)" ceholder="请输入"></n-select>
</div>
</n-descriptions-item>
<n-descriptions-item :span="2">
<template #label> 框架协议 </template>
<span v-if="isRequired('frameProtocol')" class="text-red">*</span>
<n-radio-group v-model:value="contractData.frameProtocol" name="radiogroup1">
<n-space>
<n-radio :value="1"> </n-radio>
<n-radio :value="0"> </n-radio>
</n-space>
</n-radio-group>
</n-descriptions-item>
<n-descriptions-item :span="2" label-width="150px">
<template #label> 框架协议下的合同 </template>
<span v-if="isRequired('frameProtocolCtr')" class="text-red">*</span>
<n-radio-group v-model:value="contractData.frameProtocolCtr" name="radiogroup2">
<n-space>
<n-radio :value="1"> </n-radio>
<n-radio :value="0"> </n-radio>
</n-space>
</n-radio-group>
</n-descriptions-item>
</n-descriptions>
</n-spin>
</n-card>
<n-card size="small">
<template #header>
<div class="flex flex-row items-center">
<span>选商资料</span>
</div>
</template>
<n-spin :show="loading || addressLoading">
<n-descriptions label-placement="left" bordered :column="3" label-class="w-130px" size="small">
<n-descriptions-item :span="2">
<template #label> 项目
<span v-if="isRequired('projectNum')" class="text-red">*</span>
</template>
<div class="min-w-400px">
<NSelect v-model:value="currData.projectNum" :options="getDictOptions(DICT_TYPE.comprehensiveProject)">
</NSelect>
</div>
</n-descriptions-item>
<n-descriptions-item :span="1">
<template #label> 项目类别
<span v-if="isRequired('projectProp')" class="text-red">*</span>
</template>
<div class="h-80px overflow-y-auto">
<DictTag v-model:value="currData.projectProp" :options="getDictOptions(DICT_TYPE.contractProjectType)"
selectable> </DictTag>
</div>
</n-descriptions-item>
<n-descriptions-item :span="3">
<template #label> 项目名称
<span v-if="isRequired('projectNameId')" class="text-red">*</span>
</template>
<div>
<NSelect v-model:value="currData.projectNameId"
:options="getDictOptions(DICT_TYPE.comprehensiveProjectName)">
</NSelect>
</div>
</n-descriptions-item>
<n-descriptions-item :span="3">
<template #label> 商务计价方式
<span v-if="isRequired('priceStyleId')" class="text-red">*</span>
</template>
<div class="">
<DictTag v-model:value="currData.priceStyleId" :options="getDictOptions(DICT_TYPE.contractPriceStyle)"
selectable> </DictTag>
</div>
</n-descriptions-item>
<n-descriptions-item :span="3">
<template #label> 选商方式
<span v-if="isRequired('priceStyleId')" class="text-red">*</span>
</template>
<div class="">
<DictTag v-model:value="currData.priceStyleId"
:options="getDictOptions(DICT_TYPE.contractSelectionMethod)" selectable> </DictTag>
</div>
</n-descriptions-item>
<n-descriptions-item :span="3">
<template #label> 选商方式说明
<span v-if="isRequired('choiceReason')" class="text-red">*</span>
</template>
<div>
<NInput v-model:value="currData.choiceReason" type="textarea"> </NInput>
</div>
</n-descriptions-item>
<n-descriptions-item :span="3">
<template #label> 资质要求
<span v-if="isRequired('qualificReq')" class="text-red">*</span>
</template>
<div>
<NInput v-model:value="currData.qualificReq" type="textarea"> </NInput>
</div>
</n-descriptions-item>
<n-descriptions-item :span="3">
<template #label> 划分标段
<span v-if="isRequired('isBid')" class="text-red">*</span>
</template>
<div class="h-80px overflow-y-auto">
<DictTag v-model:value="currData.isBid" :options="getDictOptions(DICT_TYPE.sectionType)" selectable>
</DictTag>
</div>
</n-descriptions-item>
<n-descriptions-item :span="3">
<template #label> 项目范围
<span v-if="isRequired('projectRange')" class="text-red">*</span>
</template>
<div>
<NInput v-model:value="currData.projectRange" type="textarea"> </NInput>
</div>
</n-descriptions-item>
<n-descriptions-item :span="3">
<template #label> 项目内容
<span v-if="isRequired('projectContent')" class="text-red">*</span>
</template>
<div>
<NInput v-model:value="currData.projectContent" type="textarea"> </NInput>
</div>
</n-descriptions-item>
<n-descriptions-item :span="3">
<template #label> 工期要求(质量要求)
<span v-if="isRequired('quality')" class="text-red">*</span>
</template>
<div>
<NInput v-model:value="currData.quality" type="textarea"> </NInput>
</div>
</n-descriptions-item>
<n-descriptions-item :span="3">
<template #label> 工程量(采购量)
<span v-if="isRequired('stockNums')" class="text-red">*</span>
</template>
<div>
<NInput v-model:value="currData.stockNums" type="textarea"> </NInput>
</div>
</n-descriptions-item>
<n-descriptions-item :span="3">
<template #label> 计划投资明细
<span v-if="isRequired('stockPlanMx')" class="text-red">*</span>
</template>
<div>
<NInput v-model:value="currData.stockPlanMx" type="textarea"> </NInput>
</div>
</n-descriptions-item>
</n-descriptions>
</n-spin>
</n-card>
<n-card size="small">
<template #header>
<div class="flex flex-row items-center">
<span>招标文件</span>
</div>
</template>
<n-spin :show="loading || addressLoading">
<n-descriptions label-placement="left" bordered :column="3" label-class="w-100px" size="small">
<n-descriptions-item :span="3">
<template #label> 相关附件 </template>
<div>
<NUpload ref="uploadAfterRef" directory-dnd multiple :default-file-list="fileUploader.fileList.value"
:custom-request="fileUploader.customRequest" show-download-button :default-upload="false" :max="5"
@update:file-list="fileUploader.handleFileUpdate" @download="handleDownload"
@finish="fileUploader.handleFileUploadFinish">
<n-button>上传文件</n-button>
</NUpload>
</div>
</n-descriptions-item>
</n-descriptions>
</n-spin>
</n-card>
<n-card size="small">
<template #header>
<div class="flex flex-row items-center">
<span>签约依据</span>
</div>
</template>
<n-spin :show="loading || addressLoading">
<VxeGrid ref="xGridRef" v-bind="gridOptions">
<template #operation></template>
</VxeGrid>
</n-spin>
</n-card>
</n-space>
</div>
</template>
<style scoped>
:deep(.readonly .n-input__input-el) {
cursor: pointer !important;
}
:deep(.readonly .n-input__textarea-el) {
cursor: pointer !important;
}
</style>

View File

@ -0,0 +1,164 @@
<script setup lang="ts">
import { reactive, ref, computed } from 'vue';
import { useDialog, useMessage } from 'naive-ui';
import { BasicForm, useForm } from '@/components/Form';
import { PrimaryKey, getColumns, getFormSchema } from './schema';
import { useVxeTable } from '@/hooks/common/vxeTable';
import { usePagination } from 'alova/client';
import { paginationProps } from "@/utils/alova";
import { toDetailPage } from '../../utils';
import { useRouterPush } from '@/hooks/common/router';
const { routerPush } = useRouterPush();
const { xGridRef, gridProps, triggerProxy } = useVxeTable({ ref: 'xGridRef' });
const message = useMessage();
const dialog = useDialog();
const editModalRef = ref();
const recordModalRef = ref();
const searchParams = reactive<any>({});
/** Hooks - 表单 */
const [register, { setFieldsValue, getFieldsValue }] = useForm({
gridProps: { cols: '1 s:2 m:3 l:3 xl:4 2xl:4' },
labelWidth: 80,
schemas: getFormSchema()
});
/** Hooks - 数据请求 */
const { send, onSuccess } = usePagination(({ pageNum, pageSize }: any) =>
Apis.ht.get_selectmerchantsbasicinfo_page({ params: { pageNum, pageSize, ...searchParams, ...getFieldsValue() } }),
{ ...paginationProps(), immediate: false }
);
onSuccess((res: any) => {
console.log(res);
});
/** Hooks - 表格 */
const gridOptions = reactive(gridProps({
columns: getColumns(),
proxyConfig: {
autoLoad: true,
ajax: {
query: ({ page }) => send({ pageNum: page.currentPage, pageSize: page.pageSize })
},
}
}));
function handleQuery(_values: Recordable) {
triggerProxy("reload");
}
function handleReset(_values: Recordable) {
triggerProxy("reload");
}
function openEditModal(record?: Recordable) {
if (record && record.contractId) {
routerPush({
path: '/contract/business/edit/' + record.guid,
})
} else {
routerPush({
path: '/contract/business/edit',
})
}
}
/** 选中数据 */
const selectRow: Recordable = computed(() => {
return xGridRef.value?.getRadioRecord() || null
})
/** 单选框选中事件 */
const setSelectRow = (row: Recordable) => {
if (selectRow.value && selectRow.value[PrimaryKey] === row[PrimaryKey]) {
xGridRef.value?.clearRadioRow()
} else {
xGridRef.value?.setRadioRow(row)
}
}
/** 表格单元格单击事件 */
function handleCellClick({ row }) {
setSelectRow(row)
}
</script>
<template>
<div class="flex-col-stretch gap-4px overflow-hidden bg-white px-4 py-2 lt-sm:overflow-auto">
<BasicForm @register="register" @submit="handleQuery" @reset="handleReset">
<template #money-slot>
<NInput placeholder=""></NInput>
<span class="mx-1"></span>
<NInput placeholder=""></NInput>
</template>
</BasicForm>
<div class="vxebasic-table-container">
<VxeGrid ref="xGridRef" v-bind="gridOptions" @cell-click="handleCellClick">
<template #toolbar_buttons>
<NSpace>
<NButton type="primary" :disabled="!selectRow || !selectRow.guid"
@click="toDetailPage('business', selectRow.guid)">
<template #icon>
<icon-mdi-eye />
</template>
查看
</NButton>
<!-- <NButton type="primary" @click="openEditModal()">
<template #icon>
<icon-mdi-plus />
</template>
新增
</NButton>
<NButton type="warning" :disabled="!selectRow || !selectRow[PrimaryKey]" @click="openEditModal(selectRow)">
<template #icon>
<icon-mdi-square-edit-outline />
</template>
修改
</NButton>
<NButton type="error" :disabled="!selectRow || !selectRow[PrimaryKey]" @click="handleDelete(selectRow)">
<template #icon>
<icon-mdi-delete-outline />
</template>
删除
</NButton> -->
</NSpace>
</template>
<template #radio_cell="{ row, checked }">
<span class="text-base" @click.stop="setSelectRow(row)">
<icon-mdi-check-circle-outline v-if="checked"></icon-mdi-check-circle-outline>
<icon-mdi-checkbox-blank-circle-outline v-else></icon-mdi-checkbox-blank-circle-outline>
</span>
</template>
<template #contract-name-slot="{ row }">
<NTooltip trigger="hover">
<template #trigger>
<span class="text-blue hover:underline underline-offset-2 cursor-pointer" @click="openEditModal(row)">{{
row.contractName }}</span>
</template>
<span class=""> {{ row.contractName }}</span>
</NTooltip>
</template>
</VxeGrid>
</div>
</div>
</template>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,58 @@
import type { VxeGridPropTypes } from 'vxe-table';
import type { FormSchema } from '@/components/Form';
import { useRender } from '@/components/Table';
import { DICT_TYPE, getDictOptions } from '@/utils/dict';
export const PrimaryKey = 'guid';
export function getColumns(_params: any = {}): VxeGridPropTypes.Columns {
return [
{ type: 'radio', width: 40, slots: { radio: 'radio_cell' }, align: 'center', fixed: 'left' },
{ field: 'reportNo', title: '报审序号', width: 100 },
{
field: 'contractName', title: '合同名称', minWidth: 200, slots: {
default: 'contract-name-slot'
}
},
{ field: 'choiceTypeName', title: '选商方式', width: 150 },
{ field: 'fundDitchName', title: '资金渠道', width: 150 },
{ field: 'contractMoney', title: '金额', width: 100 },
{ field: 'priceTypeName', title: '币种', width: 100 },
{ field: 'inputPerson', title: '承办人', width: 100 },
{ field: 'inputDepartName', title: '承办部门', width: 100 },
{ field: 'inputDate', title: '承办时间', width: 130 },
];
}
export function getFormSchema(_params: any = {}): FormSchema[] {
return [
{
field: 'contractName',
component: 'NInput',
label: '合同名称',
componentProps: { placeholder: "" }
},
{
field: 'inputDepartName',
component: 'NInput',
label: '承办部门',
componentProps: { placeholder: "" }
},
{
field: 'contractMoney',
component: 'NInputNumber',
label: '合同金额',
componentProps: {},
},
{
field: 'choiceType',
component: 'NSelect',
label: '选商方式',
componentProps: {
placeholder: "",
options: getDictOptions(DICT_TYPE.contractSelectionMethod)
}
},
];
}

View File

@ -0,0 +1,155 @@
<script lang="ts" setup>
import { onMounted, ref, reactive } from 'vue';
import { usePagination, useRequest } from 'alova/client';
import { paginationProps } from "@/utils/alova";
import { useVxeTable } from '@/hooks/common/vxeTable';
import { getDictObj, DICT_TYPE } from '@/utils/dict';
const props = withDefaults(defineProps<{
id: string
}>(), {
id: '',
});
//
const { xGridRef, gridProps } = useVxeTable({ ref: 'xGridRef' });
/** Hooks - 数据请求 合同基本信息 */
const { send, data, loading, onSuccess } = useRequest(params =>
Apis.ht.get_contractbaseinfo_getone({ params: params }),
{ initialData: {}, immediate: false }
);
onSuccess((data: any) => {
console.log(data);
});
/** Hooks - 表格 */
const gridOptions = reactive(gridProps({
maxHeight: '400px',
columns: [
{ type: 'seq', title: '序号', width: 50, align: 'center' },
{ field: 'meetingDate', title: '签约依据名称', width: 150, align: 'center' },
{ field: 'meetTheme', title: '签约依据编号', minWidth: 200, slots: { default: 'meetingThemeSlot' }, headerAlign: 'center', align: 'left' },
],
data: [],
pagerConfig: {
enabled: false
},
toolbarConfig: {
enabled: false
},
}));
//
const { xGridRef: xGridRef2, triggerProxy: triggerProxy2 } = useVxeTable({ ref: 'xGridRef2' });
/** Hooks - 表格 */
const gridOptions2 = reactive(gridProps({
maxHeight: '400px',
columns: [
{ type: 'seq', title: '序号', width: 50, align: 'center' },
{ field: 'meetingDate', title: '任务名称', minWidth: 150, align: 'center' },
{ field: 'ssfs', title: '送审方式', width: 200, headerAlign: 'center', align: 'left' },
{ field: 'spjb', title: '审批级别', width: 150, },
{ field: 'spr', title: '审批人', width: 150, },
{ field: 'sssj', title: '送审时间', width: 150, },
{ field: 'spzt', title: '审批状态', width: 150, },
],
proxyConfig: {
autoLoad: false,
ajax: {
query: ({ page }) => send({ pageNum: page.currentPage, pageSize: page.pageSize })
},
},
pagerConfig: {
enabled: false
},
toolbarConfig: {
enabled: false
},
}));
const currData = ref<any>({})
onMounted(async () => {
console.log('立项组件加载')
//
send({ guid: props.id })
// try {
// let data = await Apis.ht.get_contractbaseinfo_getone({
// params: { guid: props.id }
// })
// console.log('[ data ] >', data)
// } catch (error) {
// } finally {
// }
// triggerProxy2('reload')
})
</script>
<template>
<div>
<NSpace vertical>
<n-spin :show="loading">
<NCard title="合同付款" size="small">
<NDescriptions bordered size="small" :column="2" label-placement="left" label-class="w-150px">
<NDescriptionsItem label="合同名称">
{{ data.ctrTypeName }}
</NDescriptionsItem>
<NDescriptionsItem label="合同编号">
{{ data.contractName }}
</NDescriptionsItem>
<NDescriptionsItem label="合同标的金额">
{{ data.ctrTwoTypeName }}
</NDescriptionsItem>
<NDescriptionsItem label="累计结算金额">
{{ data.price }}
</NDescriptionsItem>
<NDescriptionsItem label="本次付款金额">
<NInputNumber v-model:value="data.price1"></NInputNumber>
</NDescriptionsItem>
<NDescriptionsItem label="剩余金额">
{{ data.price }}
</NDescriptionsItem>
<NDescriptionsItem label="应付款时间">
<NDatePicker type="date"></NDatePicker>
</NDescriptionsItem>
<NDescriptionsItem label="付款性质">
<NSelect></NSelect>
</NDescriptionsItem>
<NDescriptionsItem label="付款单位">
<NInput v-model:value="data.name"></NInput>
</NDescriptionsItem>
<NDescriptionsItem label="收款单位">
<NInput v-model:value="data.name"></NInput>
</NDescriptionsItem>
<NDescriptionsItem label="承办人">{{ data.inputPerson }}</NDescriptionsItem>
<NDescriptionsItem label="承办人部门">{{ data.inputDepartName }}</NDescriptionsItem>
<NDescriptionsItem label="承办时间">{{ data.inputDate }}</NDescriptionsItem>
</NDescriptions>
</NCard>
</n-spin>
<NCard title="签约依据信息" size="small">
<VxeGrid ref="xGridRef" v-bind="gridOptions">
</VxeGrid>
</NCard>
<NCard title="审批信息" size="small">
<VxeGrid ref="xGridRef2" v-bind="gridOptions2">
</VxeGrid>
</NCard>
</NSpace>
</div>
</template>
<style></style>

View File

@ -1,5 +1,5 @@
<script setup lang="ts">
import { computed, onMounted, reactive, ref } from "vue";
import { computed, defineAsyncComponent, onMounted, reactive, ref } from "vue";
import { useDialog, useMessage } from "naive-ui";
import type { UploadCustomRequestOptions, UploadFileInfo, UploadInst } from "naive-ui";
import { useRoute } from "vue-router";
@ -16,6 +16,11 @@ import { useTabStore } from "@/store/modules/tab";
import { FileUploader } from "@/utils/file";
import { FileSource } from "@/enums";
const ContractPayment = defineAsyncComponent(() => import('../components/contract-payment/contract-payment.vue'));
const ContractModify = defineAsyncComponent(() => import('../components/contract-modify/contract-modify.vue'));
const ContractRelieve = defineAsyncComponent(() => import('../components/contract-relieve/contract-relieve.vue'));
const ContractBreak = defineAsyncComponent(() => import('../components/contract-break/contract-break.vue'));
const { xGridRef, gridProps, triggerProxy } = useVxeTable({ ref: "xGridRef" });
const PrimaryKey = "guid";
@ -141,7 +146,7 @@ function handleDownload(file: UploadFileInfo) {
/**
* 打开选择签约依据弹窗
*/
async function handleOpenChooseBasisContractModal(status: boolean) {}
async function handleOpenChooseBasisContractModal(status: boolean) { }
/**
* 筛选合同列表数据
@ -254,261 +259,24 @@ function handleBack() {
</script>
<template>
<div
ref="containerRef"
class="flex-col-stretch relative gap-16px overflow-hidden bg-white px-4 py-2 lt-sm:overflow-auto"
>
<!-- <div>
<n-affix
class="z-20 w-full bg-white py-1"
:trigger-top="10"
position="absolute"
:listen-to="() => containerRef"
>
<n-space>
<n-button type="primary" size="small" @click="handleSubmit('upload')">
<template #icon>
<icon-mdi-content-save-outline />
</template>
保存
</n-button>
<n-button type="primary" size="small" @click="handleSubmit('upload')">
<template #icon>
<icon-mdi-send />
</template>
提交
</n-button>
<n-button v-if="id" type="error" size="small">
<template #icon>
<icon-mdi-delete-outline />
</template>
删除
</n-button>
<n-button size="small" @click="handleBack()">
<template #icon>
<icon-mdi-undo-variant />
</template>
返回
</n-button>
</n-space>
</n-affix>
</div> -->
<div ref="containerRef"
class="flex-col-stretch relative gap-16px overflow-hidden bg-white px-4 py-2 lt-sm:overflow-auto">
<n-tabs type="card" animated>
<n-tab-pane name="1" tab="合同付款"> </n-tab-pane>
<n-tab-pane name="2" tab="合同变更"> </n-tab-pane>
<n-tab-pane name="3" tab="合同终止(解除)申报"> </n-tab-pane>
<n-tab-pane name="4" tab="违约情况申报"> </n-tab-pane>
<n-tab-pane name="1" tab="合同付款">
<ContractPayment></ContractPayment>
</n-tab-pane>
<n-tab-pane name="2" tab="合同变更">
<ContractModify></ContractModify>
</n-tab-pane>
<n-tab-pane name="3" tab="合同终止(解除)申报">
<ContractRelieve></ContractRelieve>
</n-tab-pane>
<n-tab-pane name="4" tab="违约情况申报">
<ContractBreak></ContractBreak>
</n-tab-pane>
</n-tabs>
<n-space vertical class="overflow-auto">
<!-- <div class="h-20px" /> -->
<n-card size="small">
<template #header>
<div class="flex flex-row items-center">
<span>基本信息</span>
</div>
</template>
<n-spin :show="loading">
<n-descriptions
label-placement="left"
bordered
:column="6"
label-class="w-120px!"
size="small"
>
<n-descriptions-item :span="6">
<template #label>
合同名称
<span v-if="isRequired('contractName')" class="text-red">*</span>
</template>
<n-input v-model:value="currData.contractName" placeholder="请输入" />
</n-descriptions-item>
<n-descriptions-item :span="6">
<template #label>
合同类别
<span v-if="isRequired('ctrType')" class="text-red">*</span>
</template>
<div class="h-80px overflow-y-auto">
<DictTag
v-model:value="currData.ctrType"
:options="filterContractTypes('-1')"
selectable
/>
</div>
</n-descriptions-item>
<n-descriptions-item :span="6">
<template #label>
二级类别
<span v-if="isRequired('ctrTwoType')" class="text-red">*</span>
</template>
<div class="h-80px overflow-y-auto">
<DictTag
v-model:value="currData.ctrTwoType"
:options="filterContractTypes(currData.ctrType)"
selectable
placeholder="请选择二级类别"
/>
</div>
</n-descriptions-item>
<n-descriptions-item :span="6">
<template #label>
资金流向
<span v-if="isRequired('fundAllocation')" class="text-red">*</span>
</template>
<DictTag
v-model:value="currData.fundAllocation"
:options="getDictOptions(DICT_TYPE.contractFundFlow)"
selectable
/>
</n-descriptions-item>
<n-descriptions-item :span="6">
<template #label>
资金渠道
<span v-if="isRequired('fundDitch')" class="text-red">*</span>
</template>
<DictTag
v-model:value="currData.fundDitch"
:options="getDictOptions(DICT_TYPE.contractFundingSource)"
selectable
/>
</n-descriptions-item>
<n-descriptions-item :span="6">
<template #label>
组织形式
<span v-if="isRequired('organiza')" class="text-red">*</span>
</template>
<div>
<DictTag
v-model:value="currData.organiza"
:options="getDictOptions(DICT_TYPE.contractOrganizationForm)"
selectable
/>
</div>
</n-descriptions-item>
<n-descriptions-item :span="2">
<template #label>
预算金额
<span v-if="isRequired('budgetSum')" class="text-red">*</span>
</template>
<div class="flex flex-row">
<n-input-number
v-model:value="currData.budgetSum"
class="flex-1"
placeholder="请输入"
/>
<n-select
v-model:value="currData.priceType"
class="w-100px ml-2"
:options="getDictOptions(DICT_TYPE.contractCurrencyUnit)"
ceholder="请输入"
/>
</div>
</n-descriptions-item>
<n-descriptions-item :span="2">
<template #label> 框架协议 </template>
<span v-if="isRequired('frameProtocol')" class="text-red">*</span>
<n-radio-group v-model:value="currData.frameProtocol" name="radiogroup1">
<n-space>
<n-radio :value="1"> </n-radio>
<n-radio :value="0"> </n-radio>
</n-space>
</n-radio-group>
</n-descriptions-item>
<n-descriptions-item :span="2" label-width="150px">
<template #label> 框架协议下的合同 </template>
<span v-if="isRequired('frameProtocolCtr')" class="text-red">*</span>
<n-radio-group v-model:value="currData.frameProtocolCtr" name="radiogroup2">
<n-space>
<n-radio :value="1"> </n-radio>
<n-radio :value="0"> </n-radio>
</n-space>
</n-radio-group>
</n-descriptions-item>
</n-descriptions>
</n-spin>
</n-card>
<n-card size="small">
<template #header>
<div class="flex flex-row items-center">
<span>相关资料</span>
</div>
</template>
<n-spin :show="loading || addressLoading">
<n-descriptions
label-placement="left"
bordered
:column="3"
label-class="w-100px"
size="small"
>
<n-descriptions-item :span="3">
<template #label> 相关附件 </template>
<div>
<NUpload
ref="uploadRef"
directory-dnd
multiple
:default-file-list="fileUploader.fileList.value"
:custom-request="fileUploader.customRequest"
show-download-button
:default-upload="false"
:max="5"
@update:file-list="fileUploader.handleFileUpdate"
@download="handleDownload"
@finish="fileUploader.handleFileUploadFinish"
>
<n-button>上传文件</n-button>
</NUpload>
</div>
</n-descriptions-item>
</n-descriptions>
</n-spin>
</n-card>
<n-card size="small">
<template #header>
<div class="flex flex-row items-center">
<span>签约依据</span>
</div>
</template>
<template #header-extra>
<n-button
type="primary"
size="small"
@click="handleOpenChooseBasisContractModal(true)"
>
<template #icon>
<icon-mdi-plus />
</template>
新增签约依据
</n-button>
</template>
<n-spin :show="loading || addressLoading">
<VxeGrid ref="xGridRef" v-bind="gridOptions">
<template #toolbar_buttons />
<template #operation />
</VxeGrid>
</n-spin>
</n-card>
</n-space>
</div>
</template>