23 changed files with 2235 additions and 280 deletions
@ -0,0 +1,57 @@ |
|||
import { defHttp } from '@/utils/http/axios'; |
|||
import { ID, IDS, commonExport } from '@/api/base'; |
|||
import { ContractualInfoVO, ContractualInfoForm, ContractualInfoQuery } from './model'; |
|||
|
|||
/** |
|||
* 查询合同信息保存列表 |
|||
* @param params |
|||
* @returns |
|||
*/ |
|||
export function ContractualInfoList(params?: ContractualInfoQuery) { |
|||
return defHttp.get<ContractualInfoVO[]>({ url: '/productManagement/ContractualInfo/list', params }); |
|||
} |
|||
|
|||
/** |
|||
* 导出合同信息保存列表 |
|||
* @param params |
|||
* @returns |
|||
*/ |
|||
export function ContractualInfoExport(params?: ContractualInfoQuery) { |
|||
return commonExport('/productManagement/ContractualInfo/export', params ?? {}); |
|||
} |
|||
|
|||
/** |
|||
* 查询合同信息保存详细 |
|||
* @param id id |
|||
* @returns |
|||
*/ |
|||
export function ContractualInfoInfo(id: ID) { |
|||
return defHttp.get<ContractualInfoVO>({ url: '/productManagement/ContractualInfo/' + id }); |
|||
} |
|||
|
|||
/** |
|||
* 新增合同信息保存 |
|||
* @param data |
|||
* @returns |
|||
*/ |
|||
export function ContractualInfoAdd(data: ContractualInfoForm) { |
|||
return defHttp.postWithMsg<void>({ url: '/productManagement/ContractualInfo', data }); |
|||
} |
|||
|
|||
/** |
|||
* 更新合同信息保存 |
|||
* @param data |
|||
* @returns |
|||
*/ |
|||
export function ContractualInfoUpdate(data: ContractualInfoForm) { |
|||
return defHttp.putWithMsg<void>({ url: '/productManagement/ContractualInfo', data }); |
|||
} |
|||
|
|||
/** |
|||
* 删除合同信息保存 |
|||
* @param id id |
|||
* @returns |
|||
*/ |
|||
export function ContractualInfoRemove(id: ID | IDS) { |
|||
return defHttp.deleteWithMsg<void>({ url: '/productManagement/ContractualInfo/' + id },); |
|||
} |
@ -0,0 +1,90 @@ |
|||
import { BaseEntity, PageQuery } from '@/api/base'; |
|||
|
|||
export interface ContractualInfoVO { |
|||
/** |
|||
* 合同名称 |
|||
*/ |
|||
fileName: string; |
|||
|
|||
/** |
|||
* 采购人名称 |
|||
*/ |
|||
purchaserName: string; |
|||
|
|||
/** |
|||
* 供应商名称或姓名 |
|||
*/ |
|||
supplierName: string; |
|||
|
|||
/** |
|||
* 合同签订时间 |
|||
*/ |
|||
signDate: string; |
|||
|
|||
/** |
|||
* 合同金额 |
|||
*/ |
|||
contractAmount: string; |
|||
|
|||
} |
|||
|
|||
export interface ContractualInfoForm extends BaseEntity { |
|||
/** |
|||
* 合同名称 |
|||
*/ |
|||
fileName?: string; |
|||
|
|||
/** |
|||
* 采购人名称 |
|||
*/ |
|||
purchaserName?: string; |
|||
|
|||
/** |
|||
* 供应商名称或姓名 |
|||
*/ |
|||
supplierName?: string; |
|||
|
|||
/** |
|||
* 合同签订时间 |
|||
*/ |
|||
signDate?: string; |
|||
|
|||
/** |
|||
* 合同金额 |
|||
*/ |
|||
contractAmount?: string; |
|||
|
|||
} |
|||
|
|||
export interface ContractualInfoQuery extends PageQuery { |
|||
|
|||
/** |
|||
* 合同名称 |
|||
*/ |
|||
fileName?: string; |
|||
|
|||
/** |
|||
* 采购人名称 |
|||
*/ |
|||
purchaserName?: string; |
|||
|
|||
/** |
|||
* 供应商名称或姓名 |
|||
*/ |
|||
supplierName?: string; |
|||
|
|||
/** |
|||
* 合同签订时间 |
|||
*/ |
|||
signDate?: string; |
|||
|
|||
/** |
|||
* 合同金额 |
|||
*/ |
|||
contractAmount?: string; |
|||
|
|||
/** |
|||
* 日期范围参数 |
|||
*/ |
|||
params?: any; |
|||
} |
@ -0,0 +1,103 @@ |
|||
import { defHttp } from '@/utils/http/axios'; |
|||
import { ID, IDS, commonExport } from '@/api/base'; |
|||
import { JyjcontractualTaskBatchVO, JyjcontractualTaskBatchForm, JyjcontractualTaskBatchQuery } from './model'; |
|||
|
|||
/** |
|||
* 查询合同批处理记录列表 |
|||
* @param params |
|||
* @returns |
|||
*/ |
|||
export function JyjcontractualTaskBatchList(params?: JyjcontractualTaskBatchQuery) { |
|||
return defHttp.get<JyjcontractualTaskBatchVO[]>({ url: '/productManagement/JyjcontractualTaskBatch/list', params }); |
|||
} |
|||
|
|||
/** |
|||
* 导出合同批处理记录列表 |
|||
* @param params |
|||
* @returns |
|||
*/ |
|||
export function JyjcontractualTaskBatchExport(params?: JyjcontractualTaskBatchQuery) { |
|||
return commonExport('/productManagement/JyjcontractualTaskBatch/export', params ?? {}); |
|||
} |
|||
|
|||
/** |
|||
* 查询合同批处理记录详细 |
|||
* @param id id |
|||
* @returns |
|||
*/ |
|||
export function JyjcontractualTaskBatchInfo(id: ID) { |
|||
return defHttp.get<JyjcontractualTaskBatchVO>({ url: '/productManagement/JyjcontractualTaskBatch/' + id }); |
|||
} |
|||
|
|||
/** |
|||
* 新增合同批处理记录 |
|||
* @param data |
|||
* @returns |
|||
*/ |
|||
export function JyjcontractualTaskBatchAdd(data: JyjcontractualTaskBatchForm) { |
|||
return defHttp.postWithMsg<void>({ url: '/productManagement/JyjcontractualTaskBatch', data ,timeout:1000 * 60 * 10}); |
|||
} |
|||
|
|||
/** |
|||
* 更新合同批处理记录 |
|||
* @param data |
|||
* @returns |
|||
*/ |
|||
export function JyjcontractualTaskBatchUpdate(data: JyjcontractualTaskBatchForm) { |
|||
return defHttp.putWithMsg<void>({ url: '/productManagement/JyjcontractualTaskBatch', data }); |
|||
} |
|||
|
|||
/** |
|||
* 删除合同批处理记录 |
|||
* @param id id |
|||
* @returns |
|||
*/ |
|||
export function JyjcontractualTaskBatchRemove(id: ID | IDS) { |
|||
return defHttp.deleteWithMsg<void>({ url: '/productManagement/JyjcontractualTaskBatch/' + id },); |
|||
} |
|||
|
|||
import { UploadFileParams } from '#/axios'; |
|||
import { AxiosProgressEvent } from 'axios'; |
|||
|
|||
/** |
|||
* @description: Upload interface |
|||
*/ |
|||
export function uploadContractual( |
|||
params: UploadFileParams, |
|||
onUploadProgress?: (progressEvent: AxiosProgressEvent) => void, |
|||
) { |
|||
return defHttp.uploadFile<any>( |
|||
{ |
|||
// 固定url地址
|
|||
url: '/productManagement/JyjcontractualTaskBatch/back/upload', |
|||
onUploadProgress, |
|||
timeout: 1000 * 60 * 10, |
|||
}, |
|||
params, |
|||
); |
|||
} |
|||
/** |
|||
* 根据id获取合同的审查结果 |
|||
* @param id id |
|||
* @returns |
|||
*/ |
|||
export function getContractualResultById(id: ID) { |
|||
return defHttp.get({ url: '/productManagement/JyjcontractualTaskBatch/getContractulResultById/' + id }); |
|||
} |
|||
/** |
|||
* 获取PDF文件流 |
|||
* @param fileName - PDF文件名 |
|||
* @returns Blob 数据 |
|||
*/ |
|||
export function getPdfFile(id:Number) { |
|||
return defHttp.get( |
|||
{ |
|||
url: `/productManagement/JyjcontractualTaskBatch/getContractulPdf/${id}`, |
|||
responseType: 'blob', |
|||
headers: { |
|||
Accept: 'application/pdf', |
|||
} |
|||
}, |
|||
{ isReturnNativeResponse: true } |
|||
); |
|||
} |
@ -0,0 +1,160 @@ |
|||
import { BaseEntity, PageQuery } from '@/api/base'; |
|||
|
|||
export interface JyjcontractualTaskBatchVO { |
|||
/** |
|||
* 任务名称 |
|||
*/ |
|||
taskName: string; |
|||
|
|||
/** |
|||
* 任务类型 |
|||
*/ |
|||
taskType: string; |
|||
|
|||
/** |
|||
* 文档名称 |
|||
*/ |
|||
documentName: string; |
|||
|
|||
/** |
|||
* OSS文件ID |
|||
*/ |
|||
ossId: string | number; |
|||
|
|||
/** |
|||
* 进度状态 |
|||
*/ |
|||
progressStatus: string; |
|||
|
|||
/** |
|||
* 列队任务id |
|||
*/ |
|||
groupId: string | number; |
|||
|
|||
/** |
|||
* 合同总数 |
|||
*/ |
|||
totalContracts: number; |
|||
|
|||
/** |
|||
* 已审批总数 |
|||
*/ |
|||
approvedCount: number; |
|||
|
|||
/** |
|||
* 审核通过数量 |
|||
*/ |
|||
passCount: number; |
|||
|
|||
/** |
|||
* 审核不通过数量 |
|||
*/ |
|||
rejectCount: number; |
|||
|
|||
/** |
|||
* 非审查范围数量 |
|||
*/ |
|||
irrelevantCount: number; |
|||
|
|||
/** |
|||
* 进度(百分比) |
|||
*/ |
|||
progress: number; |
|||
|
|||
} |
|||
|
|||
export interface JyjcontractualTaskBatchForm extends BaseEntity { |
|||
/** |
|||
* 任务名称 |
|||
*/ |
|||
taskName?: string; |
|||
|
|||
/** |
|||
* 任务类型 |
|||
*/ |
|||
taskType?: string; |
|||
|
|||
/** |
|||
* 文档名称 |
|||
*/ |
|||
documentName?: string; |
|||
|
|||
/** |
|||
* OSS文件ID |
|||
*/ |
|||
ossId?: string | number; |
|||
|
|||
/** |
|||
* 进度状态 |
|||
*/ |
|||
progressStatus?: string; |
|||
|
|||
} |
|||
|
|||
export interface JyjcontractualTaskBatchQuery extends PageQuery { |
|||
|
|||
/** |
|||
* 任务名称 |
|||
*/ |
|||
taskName?: string; |
|||
|
|||
/** |
|||
* 任务类型 |
|||
*/ |
|||
taskType?: string; |
|||
|
|||
/** |
|||
* 文档名称 |
|||
*/ |
|||
documentName?: string; |
|||
|
|||
/** |
|||
* OSS文件ID |
|||
*/ |
|||
ossId?: string | number; |
|||
|
|||
/** |
|||
* 进度状态 |
|||
*/ |
|||
progressStatus?: string; |
|||
|
|||
/** |
|||
* 列队任务id |
|||
*/ |
|||
groupId?: string | number; |
|||
|
|||
/** |
|||
* 合同总数 |
|||
*/ |
|||
totalContracts?: number; |
|||
|
|||
/** |
|||
* 已审批总数 |
|||
*/ |
|||
approvedCount?: number; |
|||
|
|||
/** |
|||
* 审核通过数量 |
|||
*/ |
|||
passCount?: number; |
|||
|
|||
/** |
|||
* 审核不通过数量 |
|||
*/ |
|||
rejectCount?: number; |
|||
|
|||
/** |
|||
* 非审查范围数量 |
|||
*/ |
|||
irrelevantCount?: number; |
|||
|
|||
/** |
|||
* 进度(百分比) |
|||
*/ |
|||
progress?: number; |
|||
|
|||
/** |
|||
* 日期范围参数 |
|||
*/ |
|||
params?: any; |
|||
} |
@ -0,0 +1,85 @@ |
|||
import { BasicColumn } from '@/components/Table'; |
|||
import { FormSchema } from '@/components/Form'; |
|||
export const formSchemas: FormSchema[] = [ |
|||
{ |
|||
label: '合同名称', |
|||
field: 'fileName', |
|||
component: 'Input', |
|||
}, |
|||
{ |
|||
label: '采购人名称', |
|||
field: 'purchaserName', |
|||
component: 'Input', |
|||
}, |
|||
{ |
|||
label: '供应商名称或姓名', |
|||
field: 'supplierName', |
|||
component: 'Input', |
|||
}, |
|||
{ |
|||
label: '合同签订时间', |
|||
field: 'signDate', |
|||
component: 'Input', |
|||
}, |
|||
{ |
|||
label: '合同金额', |
|||
field: 'contractAmount', |
|||
component: 'Input', |
|||
}, |
|||
]; |
|||
|
|||
export const columns: BasicColumn[] = [ |
|||
{ |
|||
title: '合同名称', |
|||
dataIndex: 'fileName', |
|||
}, |
|||
{ |
|||
title: '采购人名称', |
|||
dataIndex: 'purchaserName', |
|||
}, |
|||
{ |
|||
title: '供应商名称或姓名', |
|||
dataIndex: 'supplierName', |
|||
}, |
|||
{ |
|||
title: '合同签订时间', |
|||
dataIndex: 'signDate', |
|||
}, |
|||
{ |
|||
title: '合同金额', |
|||
dataIndex: 'contractAmount', |
|||
}, |
|||
]; |
|||
|
|||
export const modalSchemas: FormSchema[] = [ |
|||
{ |
|||
label: '合同名称', |
|||
field: 'fileName', |
|||
required: true, |
|||
component: 'Input', |
|||
}, |
|||
{ |
|||
label: '采购人名称', |
|||
field: 'purchaserName', |
|||
required: true, |
|||
component: 'Input', |
|||
}, |
|||
{ |
|||
label: '供应商名称或姓名', |
|||
field: 'supplierName', |
|||
required: true, |
|||
component: 'Input', |
|||
}, |
|||
{ |
|||
label: '合同签订时间', |
|||
field: 'signDate', |
|||
required: true, |
|||
component: 'Input', |
|||
}, |
|||
{ |
|||
label: '合同金额', |
|||
field: 'contractAmount', |
|||
required: true, |
|||
component: 'Input', |
|||
}, |
|||
]; |
@ -0,0 +1,68 @@ |
|||
<template> |
|||
<BasicModal |
|||
v-bind="$attrs" |
|||
:title="title" |
|||
@register="registerInnerModal" |
|||
@ok="handleSubmit" |
|||
@cancel="resetForm" |
|||
> |
|||
<BasicForm @register="registerForm" /> |
|||
</BasicModal> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { BasicModal, useModalInner } from '@/components/Modal'; |
|||
import { BasicForm, useForm } from '@/components/Form'; |
|||
import { computed, ref, unref } from 'vue'; |
|||
import { ContractualInfoInfo, ContractualInfoAdd, ContractualInfoUpdate } from '@/api/contractReview/ContractualInfo'; |
|||
import { modalSchemas } from './ContractualInfo.data'; |
|||
|
|||
defineOptions({ name: 'ContractualInfoModal' }); |
|||
|
|||
const emit = defineEmits(['register', 'reload']); |
|||
|
|||
const isUpdate = ref<boolean>(false); |
|||
const title = computed<string>(() => { |
|||
return isUpdate.value ? '编辑合同信息保存' : '新增合同信息保存'; |
|||
}); |
|||
|
|||
const [registerInnerModal, { modalLoading, closeModal }] = useModalInner( |
|||
async (data: { record?: Recordable; update: boolean }) => { |
|||
modalLoading(true); |
|||
const { record, update } = data; |
|||
isUpdate.value = update; |
|||
if (update && record) { |
|||
const ret = await ContractualInfoInfo(record.id); |
|||
await setFieldsValue(ret); |
|||
} |
|||
modalLoading(false); |
|||
}, |
|||
); |
|||
|
|||
const [registerForm, { setFieldsValue, resetForm, validate }] = useForm({ |
|||
labelWidth: 100, |
|||
showActionButtonGroup: false, |
|||
baseColProps: { span: 24 }, |
|||
schemas: modalSchemas, |
|||
}); |
|||
|
|||
async function handleSubmit() { |
|||
try { |
|||
modalLoading(true); |
|||
const data = await validate(); |
|||
if (unref(isUpdate)) { |
|||
await ContractualInfoUpdate(data); |
|||
} else { |
|||
await ContractualInfoAdd(data); |
|||
} |
|||
emit('reload'); |
|||
closeModal(); |
|||
await resetForm(); |
|||
} catch (e) { |
|||
} finally { |
|||
modalLoading(false); |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped></style> |
@ -0,0 +1,115 @@ |
|||
<template> |
|||
<PageWrapper dense> |
|||
<BasicTable @register="registerTable"> |
|||
<template #toolbar> |
|||
<a-button |
|||
@click="downloadExcel(ContractualInfoExport, '合同信息保存数据', getForm().getFieldsValue())" |
|||
v-auth="'productManagement:ContractualInfo:export'" |
|||
>导出</a-button |
|||
> |
|||
<a-button |
|||
type="primary" |
|||
danger |
|||
@click="multipleRemove(ContractualInfoRemove)" |
|||
:disabled="!selected" |
|||
v-auth="'productManagement:ContractualInfo:remove'" |
|||
>删除</a-button |
|||
> |
|||
<a-button |
|||
type="primary" |
|||
@click="handleAdd" |
|||
v-auth="'productManagement:ContractualInfo:add'" |
|||
>新增</a-button |
|||
> |
|||
</template> |
|||
<template #bodyCell="{ column, record }"> |
|||
<template v-if="column.key === 'action'"> |
|||
<TableAction |
|||
stopButtonPropagation |
|||
:actions="[ |
|||
{ |
|||
label: '修改', |
|||
icon: IconEnum.EDIT, |
|||
type: 'primary', |
|||
ghost: true, |
|||
auth: 'productManagement:ContractualInfo:edit', |
|||
onClick: handleEdit.bind(null, record), |
|||
}, |
|||
{ |
|||
label: '删除', |
|||
icon: IconEnum.DELETE, |
|||
type: 'primary', |
|||
danger: true, |
|||
ghost: true, |
|||
auth: 'productManagement:ContractualInfo:remove', |
|||
popConfirm: { |
|||
placement: 'left', |
|||
title: '是否删除合同信息保存[' + record.id + ']?', |
|||
confirm: handleDelete.bind(null, record), |
|||
}, |
|||
}, |
|||
]" |
|||
/> |
|||
</template> |
|||
</template> |
|||
</BasicTable> |
|||
<ContractualInfoModal @register="registerModal" @reload="reload" /> |
|||
</PageWrapper> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { PageWrapper } from '@/components/Page'; |
|||
import { BasicTable, useTable, TableAction } from '@/components/Table'; |
|||
import { ContractualInfoList, ContractualInfoExport, ContractualInfoRemove } from '@/api/contractReview/ContractualInfo'; |
|||
import { downloadExcel } from '@/utils/file/download'; |
|||
import { useModal } from '@/components/Modal'; |
|||
import ContractualInfoModal from './ContractualInfoModal.vue'; |
|||
import { formSchemas, columns } from './ContractualInfo.data'; |
|||
import { IconEnum } from '@/enums/appEnum'; |
|||
|
|||
defineOptions({ name: 'ContractualInfo' }); |
|||
|
|||
const [registerTable, { reload, multipleRemove, selected, getForm }] = useTable({ |
|||
rowSelection: { |
|||
type: 'checkbox', |
|||
}, |
|||
title: '合同信息保存列表', |
|||
api: ContractualInfoList, |
|||
showIndexColumn: false, |
|||
rowKey: 'id', |
|||
useSearchForm: true, |
|||
formConfig: { |
|||
schemas: formSchemas, |
|||
baseColProps: { |
|||
xs: 24, |
|||
sm: 24, |
|||
md: 24, |
|||
lg: 6, |
|||
}, |
|||
}, |
|||
columns: columns, |
|||
actionColumn: { |
|||
width: 200, |
|||
title: '操作', |
|||
key: 'action', |
|||
fixed: 'right', |
|||
}, |
|||
}); |
|||
|
|||
const [registerModal, { openModal }] = useModal(); |
|||
|
|||
function handleEdit(record: Recordable) { |
|||
openModal(true, { record, update: true }); |
|||
} |
|||
|
|||
function handleAdd() { |
|||
openModal(true, { update: false }); |
|||
} |
|||
|
|||
async function handleDelete(record: Recordable) { |
|||
await ContractualInfoRemove([record.id]); |
|||
await reload(); |
|||
} |
|||
</script> |
|||
|
|||
<style scoped></style> |
@ -0,0 +1,264 @@ |
|||
<template> |
|||
<BasicModal |
|||
v-bind="$attrs" |
|||
title="title" |
|||
:canFullscreen="true" |
|||
:defaultFullscreen="true" |
|||
:showOkBtn="false" |
|||
@register="registerInnerModal" |
|||
> |
|||
<div class="grid grid-cols-5 gap-4"> |
|||
<div class="col-span-3"> |
|||
<!-- <CanvasEditor |
|||
ref="canvasEditor" |
|||
:parentContent="parentContent" |
|||
:view="view" |
|||
@save-content="handleSaveCanvasEditorContent" |
|||
/> --> |
|||
<PdfViewer :id="currentId" :key="pdfViewerkey" v-if="currentId!=0"/> |
|||
</div> |
|||
<div class="col-span-2"> |
|||
<!-- 添加 col-span-2 和高度样式 --> |
|||
<div class="tabs-container"> |
|||
<Tabs v-model:activeKey="activeKey" type="card"> |
|||
<TabPane key="1" tab="审查结果"> |
|||
<div class="scroll-container"> |
|||
<div class="card-container"> |
|||
<Card |
|||
v-for="(item, index) in cardList" |
|||
:key="index" |
|||
:class="['custom-card', { 'card-selected': selectedCard === item.title }]" |
|||
@click="handleCardClick(item.title)" |
|||
> |
|||
<template #title>{{ item.title }}</template> |
|||
{{ item.text }} |
|||
{{ item.content }} |
|||
</Card> |
|||
</div> |
|||
</div> |
|||
</TabPane> |
|||
<!-- <TabPane key="2" tab="Tab 2">Content of Tab Pane 2</TabPane> |
|||
<TabPane key="3" tab="Tab 3">Content of Tab Pane 3</TabPane> --> |
|||
</Tabs> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</BasicModal> |
|||
</template> |
|||
<script lang="ts" setup> |
|||
import { ref, onMounted, reactive } from 'vue'; |
|||
import { BasicModal, useModalInner } from '@/components/Modal'; |
|||
|
|||
import { Tabs, TabPane, Card } from 'ant-design-vue'; |
|||
import { useRouter } from 'vue-router'; |
|||
import CanvasEditor from '@/views/CanvasEditor/index.vue'; |
|||
import { getContractualResultById } from '@/api/contractReview/JyjcontractualTaskBatch'; |
|||
import PdfViewer from "./PdfViewer.vue" |
|||
const activeKey = ref('1'); |
|||
const currentId = ref<any>(0); |
|||
const pdfViewerkey = ref(0); |
|||
// 卡片数据 |
|||
const cardList = ref<any>([]); |
|||
const selectedCard = ref(''); // 用于存储当前选中的卡片标题 |
|||
const handleCardClick = (title) => { |
|||
selectedCard.value = title; |
|||
console.log('点击的卡片标题:', title); |
|||
}; |
|||
|
|||
const [registerInnerModal, { modalLoading, closeModal }] = useModalInner( |
|||
async (data: { record?: Recordable }) => { |
|||
modalLoading(true); |
|||
cardList.value=[] |
|||
const { record } = data; |
|||
const res = await getContractualResultById(record.id); |
|||
pdfViewerkey.value+=1 |
|||
currentId.value=record.id |
|||
for (const item of res.results) { |
|||
for (const content of item.contentList) { |
|||
cardList.value.push({ |
|||
title: content.problemTitle, |
|||
text: content.text, |
|||
content: content.problemDesc, |
|||
isPosition: content.isPosition, |
|||
accord: item.accord, |
|||
}); |
|||
} |
|||
} |
|||
modalLoading(false); |
|||
}, |
|||
); |
|||
|
|||
// 存放父组件传递的数据 |
|||
let parentContent = reactive<any>(undefined); |
|||
// 存放子组件数据 |
|||
const content = ref<any>(undefined); |
|||
// 标识符 |
|||
const view = ref<string | undefined>(undefined); |
|||
|
|||
// 初始化数据 |
|||
onMounted(() => { |
|||
console.log('模拟父组件向后端请求数据, 传递给子组件'); |
|||
getEditorContent(); |
|||
view.value = 'parent'; |
|||
}); |
|||
|
|||
// 模拟后端获取数据的方法 |
|||
const getEditorContent = () => { |
|||
parentContent = { |
|||
header: [ |
|||
{ |
|||
value: '', |
|||
size: 12, |
|||
bold: false, |
|||
color: 'rgb(33, 53, 71)', |
|||
italic: false, |
|||
}, |
|||
], |
|||
main: [ |
|||
{ |
|||
value: |
|||
'父类传递的数据 通过后端获取!\n甲方:企查查科技股份有限公司\n法定代表人:陈德强\n住所:苏州工业园区科创东区东石泾港路2号润港产业园6号楼10层1001室11', |
|||
size: 10, |
|||
bold: false, |
|||
}, |
|||
], |
|||
}; |
|||
}; |
|||
|
|||
// 组件引用 |
|||
const canvasEditor = ref<InstanceType<typeof CanvasEditor> | null>(null); |
|||
|
|||
// 保存内容方法 |
|||
const handleSaveContent = () => { |
|||
// 访问子组件的方法 |
|||
(canvasEditor.value as any).saveContent(); |
|||
}; |
|||
|
|||
// 处理子组件传递的数据 |
|||
const handleSaveCanvasEditorContent = (data: any) => { |
|||
console.log('从子组件接收到的数据:', data); |
|||
// 将data数据转换为 json 格式的数据, 方便入库处理 |
|||
content.value = JSON.stringify(data); |
|||
console.log('转换后的数据 content 为: ', content.value); |
|||
}; |
|||
|
|||
const router = useRouter(); |
|||
// 状态定义 |
|||
const loading = ref(false); |
|||
const handleMenuClick = ({ key }) => { |
|||
console.log(key); |
|||
router.push({ path: key }); |
|||
}; |
|||
// 获取任务列表 |
|||
const fetchTaskList = async () => {}; |
|||
|
|||
// 页面加载时获取数据 |
|||
onMounted(() => { |
|||
fetchTaskList(); |
|||
}); |
|||
setTimeout(() => { |
|||
loading.value = false; |
|||
}, 1500); |
|||
</script> |
|||
<style scoped> |
|||
/* Tabs 相关样式 */ |
|||
/* 设置未选中标签的基本样式 */ |
|||
:deep(.ant-tabs-card > .ant-tabs-nav .ant-tabs-tab) { |
|||
color: rgba(0, 0, 0, 0.85) !important; /* 文字颜色为黑色,带透明度 */ |
|||
background: #fff; /* 背景色为白色 */ |
|||
border: 1px solid #f0f0f0; /* 浅灰色边框 */ |
|||
} |
|||
|
|||
/* 设置选中标签的样式 */ |
|||
:deep(.ant-tabs-card > .ant-tabs-nav .ant-tabs-tab-active) { |
|||
color: #ffffff !important; /* 文字颜色为白色 */ |
|||
background: #1890ff !important; /* 背景色为蓝色 */ |
|||
border-color: #1890ff !important; /* 边框颜色也改为蓝色 */ |
|||
} |
|||
|
|||
/* 确保选中标签的文字颜色为白色 */ |
|||
:deep(.ant-tabs-card > .ant-tabs-nav .ant-tabs-tab-active .ant-tabs-tab-btn) { |
|||
color: #ffffff !important; /* 文字颜色强制设为白色 */ |
|||
} |
|||
|
|||
/* 鼠标悬停时的文字颜色效果 */ |
|||
:deep(.ant-tabs-card > .ant-tabs-nav .ant-tabs-tab:hover .ant-tabs-tab-btn) { |
|||
color: #1890ff !important; /* 悬停时文字变为蓝色 */ |
|||
} |
|||
|
|||
/* Card 相关样式 */ |
|||
/* 卡片容器布局设置 */ |
|||
.card-container { |
|||
display: flex; |
|||
gap: 16px; |
|||
flex-wrap: wrap; |
|||
width: 100%; |
|||
} |
|||
|
|||
/* 单个卡片的基本样式 */ |
|||
.custom-card { |
|||
width: 100%; |
|||
margin-bottom: 16px; |
|||
cursor: pointer; |
|||
transition: all 0.3s; |
|||
background-color: #fff; /* 添加默认背景色 */ |
|||
border: 1px solid #e8e8e8; /* 添加默认边框 */ |
|||
} |
|||
|
|||
/* 卡片选中状态 */ |
|||
.custom-card.card-selected { |
|||
background-color: #e6f4ff !important; /* 选中时的淡蓝色背景 */ |
|||
border: 1px solid #1890ff !important; /* 选中时的蓝色边框 */ |
|||
} |
|||
|
|||
/* 卡片悬停效果 */ |
|||
.custom-card:hover { |
|||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); |
|||
transform: translateY(-2px); |
|||
} |
|||
|
|||
/* 选中状态下的悬停效果 */ |
|||
.custom-card.card-selected:hover { |
|||
background-color: #e6f4ff !important; |
|||
box-shadow: 0 4px 12px rgba(24, 144, 255, 0.1); |
|||
} |
|||
|
|||
/* 卡片点击效果 */ |
|||
.custom-card:active { |
|||
transform: translateY(0); |
|||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); |
|||
} |
|||
|
|||
/* 修改卡片内容区域的内边距 */ |
|||
:deep(.ant-card-body) { |
|||
padding: 16px; |
|||
} |
|||
|
|||
/* 滚动容器样式 */ |
|||
.scroll-container { |
|||
height: calc(100vh - 25vh); /* 可以根据实际需要调整高度 */ |
|||
overflow-y: auto; /* 添加垂直滚动条 */ |
|||
padding: 16px; |
|||
} |
|||
|
|||
/* 美化滚动条样式(可选) */ |
|||
.scroll-container::-webkit-scrollbar { |
|||
width: 6px; /* 滚动条宽度 */ |
|||
} |
|||
|
|||
.scroll-container::-webkit-scrollbar-thumb { |
|||
background-color: #ccc; /* 滚动条颜色 */ |
|||
border-radius: 3px; /* 滚动条圆角 */ |
|||
} |
|||
|
|||
.scroll-container::-webkit-scrollbar-track { |
|||
background-color: #f1f1f1; /* 滚动条轨道颜色 */ |
|||
} |
|||
|
|||
/* 保持原有的卡片容器样式 */ |
|||
.card-container { |
|||
display: flex; |
|||
gap: 16px; |
|||
flex-wrap: wrap; |
|||
} |
|||
</style> |
@ -0,0 +1,208 @@ |
|||
<template> |
|||
<PageWrapper dense> |
|||
<BasicTable @register="registerTable"> |
|||
<template #toolbar v-if="props.showAdd"> |
|||
<a-button |
|||
type="primary" |
|||
@click="handleAdd" |
|||
v-auth="'productManagement:ContractualTasks:add'" |
|||
>新增</a-button |
|||
> |
|||
</template> |
|||
<template #bodyCell="{ column, record }"> |
|||
<template v-if="column.key === 'action'"> |
|||
<TableAction |
|||
stopButtonPropagation |
|||
:actions="[ |
|||
{ |
|||
label: '详情', |
|||
icon: IconEnum.EDIT, |
|||
type: 'primary', |
|||
ghost: true, |
|||
ifShow: () => { |
|||
if ( |
|||
record.progressStatus != 'PENDING' && |
|||
record.progressStatus != 'STARTED' && |
|||
record.progressStatus != 'REVOKED' |
|||
) { |
|||
return true; |
|||
} else { |
|||
return false; |
|||
} |
|||
}, |
|||
onClick: handleDetail.bind(null, record), |
|||
}, |
|||
// { |
|||
// label: '下载', |
|||
// icon: IconEnum.DOWNLOAD, |
|||
// type: 'primary', |
|||
// color: 'success', |
|||
// ghost: true, |
|||
// ifShow: () => { |
|||
// if ( |
|||
// record.progressStatus != 'PENDING' && |
|||
// record.progressStatus != 'STARTED' && |
|||
// record.progressStatus != 'REVOKED' |
|||
// ) { |
|||
// return true; |
|||
// } else { |
|||
// return false; |
|||
// } |
|||
// }, |
|||
// onClick: handleDownload.bind(null, record), |
|||
// }, |
|||
// { |
|||
// label: '下载', |
|||
// icon: IconEnum.DOWNLOAD, |
|||
// type: 'primary', |
|||
// color: 'success', |
|||
// ghost: true, |
|||
// ifShow: () => { |
|||
// if ( |
|||
// record.progressStatus != 'PENDING' && |
|||
// record.progressStatus != 'STARTED' && |
|||
// record.progressStatus != 'REVOKED' |
|||
// ) { |
|||
// return true; |
|||
// } else { |
|||
// return false; |
|||
// } |
|||
// }, |
|||
// onClick: handleDownload.bind(null, record), |
|||
// }, |
|||
{ |
|||
label: '终止任务', |
|||
icon: IconEnum.DELETE, |
|||
type: 'primary', |
|||
danger: true, |
|||
ghost: true, |
|||
ifShow: () => { |
|||
if (record.progressStatus == 'PENDING') { |
|||
return true; |
|||
} else { |
|||
return false; |
|||
} |
|||
}, |
|||
popConfirm: { |
|||
placement: 'left', |
|||
title: '是否终止当前任务?', |
|||
confirm: handleStop.bind(null, record), |
|||
}, |
|||
}, |
|||
]" |
|||
/> |
|||
</template> |
|||
</template> |
|||
</BasicTable> |
|||
<ContractualTasksModal @register="registerModal" @reload="reload" /> |
|||
<ContractualShowModal @register="registerShowModal" /> |
|||
<DocsDrawer @register="registerDrawer" /> |
|||
|
|||
</PageWrapper> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { PageWrapper } from '@/components/Page'; |
|||
import { BasicTable, useTable, TableAction } from '@/components/Table'; |
|||
|
|||
import { useModal } from '@/components/Modal'; |
|||
import ContractualTasksModal from './ContractualTasksModal.vue'; |
|||
import DocsDrawer from '@/views/documentReview/DocumentTasks/DocsDrawer.vue'; |
|||
|
|||
import { formSchemas, columns } from './ContractualTasks.data'; |
|||
import { IconEnum } from '@/enums/appEnum'; |
|||
import ContractualShowModal from '@/views/contractReview/ContractualTasks/ContractualShowModal.vue'; |
|||
import { DocumentTasksStop } from '@/api/documentReview/DocumentTasks'; |
|||
import { ContractualTasksList } from '@/api/contractReview/ContractualTasks'; |
|||
import { |
|||
DocumentTaskResultsInfoByTaskId, |
|||
DocumentTaskResultDownload, |
|||
} from '@/api/documentReview/DocumentTaskResults'; |
|||
import { onMounted } from 'vue'; |
|||
import { useDrawer } from '@/components/Drawer'; |
|||
|
|||
// 组件初始化时判断 batchName 是否存在 |
|||
onMounted(() => { |
|||
console.log('onMountedonMountedonMounted'); |
|||
const { setFieldsValue, updateSchema } = getForm(); |
|||
if (props.batchName) { |
|||
setFieldsValue({ batchName: props.batchName }); |
|||
updateSchema({ field: 'batchName', dynamicDisabled: true }); |
|||
} |
|||
if (props.resultType) { |
|||
if (props.resultType == '审查失败文件') { |
|||
setFieldsValue({ progressStatus: 'FAILURE', groupId: props.groupId }); |
|||
updateSchema([ |
|||
{ field: 'progressStatus', dynamicDisabled: true }, |
|||
{ field: 'resultType', dynamicDisabled: true }, |
|||
]); |
|||
} else { |
|||
setFieldsValue({ resultType: props.resultType, groupId: props.groupId }); |
|||
updateSchema({ field: 'resultType', dynamicDisabled: true }); |
|||
} |
|||
} |
|||
}); |
|||
|
|||
const [registerShowModal, { openModal: openShowModal }] = useModal(); |
|||
const [registerDrawer, { openDrawer }] = useDrawer(); |
|||
|
|||
defineOptions({ name: 'ContractualTasks' }); |
|||
let props = defineProps(['showAdd', 'groupId', 'batchName', 'resultType']); |
|||
|
|||
const [registerTable, { reload, multipleRemove, selected, getForm }] = useTable({ |
|||
rowSelection: { |
|||
type: 'checkbox', |
|||
}, |
|||
title: '合同任务列表', |
|||
api: ContractualTasksList, |
|||
showIndexColumn: false, |
|||
rowKey: 'id', |
|||
useSearchForm: true, |
|||
formConfig: { |
|||
schemas: formSchemas, |
|||
baseColProps: { |
|||
xs: 24, |
|||
sm: 24, |
|||
md: 24, |
|||
lg: 6, |
|||
}, |
|||
}, |
|||
columns: columns, |
|||
actionColumn: { |
|||
width: 200, |
|||
title: '操作', |
|||
key: 'action', |
|||
fixed: 'right', |
|||
}, |
|||
}); |
|||
|
|||
const [registerModal, { openModal }] = useModal(); |
|||
|
|||
async function handleDetail(record: Recordable) { |
|||
if(record.progressStatus=="FAILURE"){ |
|||
let res = await DocumentTaskResultsInfoByTaskId(record.id); |
|||
openDrawer(true, { value: res.result, type: 'markdown' }); |
|||
}else{ |
|||
openShowModal(true, {record }); |
|||
|
|||
} |
|||
|
|||
//根据record.id查询结果详情 |
|||
} |
|||
|
|||
async function handleStop(record: Recordable) { |
|||
await DocumentTasksStop(record.id); |
|||
await reload(); |
|||
} |
|||
|
|||
function handleAdd() { |
|||
openModal(true, { update: false }); |
|||
} |
|||
|
|||
async function handleDownload(record: Recordable) { |
|||
await DocumentTaskResultDownload([record.id]); |
|||
await reload(); |
|||
} |
|||
</script> |
|||
|
|||
<style scoped></style> |
@ -0,0 +1,127 @@ |
|||
<template> |
|||
<div class="pdf-container"> |
|||
<vue-office-docx |
|||
:src="pdfUrl" |
|||
/> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { ref, onMounted, nextTick } from 'vue'; |
|||
//引入VueOfficeDocx组件 |
|||
import VueOfficeDocx from '@vue-office/pdf' |
|||
//引入相关样式 |
|||
import { message } from 'ant-design-vue'; |
|||
import { MinusOutlined, PlusOutlined } from '@ant-design/icons-vue'; |
|||
import { getPdfFile } from '@/api/contractReview/JyjcontractualTaskBatch'; |
|||
|
|||
// Props 定义 |
|||
interface Props { |
|||
id: string; |
|||
} |
|||
const props = defineProps<Props>(); |
|||
|
|||
const pdfUrl = ref<string>(''); |
|||
const currentPage = ref(1); |
|||
const pageCount = ref(0); |
|||
const documentLoaded = ref(false); |
|||
const scale = ref(1.5); // 初始缩放比例 |
|||
|
|||
|
|||
// 适应宽度 |
|||
const fitToWidth = () => { |
|||
const pdfContainer = document.querySelector('.pdf-wrapper'); |
|||
if (pdfContainer) { |
|||
const containerWidth = pdfContainer.clientWidth; |
|||
const pdfCanvas = document.querySelector('.vue3-pdf canvas') as HTMLCanvasElement; |
|||
if (pdfCanvas) { |
|||
const originalWidth = pdfCanvas.width / scale.value; |
|||
scale.value = +(containerWidth / originalWidth * 0.95).toFixed(1); // 95% 的容器宽度 |
|||
} |
|||
} |
|||
}; |
|||
|
|||
// 加载 PDF |
|||
const loadPdf = async () => { |
|||
try { |
|||
if(props.id==0){ |
|||
pdfUrl.value=''; |
|||
return; |
|||
} |
|||
console.log('PDF加载成功1:', pdfUrl.value); |
|||
|
|||
const response = await getPdfFile(props.id); |
|||
if (response?.data) { |
|||
console.log('PDF加载成功2:', pdfUrl.value); |
|||
|
|||
const blob = new Blob([response.data], { type: 'application/pdf' }); |
|||
pdfUrl.value = URL.createObjectURL(blob); |
|||
console.log('PDF加载成功3:', pdfUrl.value); |
|||
// PDF 加载完成后自动适应宽度 |
|||
nextTick(() => { |
|||
setTimeout(fitToWidth, 500); |
|||
}); |
|||
} |
|||
} catch (error) { |
|||
console.error('PDF加载失败:', error); |
|||
} |
|||
}; |
|||
|
|||
const handleError = (error: Error) => { |
|||
message.error('PDF显示出错:' + error.message); |
|||
}; |
|||
|
|||
|
|||
onMounted(() => { |
|||
loadPdf(); |
|||
}); |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.pdf-container { |
|||
width: 100%; |
|||
height: 100vh; |
|||
display: flex; |
|||
flex-direction: column; |
|||
background: #f0f2f5; |
|||
} |
|||
|
|||
.pdf-wrapper { |
|||
flex: 1; |
|||
overflow: auto; |
|||
padding: 20px; |
|||
display: flex; |
|||
justify-content: center; |
|||
background: #f0f2f5; |
|||
} |
|||
|
|||
.loading { |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
height: 100%; |
|||
font-size: 16px; |
|||
color: #666; |
|||
} |
|||
|
|||
.pdf-controls { |
|||
padding: 16px; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
background: #fff; |
|||
border-top: 1px solid #f0f0f0; |
|||
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.05); |
|||
} |
|||
|
|||
:deep(.vue3-pdf) { |
|||
background: white; |
|||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); |
|||
padding: 20px; |
|||
} |
|||
|
|||
:deep(.vue3-pdf canvas) { |
|||
max-width: 100%; |
|||
height: auto !important; |
|||
} |
|||
</style> |
@ -0,0 +1,149 @@ |
|||
<template> |
|||
<!-- 添加 col-span-2 和高度样式 --> |
|||
<div class="tabs-container"> |
|||
<Tabs v-model:activeKey="activeKey" type="card"> |
|||
<TabPane key="1" tab="审查结果"> |
|||
<div class="scroll-container"> |
|||
<div class="card-container"> |
|||
<Card |
|||
v-for="(item, index) in props.cardList" |
|||
:key="index" |
|||
:class="['custom-card', { 'card-selected': selectedCard === item.title }]" |
|||
@click="handleCardClick(item.title)" |
|||
> |
|||
<template #title>{{ item.title }}</template> |
|||
{{ item.text }} |
|||
{{ item.content }} |
|||
</Card> |
|||
</div> |
|||
</div> |
|||
</TabPane> |
|||
<!-- <TabPane key="2" tab="Tab 2">Content of Tab Pane 2</TabPane> |
|||
<TabPane key="3" tab="Tab 3">Content of Tab Pane 3</TabPane> --> |
|||
</Tabs> |
|||
</div> |
|||
</template> |
|||
<script lang="ts" setup> |
|||
import { ref, onMounted, reactive } from 'vue'; |
|||
|
|||
import { Tabs, TabPane, Card } from 'ant-design-vue'; |
|||
// Props 定义 |
|||
interface Props { |
|||
cardList:any[]; |
|||
} |
|||
const props = defineProps<Props>(); |
|||
const activeKey = ref('1'); |
|||
// 卡片数据 |
|||
const selectedCard = ref(''); // 用于存储当前选中的卡片标题 |
|||
const handleCardClick = (title) => { |
|||
selectedCard.value = title; |
|||
console.log('点击的卡片标题:', title); |
|||
}; |
|||
|
|||
// 初始化数据 |
|||
onMounted(() => { |
|||
|
|||
}); |
|||
</script> |
|||
<style scoped> |
|||
/* Tabs 相关样式 */ |
|||
/* 设置未选中标签的基本样式 */ |
|||
:deep(.ant-tabs-card > .ant-tabs-nav .ant-tabs-tab) { |
|||
color: rgba(0, 0, 0, 0.85) !important; /* 文字颜色为黑色,带透明度 */ |
|||
background: #fff; /* 背景色为白色 */ |
|||
border: 1px solid #f0f0f0; /* 浅灰色边框 */ |
|||
} |
|||
|
|||
/* 设置选中标签的样式 */ |
|||
:deep(.ant-tabs-card > .ant-tabs-nav .ant-tabs-tab-active) { |
|||
color: #ffffff !important; /* 文字颜色为白色 */ |
|||
background: #1890ff !important; /* 背景色为蓝色 */ |
|||
border-color: #1890ff !important; /* 边框颜色也改为蓝色 */ |
|||
} |
|||
|
|||
/* 确保选中标签的文字颜色为白色 */ |
|||
:deep(.ant-tabs-card > .ant-tabs-nav .ant-tabs-tab-active .ant-tabs-tab-btn) { |
|||
color: #ffffff !important; /* 文字颜色强制设为白色 */ |
|||
} |
|||
|
|||
/* 鼠标悬停时的文字颜色效果 */ |
|||
:deep(.ant-tabs-card > .ant-tabs-nav .ant-tabs-tab:hover .ant-tabs-tab-btn) { |
|||
color: #1890ff !important; /* 悬停时文字变为蓝色 */ |
|||
} |
|||
|
|||
/* Card 相关样式 */ |
|||
/* 卡片容器布局设置 */ |
|||
.card-container { |
|||
display: flex; |
|||
gap: 16px; |
|||
flex-wrap: wrap; |
|||
width: 100%; |
|||
} |
|||
|
|||
/* 单个卡片的基本样式 */ |
|||
.custom-card { |
|||
width: 100%; |
|||
margin-bottom: 16px; |
|||
cursor: pointer; |
|||
transition: all 0.3s; |
|||
background-color: #fff; /* 添加默认背景色 */ |
|||
border: 1px solid #e8e8e8; /* 添加默认边框 */ |
|||
} |
|||
|
|||
/* 卡片选中状态 */ |
|||
.custom-card.card-selected { |
|||
background-color: #e6f4ff !important; /* 选中时的淡蓝色背景 */ |
|||
border: 1px solid #1890ff !important; /* 选中时的蓝色边框 */ |
|||
} |
|||
|
|||
/* 卡片悬停效果 */ |
|||
.custom-card:hover { |
|||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); |
|||
transform: translateY(-2px); |
|||
} |
|||
|
|||
/* 选中状态下的悬停效果 */ |
|||
.custom-card.card-selected:hover { |
|||
background-color: #e6f4ff !important; |
|||
box-shadow: 0 4px 12px rgba(24, 144, 255, 0.1); |
|||
} |
|||
|
|||
/* 卡片点击效果 */ |
|||
.custom-card:active { |
|||
transform: translateY(0); |
|||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); |
|||
} |
|||
|
|||
/* 修改卡片内容区域的内边距 */ |
|||
:deep(.ant-card-body) { |
|||
padding: 16px; |
|||
} |
|||
|
|||
/* 滚动容器样式 */ |
|||
.scroll-container { |
|||
height: calc(100vh - 25vh); /* 可以根据实际需要调整高度 */ |
|||
overflow-y: auto; /* 添加垂直滚动条 */ |
|||
padding: 16px; |
|||
} |
|||
|
|||
/* 美化滚动条样式(可选) */ |
|||
.scroll-container::-webkit-scrollbar { |
|||
width: 6px; /* 滚动条宽度 */ |
|||
} |
|||
|
|||
.scroll-container::-webkit-scrollbar-thumb { |
|||
background-color: #ccc; /* 滚动条颜色 */ |
|||
border-radius: 3px; /* 滚动条圆角 */ |
|||
} |
|||
|
|||
.scroll-container::-webkit-scrollbar-track { |
|||
background-color: #f1f1f1; /* 滚动条轨道颜色 */ |
|||
} |
|||
|
|||
/* 保持原有的卡片容器样式 */ |
|||
.card-container { |
|||
display: flex; |
|||
gap: 16px; |
|||
flex-wrap: wrap; |
|||
} |
|||
</style> |
@ -0,0 +1,71 @@ |
|||
<template> |
|||
<BasicModal |
|||
v-bind="$attrs" |
|||
:title="title" |
|||
:canFullscreen="true" |
|||
:defaultFullscreen="true" |
|||
:showOkBtn="false" |
|||
@register="registerInnerModal" |
|||
> |
|||
<ContractualTasksTable |
|||
:showAdd="false" |
|||
:groupId="groupId" |
|||
:batchName="batchName" |
|||
:resultType="resultType" |
|||
:key="randomKey" |
|||
/> |
|||
</BasicModal> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { BasicModal, useModalInner } from '@/components/Modal'; |
|||
import { BasicForm, useForm } from '@/components/Form'; |
|||
import { computed, ref, unref } from 'vue'; |
|||
import { |
|||
JyjcontractualTaskBatchInfo, |
|||
JyjcontractualTaskBatchAdd, |
|||
JyjcontractualTaskBatchUpdate, |
|||
} from '@/api/contractReview/JyjcontractualTaskBatch'; |
|||
import { modalSchemas } from './JyjcontractualTaskBatch.data'; |
|||
import ContractualTasksTable from '@/views/contractReview/ContractualTasks/ContractualTasksTable.vue'; |
|||
defineOptions({ name: 'ContractualTasksTableModal' }); |
|||
// 定义随机 key 的生成函数 |
|||
const randomKey = ref('1'); |
|||
|
|||
const emit = defineEmits(['register', 'reload']); |
|||
const title = ref(''); |
|||
const resultType = ref(''); |
|||
const batchName = ref(''); |
|||
const groupId = ref(''); |
|||
const isUpdate = ref<boolean>(false); |
|||
|
|||
const [registerInnerModal, { modalLoading, closeModal }] = useModalInner( |
|||
async (data: { record?: Recordable; type?: string }) => { |
|||
modalLoading(true); |
|||
const { record, type } = data; |
|||
if (type == 'passCount') { |
|||
title.value = '查看审核通过记录'; |
|||
resultType.value = '审查合格'; |
|||
} else if (type == 'rejectCount') { |
|||
title.value = '查看审核不通过记录'; |
|||
resultType.value = '审查不合格'; |
|||
} else if(type=="irrelevantCount"){ |
|||
title.value = '查看非审查范围记录'; |
|||
resultType.value = '非审查范围文件'; |
|||
}else{ |
|||
title.value = '查看审查失败的范围记录'; |
|||
resultType.value = '审查失败文件'; |
|||
} |
|||
batchName.value = record.batchName; |
|||
groupId.value = record.id; |
|||
console.log(batchName.value, groupId.value); |
|||
const generateRandomKey = () => Math.random().toString(36).substr(2, 9); |
|||
|
|||
// 定义响应式变量 |
|||
randomKey.value = generateRandomKey(); // 初始化随机 key |
|||
modalLoading(false); |
|||
}, |
|||
); |
|||
</script> |
|||
|
|||
<style scoped></style> |
@ -0,0 +1,109 @@ |
|||
import { BasicColumn } from '@/components/Table'; |
|||
import { FormSchema } from '@/components/Form'; |
|||
import { getDictOptions } from '@/utils/dict'; |
|||
import { useRender } from '@/hooks/component/useRender'; |
|||
import { uploadContractual } from '@/api/contractReview/JyjcontractualTaskBatch'; |
|||
|
|||
export const formSchemas: FormSchema[] = [ |
|||
{ |
|||
label: '文档名称', |
|||
field: 'documentName', |
|||
component: 'Input', |
|||
}, |
|||
{ |
|||
label: '批次名称', |
|||
field: 'batchName', |
|||
component: 'Input', |
|||
}, |
|||
{ |
|||
label: '进度状态', |
|||
field: 'progressStatus', |
|||
component: 'Select', |
|||
componentProps: { |
|||
options: getDictOptions('document_task_status'), |
|||
}, |
|||
}, |
|||
]; |
|||
|
|||
const { renderDict } = useRender(); |
|||
export const columns: BasicColumn[] = [ |
|||
{ |
|||
title: '文件名称', |
|||
dataIndex: 'documentName', |
|||
}, |
|||
{ |
|||
title: '批次名称', |
|||
dataIndex: 'batchName', |
|||
}, |
|||
{ |
|||
title: '合同总数', |
|||
dataIndex: 'totalContracts', |
|||
}, |
|||
{ |
|||
title: '已审批总数', |
|||
dataIndex: 'approvedCount', |
|||
|
|||
}, |
|||
{ |
|||
title: '审核通过数量', |
|||
dataIndex: 'passCount', |
|||
width: 120, |
|||
|
|||
}, |
|||
{ |
|||
title: '审核不通过数量', |
|||
dataIndex: 'rejectCount', |
|||
width: 130, |
|||
|
|||
}, |
|||
{ |
|||
title: '非审查范围数量', |
|||
dataIndex: 'irrelevantCount', |
|||
width: 130, |
|||
}, |
|||
{ |
|||
title: '审核失败数量', |
|||
dataIndex: 'failCount', |
|||
width: 100, |
|||
}, |
|||
{ |
|||
title: '进度(百分比)', |
|||
dataIndex: 'progress', |
|||
}, |
|||
{ |
|||
title: '处理时间', |
|||
dataIndex: 'processingTime', |
|||
}, |
|||
{ |
|||
title: '状态', |
|||
dataIndex: 'progressStatus', |
|||
customRender: ({ value }) => renderDict(value, 'document_task_status'), |
|||
}, |
|||
]; |
|||
export const modalSchemas: FormSchema[] = [ |
|||
{ |
|||
label: '文件名称', |
|||
field: 'ossId', |
|||
required: true, |
|||
component: 'Upload', |
|||
componentProps: { |
|||
accept: ['.zip'], |
|||
maxSize: 500, |
|||
multiple: false, |
|||
resultField: 'ossId', |
|||
api: uploadContractual, |
|||
}, |
|||
}, |
|||
{ |
|||
label: '批次名称', |
|||
field: 'batchName', |
|||
required: false, |
|||
component: 'Input', |
|||
}, |
|||
{ |
|||
label: '备注', |
|||
field: 'remark', |
|||
required: false, |
|||
component: 'InputTextArea', |
|||
}, |
|||
]; |
@ -0,0 +1,74 @@ |
|||
<template> |
|||
<BasicModal |
|||
v-bind="$attrs" |
|||
:title="title" |
|||
@register="registerInnerModal" |
|||
@ok="handleSubmit" |
|||
@cancel="resetForm" |
|||
> |
|||
<BasicForm @register="registerForm" /> |
|||
</BasicModal> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { BasicModal, useModalInner } from '@/components/Modal'; |
|||
import { BasicForm, useForm } from '@/components/Form'; |
|||
import { computed, ref, unref } from 'vue'; |
|||
import { JyjcontractualTaskBatchInfo, JyjcontractualTaskBatchAdd, JyjcontractualTaskBatchUpdate } from '@/api/contractReview/JyjcontractualTaskBatch'; |
|||
import { modalSchemas } from './JyjcontractualTaskBatch.data'; |
|||
|
|||
defineOptions({ name: 'JyjcontractualTaskBatchModal' }); |
|||
|
|||
const emit = defineEmits(['register', 'reload']); |
|||
|
|||
const isUpdate = ref<boolean>(false); |
|||
const title = computed<string>(() => { |
|||
return isUpdate.value ? '编辑合同批处理记录' : '新增合同批处理记录'; |
|||
}); |
|||
|
|||
const [registerInnerModal, { modalLoading, closeModal }] = useModalInner( |
|||
async (data: { record?: Recordable; update: boolean }) => { |
|||
modalLoading(true); |
|||
const { record, update } = data; |
|||
isUpdate.value = update; |
|||
if (update && record) { |
|||
const ret = await JyjcontractualTaskBatchInfo(record.id); |
|||
await setFieldsValue(ret); |
|||
} |
|||
modalLoading(false); |
|||
}, |
|||
); |
|||
|
|||
const [registerForm, { setFieldsValue, resetForm, validate }] = useForm({ |
|||
labelWidth: 100, |
|||
showActionButtonGroup: false, |
|||
baseColProps: { span: 24 }, |
|||
schemas: modalSchemas, |
|||
}); |
|||
|
|||
async function handleSubmit() { |
|||
try { |
|||
modalLoading(true); |
|||
const data = await validate(); |
|||
//修改 |
|||
if (unref(isUpdate)) { |
|||
data["ossId"]=data["ossId"][0] |
|||
await JyjcontractualTaskBatchUpdate(data); |
|||
} else { |
|||
//新增 |
|||
data["ossId"]=data["ossId"][0] |
|||
data["taskName"]="batchReview" |
|||
data["taskType"]="contract_review" |
|||
await JyjcontractualTaskBatchAdd(data); |
|||
} |
|||
emit('reload'); |
|||
closeModal(); |
|||
await resetForm(); |
|||
} catch (e) { |
|||
} finally { |
|||
modalLoading(false); |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped></style> |
@ -0,0 +1,200 @@ |
|||
<template> |
|||
<PageWrapper dense> |
|||
<BasicTable @register="registerTable"> |
|||
<template #toolbar> |
|||
<!-- <a-button |
|||
@click=" |
|||
downloadExcel( |
|||
JyjcontractualTaskBatchExport, |
|||
'合同批处理记录数据', |
|||
getForm().getFieldsValue(), |
|||
) |
|||
" |
|||
v-auth="'productManagement:JyjcontractualTaskBatch:export'" |
|||
>导出</a-button |
|||
> |
|||
<a-button |
|||
type="primary" |
|||
danger |
|||
@click="multipleRemove(JyjcontractualTaskBatchRemove)" |
|||
:disabled="!selected" |
|||
v-auth="'productManagement:JyjcontractualTaskBatch:remove'" |
|||
>删除</a-button |
|||
> --> |
|||
<a-button |
|||
type="primary" |
|||
@click="handleAdd" |
|||
v-auth="'productManagement:JyjcontractualTaskBatch:add'" |
|||
>新增</a-button |
|||
> |
|||
</template> |
|||
<template #bodyCell="{ column, record }"> |
|||
<template v-if="column.key === 'passCount'"> |
|||
<div class="grid grid-flow-row auto-rows-max"> |
|||
<div> {{ record.passCount }}</div> |
|||
<div><TableAction |
|||
stopButtonPropagation |
|||
:actions="[ |
|||
{ |
|||
label: '查看', |
|||
icon: IconEnum.PREVIEW, |
|||
type: 'primary', |
|||
color:'success', |
|||
ifShow:()=>{ |
|||
if(record.passCount&&record.passCount>0){ |
|||
return true |
|||
}else{ |
|||
return false |
|||
} |
|||
}, |
|||
ghost: true, |
|||
onClick: handlePreview.bind(null, record,'passCount'), |
|||
}, |
|||
]" |
|||
/></div> |
|||
</div> |
|||
</template> |
|||
<template v-if="column.key === 'rejectCount'"> |
|||
<div class="grid grid-flow-row auto-rows-max"> |
|||
<div> {{ record.rejectCount }}</div> |
|||
<div><TableAction |
|||
stopButtonPropagation |
|||
:actions="[ |
|||
{ |
|||
label: '查看', |
|||
icon: IconEnum.PREVIEW, |
|||
type: 'primary', |
|||
color:'error', |
|||
ifShow:()=>{ |
|||
if(record.rejectCount&&record.rejectCount>0){ |
|||
return true |
|||
}else{ |
|||
return false |
|||
} |
|||
}, |
|||
ghost: true, |
|||
onClick: handlePreview.bind(null, record,'rejectCount'), |
|||
}, |
|||
]" |
|||
/></div> |
|||
</div> |
|||
</template> |
|||
<template v-if="column.key === 'irrelevantCount'"> |
|||
<div class="grid grid-flow-row auto-rows-max"> |
|||
<div> {{ record.irrelevantCount }}</div> |
|||
<div><TableAction |
|||
stopButtonPropagation |
|||
:actions="[ |
|||
{ |
|||
label: '查看', |
|||
icon: IconEnum.PREVIEW, |
|||
type: 'primary', |
|||
color:'warning', |
|||
ifShow:()=>{ |
|||
if(record.irrelevantCount&&record.irrelevantCount>0){ |
|||
return true |
|||
}else{ |
|||
return false |
|||
} |
|||
}, |
|||
ghost: true, |
|||
onClick: handlePreview.bind(null, record,'irrelevantCount'), |
|||
}, |
|||
]" |
|||
/></div> |
|||
</div> |
|||
</template> |
|||
<template v-if="column.key === 'failCount'"> |
|||
<div class="grid grid-flow-row auto-rows-max"> |
|||
<div> {{ record.failCount }}</div> |
|||
<div><TableAction |
|||
:actions="[ |
|||
{ |
|||
label: '查看', |
|||
icon: IconEnum.PREVIEW, |
|||
type: 'primary', |
|||
ifShow:()=>{ |
|||
if(record.failCount&&record.failCount>0){ |
|||
return true |
|||
}else{ |
|||
return false |
|||
} |
|||
}, |
|||
ghost: true, |
|||
onClick: handlePreview.bind(null, record,'failCount'), |
|||
}, |
|||
]" |
|||
/></div> |
|||
</div> |
|||
</template> |
|||
</template> |
|||
</BasicTable> |
|||
<JyjcontractualTaskBatchModal @register="registerModal" @reload="reload" /> |
|||
<ContractualTasksTableModal @register="registerTableModal"/> |
|||
</PageWrapper> |
|||
</template> |
|||
|
|||
<script setup lang="ts"> |
|||
import { PageWrapper } from '@/components/Page'; |
|||
import { BasicTable, useTable, TableAction } from '@/components/Table'; |
|||
import { |
|||
JyjcontractualTaskBatchList, |
|||
JyjcontractualTaskBatchExport, |
|||
JyjcontractualTaskBatchRemove, |
|||
} from '@/api/contractReview/JyjcontractualTaskBatch'; |
|||
import { downloadExcel } from '@/utils/file/download'; |
|||
import { useModal } from '@/components/Modal'; |
|||
import JyjcontractualTaskBatchModal from './JyjcontractualTaskBatchModal.vue'; |
|||
import ContractualTasksTableModal from './ContractualTasksTableModal.vue' |
|||
import { formSchemas, columns } from './JyjcontractualTaskBatch.data'; |
|||
import { IconEnum } from '@/enums/appEnum'; |
|||
|
|||
defineOptions({ name: 'JyjcontractualTaskBatch' }); |
|||
|
|||
const [registerTable, { reload, multipleRemove, selected, getForm }] = useTable({ |
|||
rowSelection: { |
|||
type: 'checkbox', |
|||
}, |
|||
title: '合同批处理记录列表', |
|||
api: JyjcontractualTaskBatchList, |
|||
showIndexColumn: false, |
|||
rowKey: 'id', |
|||
useSearchForm: true, |
|||
formConfig: { |
|||
schemas: formSchemas, |
|||
baseColProps: { |
|||
xs: 24, |
|||
sm: 24, |
|||
md: 24, |
|||
lg: 6, |
|||
}, |
|||
}, |
|||
columns: columns, |
|||
actionColumn: { |
|||
ifShow:false, |
|||
width: 200, |
|||
title: '操作', |
|||
key: 'action', |
|||
fixed: 'right', |
|||
}, |
|||
}); |
|||
|
|||
const [registerModal, { openModal }] = useModal(); |
|||
const [registerTableModal,{openModal:openTableModal}]= useModal(); |
|||
function handleEdit(record: Recordable) { |
|||
openModal(true, { record, update: true }); |
|||
} |
|||
|
|||
function handleAdd() { |
|||
openModal(true, { update: false }); |
|||
} |
|||
function handlePreview(record: Recordable,type:string){ |
|||
openTableModal(true,{record,type:type}) |
|||
} |
|||
async function handleDelete(record: Recordable) { |
|||
await JyjcontractualTaskBatchRemove([record.id]); |
|||
await reload(); |
|||
} |
|||
</script> |
|||
|
|||
<style scoped></style> |
@ -0,0 +1,251 @@ |
|||
<template> |
|||
<PageWrapper dense> |
|||
<div class="grid grid-cols-5 gap-4"> |
|||
<div class="col-span-3"> |
|||
<CanvasEditor |
|||
ref="canvasEditor" |
|||
:parentContent="parentContent" |
|||
:view="view" |
|||
@save-content="handleSaveCanvasEditorContent" |
|||
/> |
|||
</div> |
|||
<div class="col-span-2"> |
|||
<!-- 添加 col-span-2 和高度样式 --> |
|||
<div class="tabs-container"> |
|||
<Tabs v-model:activeKey="activeKey" type="card"> |
|||
<TabPane key="1" tab="Tab 1"> |
|||
<div class="scroll-container"> |
|||
<div class="card-container"> |
|||
<Card |
|||
v-for="(item, index) in cardList" |
|||
:key="index" |
|||
:class="['custom-card', { 'card-selected': selectedCard === item.title }]" |
|||
@click="handleCardClick(item.title)" |
|||
> |
|||
<template #title>{{ item.title }}</template> |
|||
{{ item.content }} |
|||
</Card> |
|||
</div> |
|||
</div> |
|||
</TabPane> |
|||
<TabPane key="2" tab="Tab 2">Content of Tab Pane 2</TabPane> |
|||
<TabPane key="3" tab="Tab 3">Content of Tab Pane 3</TabPane> |
|||
</Tabs> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</PageWrapper> |
|||
</template> |
|||
<script lang="ts" setup> |
|||
import { ref, onMounted, reactive } from 'vue'; |
|||
import { PageWrapper } from '@/components/Page'; |
|||
import WorkbenchHeader from './components/WorkbenchHeader.vue'; |
|||
import { Tabs, TabPane, Card, Dropdown, Menu, MenuItem } from 'ant-design-vue'; |
|||
import ProjectCard from './components/ProjectCard.vue'; |
|||
import QuickNav from './components/QuickNav.vue'; |
|||
import DocumentTasksTable from '@/views/documentReview/DocumentTasks/DocumentTasksTable.vue'; |
|||
import { useRouter } from 'vue-router'; |
|||
import { DownOutlined, UserOutlined } from '@ant-design/icons-vue'; |
|||
import { groupItems } from './components/data'; |
|||
import CanvasEditor from '@/views/CanvasEditor/index.vue'; |
|||
const activeKey = ref('1'); |
|||
// 卡片数据 |
|||
const cardList = ref([ |
|||
{ title: '标题1', content: '内容1' }, |
|||
{ title: '标题2', content: '内容2' }, |
|||
{ title: '标题3', content: '内容3' }, |
|||
{ title: '标题1', content: '内容1' }, |
|||
{ title: '标题2', content: '内容2' }, |
|||
{ title: '标题3', content: '内容3' }, |
|||
{ title: '标题1', content: '内容1' }, |
|||
{ title: '标题2', content: '内容2' }, |
|||
{ title: '标题3', content: '内容3' }, |
|||
{ title: '标题1', content: '内容1' }, |
|||
{ title: '标题2', content: '内容2' }, |
|||
{ title: '标题3', content: '内容3' }, |
|||
{ title: '标题1', content: '内容1' }, |
|||
{ title: '标题2', content: '内容2' }, |
|||
{ title: '标题3', content: '内容3' }, |
|||
{ title: '标题1', content: '内容1' }, |
|||
{ title: '标题2', content: '内容2' }, |
|||
{ title: '标题3', content: '内容3' }, |
|||
]); |
|||
const selectedCard = ref(''); // 用于存储当前选中的卡片标题 |
|||
const handleCardClick = (title) => { |
|||
selectedCard.value = title; |
|||
console.log('点击的卡片标题:', title); |
|||
}; |
|||
// 存放父组件传递的数据 |
|||
let parentContent = reactive<any>(undefined); |
|||
// 存放子组件数据 |
|||
const content = ref<any>(undefined); |
|||
// 标识符 |
|||
const view = ref<string | undefined>(undefined); |
|||
|
|||
// 初始化数据 |
|||
onMounted(() => { |
|||
console.log('模拟父组件向后端请求数据, 传递给子组件'); |
|||
getEditorContent(); |
|||
view.value = 'parent'; |
|||
}); |
|||
|
|||
// 模拟后端获取数据的方法 |
|||
const getEditorContent = () => { |
|||
parentContent = { |
|||
header: [ |
|||
{ |
|||
value: '', |
|||
size: 12, |
|||
bold: false, |
|||
color: 'rgb(33, 53, 71)', |
|||
italic: false, |
|||
}, |
|||
], |
|||
main: [ |
|||
{ |
|||
value: |
|||
'父类传递的数据 通过后端获取!\n甲方:企查查科技股份有限公司\n法定代表人:陈德强\n住所:苏州工业园区科创东区东石泾港路2号润港产业园6号楼10层1001室11', |
|||
size: 10, |
|||
bold: false, |
|||
}, |
|||
], |
|||
}; |
|||
}; |
|||
|
|||
// 组件引用 |
|||
const canvasEditor = ref<InstanceType<typeof CanvasEditor> | null>(null); |
|||
|
|||
// 保存内容方法 |
|||
const handleSaveContent = () => { |
|||
// 访问子组件的方法 |
|||
(canvasEditor.value as any).saveContent(); |
|||
}; |
|||
|
|||
// 处理子组件传递的数据 |
|||
const handleSaveCanvasEditorContent = (data: any) => { |
|||
console.log('从子组件接收到的数据:', data); |
|||
// 将data数据转换为 json 格式的数据, 方便入库处理 |
|||
content.value = JSON.stringify(data); |
|||
console.log('转换后的数据 content 为: ', content.value); |
|||
}; |
|||
|
|||
const router = useRouter(); |
|||
// 状态定义 |
|||
const loading = ref(false); |
|||
const handleMenuClick = ({ key }) => { |
|||
console.log(key); |
|||
router.push({ path: key }); |
|||
}; |
|||
// 获取任务列表 |
|||
const fetchTaskList = async () => {}; |
|||
|
|||
// 页面加载时获取数据 |
|||
onMounted(() => { |
|||
fetchTaskList(); |
|||
}); |
|||
setTimeout(() => { |
|||
loading.value = false; |
|||
}, 1500); |
|||
</script> |
|||
<style scoped> |
|||
/* Tabs 相关样式 */ |
|||
/* 设置未选中标签的基本样式 */ |
|||
:deep(.ant-tabs-card > .ant-tabs-nav .ant-tabs-tab) { |
|||
color: rgba(0, 0, 0, 0.85) !important; /* 文字颜色为黑色,带透明度 */ |
|||
background: #fff; /* 背景色为白色 */ |
|||
border: 1px solid #f0f0f0; /* 浅灰色边框 */ |
|||
} |
|||
|
|||
/* 设置选中标签的样式 */ |
|||
:deep(.ant-tabs-card > .ant-tabs-nav .ant-tabs-tab-active) { |
|||
color: #ffffff !important; /* 文字颜色为白色 */ |
|||
background: #1890ff !important; /* 背景色为蓝色 */ |
|||
border-color: #1890ff !important; /* 边框颜色也改为蓝色 */ |
|||
} |
|||
|
|||
/* 确保选中标签的文字颜色为白色 */ |
|||
:deep(.ant-tabs-card > .ant-tabs-nav .ant-tabs-tab-active .ant-tabs-tab-btn) { |
|||
color: #ffffff !important; /* 文字颜色强制设为白色 */ |
|||
} |
|||
|
|||
/* 鼠标悬停时的文字颜色效果 */ |
|||
:deep(.ant-tabs-card > .ant-tabs-nav .ant-tabs-tab:hover .ant-tabs-tab-btn) { |
|||
color: #1890ff !important; /* 悬停时文字变为蓝色 */ |
|||
} |
|||
|
|||
/* Card 相关样式 */ |
|||
/* 卡片容器布局设置 */ |
|||
.card-container { |
|||
display: flex; |
|||
gap: 16px; |
|||
flex-wrap: wrap; |
|||
width: 100%; |
|||
} |
|||
|
|||
/* 单个卡片的基本样式 */ |
|||
.custom-card { |
|||
width: 100%; |
|||
margin-bottom: 16px; |
|||
cursor: pointer; |
|||
transition: all 0.3s; |
|||
background-color: #fff; /* 添加默认背景色 */ |
|||
border: 1px solid #e8e8e8; /* 添加默认边框 */ |
|||
} |
|||
|
|||
/* 卡片选中状态 */ |
|||
.custom-card.card-selected { |
|||
background-color: #e6f4ff !important; /* 选中时的淡蓝色背景 */ |
|||
border: 1px solid #1890ff !important; /* 选中时的蓝色边框 */ |
|||
} |
|||
|
|||
/* 卡片悬停效果 */ |
|||
.custom-card:hover { |
|||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); |
|||
transform: translateY(-2px); |
|||
} |
|||
|
|||
/* 选中状态下的悬停效果 */ |
|||
.custom-card.card-selected:hover { |
|||
background-color: #e6f4ff !important; |
|||
box-shadow: 0 4px 12px rgba(24, 144, 255, 0.1); |
|||
} |
|||
|
|||
/* 卡片点击效果 */ |
|||
.custom-card:active { |
|||
transform: translateY(0); |
|||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); |
|||
} |
|||
|
|||
/* 修改卡片内容区域的内边距 */ |
|||
:deep(.ant-card-body) { |
|||
padding: 16px; |
|||
} |
|||
|
|||
/* 滚动容器样式 */ |
|||
.scroll-container { |
|||
height: calc(100vh - 25vh); /* 可以根据实际需要调整高度 */ |
|||
overflow-y: auto; /* 添加垂直滚动条 */ |
|||
padding: 16px; |
|||
} |
|||
|
|||
/* 美化滚动条样式(可选) */ |
|||
.scroll-container::-webkit-scrollbar { |
|||
width: 6px; /* 滚动条宽度 */ |
|||
} |
|||
|
|||
.scroll-container::-webkit-scrollbar-thumb { |
|||
background-color: #ccc; /* 滚动条颜色 */ |
|||
border-radius: 3px; /* 滚动条圆角 */ |
|||
} |
|||
|
|||
.scroll-container::-webkit-scrollbar-track { |
|||
background-color: #f1f1f1; /* 滚动条轨道颜色 */ |
|||
} |
|||
|
|||
/* 保持原有的卡片容器样式 */ |
|||
.card-container { |
|||
display: flex; |
|||
gap: 16px; |
|||
flex-wrap: wrap; |
|||
} |
|||
</style> |
Loading…
Reference in new issue