470 lines
18 KiB
TypeScript
470 lines
18 KiB
TypeScript
import type { MetaFunction, LoaderFunctionArgs } from '@remix-run/node';
|
||
import { useLoaderData, useNavigate } from '@remix-run/react';
|
||
import { useState } from 'react';
|
||
import styles from '~/styles/pages/contract-template.css?url';
|
||
|
||
// 导入FilePreview组件
|
||
import { FilePreview } from '~/components/reviews';
|
||
|
||
export const links = () => [
|
||
{ rel: 'stylesheet', href: styles },
|
||
// 添加专门的样式隔离
|
||
{
|
||
rel: 'stylesheet',
|
||
href: 'data:text/css;base64,' + btoa(`
|
||
.file-preview-isolation {
|
||
all: unset !important;
|
||
display: block !important;
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important;
|
||
font-size: 14px !important;
|
||
line-height: 1.5 !important;
|
||
color: #333 !important;
|
||
background: #fff !important;
|
||
width: 100% !important;
|
||
min-height: 600px !important;
|
||
position: relative !important;
|
||
isolation: isolate !important;
|
||
contain: layout style !important;
|
||
}
|
||
.file-preview-isolation * {
|
||
font-family: inherit !important;
|
||
box-sizing: border-box !important;
|
||
}
|
||
.file-preview-isolation .file-preview-header {
|
||
display: flex !important;
|
||
justify-content: space-between !important;
|
||
align-items: center !important;
|
||
padding: 8px 16px !important;
|
||
background: #f8f9fa !important;
|
||
border-bottom: 1px solid #dee2e6 !important;
|
||
font-size: 14px !important;
|
||
line-height: 1.5 !important;
|
||
max-width: none !important;
|
||
width: 100% !important;
|
||
text-overflow: unset !important;
|
||
white-space: nowrap !important;
|
||
overflow: visible !important;
|
||
}
|
||
.file-preview-isolation .file-preview-actions {
|
||
display: flex !important;
|
||
align-items: center !important;
|
||
gap: 8px !important;
|
||
}
|
||
.file-preview-isolation button,
|
||
.file-preview-isolation .ant-btn {
|
||
padding: 4px 8px !important;
|
||
margin: 0 2px !important;
|
||
border: 1px solid #d9d9d9 !important;
|
||
border-radius: 4px !important;
|
||
background: white !important;
|
||
color: #333 !important;
|
||
font-size: 12px !important;
|
||
line-height: 1.4 !important;
|
||
cursor: pointer !important;
|
||
display: inline-flex !important;
|
||
align-items: center !important;
|
||
justify-content: center !important;
|
||
min-height: 24px !important;
|
||
text-decoration: none !important;
|
||
white-space: nowrap !important;
|
||
vertical-align: middle !important;
|
||
}
|
||
.file-preview-isolation input,
|
||
.file-preview-isolation .ant-input {
|
||
padding: 4px 8px !important;
|
||
border: 1px solid #d9d9d9 !important;
|
||
border-radius: 4px !important;
|
||
background: white !important;
|
||
color: #333 !important;
|
||
font-size: 12px !important;
|
||
line-height: 1.4 !important;
|
||
min-height: 24px !important;
|
||
width: auto !important;
|
||
max-width: 40px !important;
|
||
text-align: center !important;
|
||
outline: none !important;
|
||
}
|
||
.file-preview-isolation .file-preview-content {
|
||
max-height: calc(100vh - 150px) !important;
|
||
overflow: auto !important;
|
||
background: #f8f9fa !important;
|
||
padding: 0 !important;
|
||
margin: 0 !important;
|
||
}
|
||
`)
|
||
}
|
||
];
|
||
|
||
export const meta: MetaFunction<typeof loader> = ({ data }) => {
|
||
return [
|
||
{ title: `${data?.template.title || '合同模板详情'} - 智慧法务` },
|
||
{
|
||
name: 'description',
|
||
content: data?.template.description || '查看合同模板详细信息'
|
||
}
|
||
];
|
||
};
|
||
|
||
// 面包屑导航配置
|
||
export const handle = {
|
||
breadcrumb: (data: { template: { title: string } }) => {
|
||
return data?.template?.title || "模板详情";
|
||
}
|
||
};
|
||
|
||
// 模拟详细数据
|
||
const getTemplateDetail = (id: string) => {
|
||
const templates = {
|
||
'1': {
|
||
id: '1',
|
||
title: '烟草产品销售合同(2023版)',
|
||
type: '销售合同 · 标准版',
|
||
description: '本模板是专为烟草行业设计的标准销售合同,严格遵循《烟草专卖法》等相关法律法规,涵盖了烟草产品销售过程中的各个关键环节。',
|
||
updateTime: '2023年10月25日',
|
||
useCount: 2156,
|
||
rating: 4.9,
|
||
fileSize: '245KB',
|
||
scope: '烟草产品销售',
|
||
legalBasis: '《合同法》《烟草专卖法》',
|
||
templateCode: 'XS-2023-001',
|
||
reviews: [
|
||
{
|
||
user: '李经理',
|
||
rating: 5,
|
||
comment: '模板非常专业,条款完整,符合行业规范。我们公司一直在使用这个模板,效果很好。',
|
||
date: '2023-10-20'
|
||
},
|
||
{
|
||
user: '王总',
|
||
rating: 4,
|
||
comment: '模板结构清晰,易于理解和使用。特别是违约责任条款写得很详细,对我们很有帮助。',
|
||
date: '2023-10-18'
|
||
}
|
||
],
|
||
features: [
|
||
{ title: '法律合规', description: '严格遵循烟草行业法律法规,确保合同条款合法有效', icon: 'ri-shield-check-line', color: 'green' },
|
||
{ title: '条款完整', description: '涵盖销售全流程,条款结构完整,逻辑清晰', icon: 'ri-settings-3-line', color: 'blue' },
|
||
{ title: '易于定制', description: '模板化设计,可根据具体业务需求灵活调整', icon: 'ri-edit-line', color: 'purple' },
|
||
{ title: '行业标准', description: '符合烟草行业标准,被广泛使用和认可', icon: 'ri-award-line', color: 'orange' }
|
||
],
|
||
structure: [
|
||
{ step: 1, title: '合同主体', description: '甲乙双方基本信息、资质证明' },
|
||
{ step: 2, title: '标的物条款', description: '产品名称、规格、数量、质量标准' },
|
||
{ step: 3, title: '价格与付款', description: '价格条款、付款方式、结算周期' },
|
||
{ step: 4, title: '交付与验收', description: '交付时间、地点、方式、验收标准' },
|
||
{ step: 5, title: '违约责任', description: '违约情形、责任承担、损失赔偿' },
|
||
{ step: 6, title: '争议解决', description: '争议处理方式、管辖法院' }
|
||
],
|
||
preview: `
|
||
中文合同预览内容...
|
||
|
||
烟草产品销售合同
|
||
合同编号:_______________
|
||
|
||
甲方(销售方):_________________________
|
||
地址:_____________________________________
|
||
法定代表人:_______________ 联系电话:_______________
|
||
烟草专卖许可证号:_________________________
|
||
|
||
乙方(采购方):_________________________
|
||
地址:_____________________________________
|
||
法定代表人:_______________ 联系电话:_______________
|
||
烟草专卖零售许可证号:_____________________
|
||
|
||
根据《中华人民共和国合同法》、《中华人民共和国烟草专卖法》等相关法律法规,
|
||
甲乙双方在平等、自愿、公平、诚信的基础上,就烟草产品销售事宜达成如下协议:
|
||
|
||
第一条 标的物
|
||
1.1 产品名称:_________________________
|
||
1.2 产品规格:_________________________
|
||
1.3 产品数量:_________________________
|
||
1.4 产品单价:_________________________
|
||
1.5 合同总金额:_______________________
|
||
|
||
... 更多条款内容请下载完整模板查看 ...
|
||
`
|
||
}
|
||
};
|
||
|
||
return templates[id as keyof typeof templates] || null;
|
||
};
|
||
|
||
export async function loader({ params }: LoaderFunctionArgs) {
|
||
const template = getTemplateDetail(params.id!);
|
||
|
||
if (!template) {
|
||
throw new Response('模板未找到', { status: 404 });
|
||
}
|
||
|
||
return { template };
|
||
}
|
||
|
||
export default function ContractTemplateDetail() {
|
||
const { template } = useLoaderData<typeof loader>();
|
||
const navigate = useNavigate();
|
||
const [isFavorited, setIsFavorited] = useState(false);
|
||
|
||
const handleBack = () => {
|
||
navigate(-1);
|
||
};
|
||
|
||
const handleDownload = () => {
|
||
console.log('下载模板:', template.id);
|
||
// 这里应该是实际的下载逻辑
|
||
};
|
||
|
||
const handlePreview = () => {
|
||
console.log('预览模板:', template.id);
|
||
// 这里应该打开预览模态框或新页面
|
||
};
|
||
|
||
const handleFavorite = () => {
|
||
setIsFavorited(!isFavorited);
|
||
console.log('收藏状态:', !isFavorited);
|
||
};
|
||
|
||
const handleShare = () => {
|
||
console.log('分享模板:', template.id);
|
||
// 这里应该是分享功能
|
||
};
|
||
|
||
const renderStars = (rating: number) => {
|
||
const stars = [];
|
||
const fullStars = Math.floor(rating);
|
||
|
||
for (let i = 0; i < 5; i++) {
|
||
if (i < fullStars) {
|
||
stars.push(<i key={i} className="ri-star-fill"></i>);
|
||
} else {
|
||
stars.push(<i key={i} className="ri-star-line"></i>);
|
||
}
|
||
}
|
||
return stars;
|
||
};
|
||
|
||
// 创建文件内容对象用于FilePreview组件
|
||
const fileContent = {
|
||
title: template.title,
|
||
contractNumber: template.templateCode,
|
||
// 设置PDF路径,FilePreview会自动拼接基础URL
|
||
path: 'contract-template/买卖合同/买卖合同范本.pdf',
|
||
parties: {
|
||
partyA: {
|
||
name: '',
|
||
address: '',
|
||
representative: '',
|
||
phone: ''
|
||
},
|
||
partyB: {
|
||
name: '',
|
||
address: '',
|
||
representative: '',
|
||
phone: ''
|
||
}
|
||
},
|
||
sections: []
|
||
};
|
||
|
||
return (
|
||
<div className="contract-search-results">
|
||
{/* 返回按钮 */}
|
||
<div className="mb-6">
|
||
<button
|
||
onClick={handleBack}
|
||
className="flex items-center px-3 py-2 text-sm border border-gray-200 rounded-lg hover:border-primary-color transition-colors"
|
||
>
|
||
<i className="ri-arrow-left-line mr-2"></i>
|
||
返回列表
|
||
</button>
|
||
</div>
|
||
|
||
{/* 模板详情 */}
|
||
<div className="template-detail max-w-4xl mx-auto">
|
||
{/* 详情头部 */}
|
||
<div className="detail-header bg-white rounded-xl p-8 mb-6 border border-gray-100">
|
||
<div className="flex justify-between items-start mb-4">
|
||
<div className="template-type">{template.type}</div>
|
||
<div className="flex items-center gap-2">
|
||
<div className="flex items-center gap-1 text-yellow-500">
|
||
{renderStars(template.rating)}
|
||
<span className="text-sm ml-1">{template.rating} (156评价)</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<h1 className="detail-title text-3xl font-semibold mb-6">{template.title}</h1>
|
||
|
||
<div className="detail-meta grid grid-cols-2 md:grid-cols-3 gap-4 mb-6">
|
||
<div className="meta-item">
|
||
<span className="meta-label text-gray-500">模板编号:</span>
|
||
<span>{template.templateCode}</span>
|
||
</div>
|
||
<div className="meta-item">
|
||
<span className="meta-label text-gray-500">更新时间:</span>
|
||
<span>{template.updateTime}</span>
|
||
</div>
|
||
<div className="meta-item">
|
||
<span className="meta-label text-gray-500">使用次数:</span>
|
||
<span>{template.useCount.toLocaleString()}次</span>
|
||
</div>
|
||
<div className="meta-item">
|
||
<span className="meta-label text-gray-500">文件大小:</span>
|
||
<span>{template.fileSize}</span>
|
||
</div>
|
||
<div className="meta-item">
|
||
<span className="meta-label text-gray-500">适用范围:</span>
|
||
<span>{template.scope}</span>
|
||
</div>
|
||
<div className="meta-item">
|
||
<span className="meta-label text-gray-500">法律依据:</span>
|
||
<span>{template.legalBasis}</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="detail-actions flex gap-3">
|
||
<button
|
||
className="detail-btn primary bg-primary text-white px-6 py-3 rounded-lg flex items-center gap-2 hover:bg-primary-hover"
|
||
onClick={handleDownload}
|
||
>
|
||
<i className="ri-download-line"></i>
|
||
立即下载使用
|
||
</button>
|
||
<button
|
||
className="detail-btn secondary bg-white border border-gray-200 px-6 py-3 rounded-lg flex items-center gap-2 hover:border-primary"
|
||
onClick={handlePreview}
|
||
>
|
||
<i className="ri-eye-line"></i>
|
||
在线预览
|
||
</button>
|
||
<button
|
||
className={`detail-btn secondary bg-white border border-gray-200 px-6 py-3 rounded-lg flex items-center gap-2 hover:border-primary ${isFavorited ? 'text-yellow-500' : ''}`}
|
||
onClick={handleFavorite}
|
||
>
|
||
<i className={isFavorited ? 'ri-star-fill' : 'ri-star-line'}></i>
|
||
收藏模板
|
||
</button>
|
||
<button
|
||
className="detail-btn secondary bg-white border border-gray-200 px-6 py-3 rounded-lg flex items-center gap-2 hover:border-primary"
|
||
onClick={handleShare}
|
||
>
|
||
<i className="ri-share-line"></i>
|
||
分享
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 详情内容 */}
|
||
<div className="detail-content bg-white rounded-xl p-8 border border-gray-100">
|
||
{/* 模板简介 */}
|
||
<div className="content-section mb-8">
|
||
<h3 className="section-title text-xl font-semibold mb-4">模板简介</h3>
|
||
<p className="text-gray-600 leading-relaxed">
|
||
{template.description}模板包含完整的合同条款结构,
|
||
包括合同主体、标的物、价格条款、交付方式、付款条件、违约责任、争议解决等核心内容。
|
||
适用于各类烟草产品的销售业务,能够有效保护交易双方的合法权益。
|
||
</p>
|
||
</div>
|
||
|
||
{/* 主要特点 */}
|
||
<div className="content-section mb-8">
|
||
<h3 className="section-title text-xl font-semibold mb-4">主要特点</h3>
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||
{template.features.map((feature, index) => (
|
||
<div key={index} className={`bg-${feature.color}-50 p-4 rounded-lg border border-${feature.color}-200`}>
|
||
<div className="flex items-center mb-2">
|
||
<i className={`${feature.icon} text-${feature.color}-600 mr-2`}></i>
|
||
<span className="font-medium">{feature.title}</span>
|
||
</div>
|
||
<p className="text-sm text-gray-600">{feature.description}</p>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
{/* 合同条款结构 */}
|
||
<div className="content-section mb-8">
|
||
<h3 className="section-title text-xl font-semibold mb-4">合同条款结构</h3>
|
||
<div className="space-y-3">
|
||
{template.structure.map((item) => (
|
||
<div key={item.step} className="flex items-center p-3 bg-gray-50 rounded-lg">
|
||
<div className="w-8 h-8 bg-primary text-white rounded-full flex items-center justify-center text-sm font-medium mr-3">
|
||
{item.step}
|
||
</div>
|
||
<div>
|
||
<div className="font-medium">{item.title}</div>
|
||
<div className="text-sm text-gray-600">{item.description}</div>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
{/* 合同预览 */}
|
||
<div className="content-section mb-8">
|
||
<h3 className="section-title text-xl font-semibold mb-4">合同预览</h3>
|
||
<div className="border border-gray-200 rounded-lg overflow-hidden">
|
||
{/* 使用更强的样式隔离 */}
|
||
<div
|
||
className="file-preview-isolation"
|
||
style={{
|
||
// 使用CSS变量避免继承
|
||
'--font-family': '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
||
'--font-size': '14px',
|
||
'--line-height': '1.5',
|
||
'--text-color': '#333333',
|
||
'--bg-color': '#ffffff',
|
||
|
||
// 强制重置所有可能的样式
|
||
all: 'unset',
|
||
display: 'block',
|
||
fontFamily: 'var(--font-family)',
|
||
fontSize: 'var(--font-size)',
|
||
lineHeight: 'var(--line-height)',
|
||
color: 'var(--text-color)',
|
||
backgroundColor: 'var(--bg-color)',
|
||
width: '100%',
|
||
minHeight: '600px',
|
||
position: 'relative',
|
||
isolation: 'isolate', // 创建新的层叠上下文
|
||
contain: 'layout style', // CSS容器化
|
||
zIndex: 0
|
||
} as React.CSSProperties}
|
||
>
|
||
<FilePreview
|
||
fileContent={fileContent}
|
||
activeReviewPointResultId={null}
|
||
targetPage={undefined}
|
||
isStructuredView={false}
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 用户评价 */}
|
||
<div className="content-section">
|
||
<h3 className="section-title text-xl font-semibold mb-4">用户评价</h3>
|
||
<div className="space-y-4">
|
||
{template.reviews.map((review, index) => (
|
||
<div key={index} className="border border-gray-200 rounded-lg p-4">
|
||
<div className="flex items-center justify-between mb-2">
|
||
<div className="flex items-center gap-2">
|
||
<div className="w-8 h-8 bg-blue-500 text-white rounded-full flex items-center justify-center text-sm font-medium">
|
||
{review.user[0]}
|
||
</div>
|
||
<span className="font-medium">{review.user}</span>
|
||
<div className="flex items-center gap-1 text-yellow-500">
|
||
{renderStars(review.rating)}
|
||
</div>
|
||
</div>
|
||
<span className="text-sm text-gray-500">{review.date}</span>
|
||
</div>
|
||
<p className="text-gray-600">{review.comment}</p>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|