Update from Vibe Studio
This commit is contained in:
3038
package-lock.json
generated
3038
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
17
package.json
17
package.json
@@ -3,9 +3,9 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite --host 0.0.0.0",
|
"dev": "npx --yes vite@6.4.1 --host 0.0.0.0",
|
||||||
"build": "vite build --mode production",
|
"build": "npx --yes vite@6.4.1 build --mode production",
|
||||||
"preview": "vite preview"
|
"preview": "npx --yes vite@6.4.1 preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ag-ui/client": "^0.0.42",
|
"@ag-ui/client": "^0.0.42",
|
||||||
@@ -18,18 +18,17 @@
|
|||||||
"less": "^4.2.1",
|
"less": "^4.2.1",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-router-dom": "^6.28.1"
|
"react-markdown": "^9.1.0",
|
||||||
|
"react-router-dom": "^6.28.1",
|
||||||
|
"remark-gfm": "^4.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/vite": "4.0.12",
|
|
||||||
"@types/node": "^22.10.5",
|
"@types/node": "^22.10.5",
|
||||||
"@types/react": "^18.3.18",
|
"@types/react": "^18.3.18",
|
||||||
"@types/react-dom": "^18.3.5",
|
"@types/react-dom": "^18.3.5",
|
||||||
"@vitejs/plugin-react": "^4.3.4",
|
"@vitejs/plugin-react": "4.7.0",
|
||||||
"path": "^0.12.7",
|
"path": "^0.12.7",
|
||||||
"prettier-plugin-tailwindcss": "^0.1.7",
|
|
||||||
"tailwindcss": "4.0.12",
|
|
||||||
"typescript": "~5.6.3",
|
"typescript": "~5.6.3",
|
||||||
"vite": "^6.0.7"
|
"vite": "6.4.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
38
src/api/contract-review.ts
Normal file
38
src/api/contract-review.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
export interface ContractReviewRequest {
|
||||||
|
contract_text: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DifyRequest {
|
||||||
|
inputs: {
|
||||||
|
contract_text: string
|
||||||
|
}
|
||||||
|
query: string
|
||||||
|
response_mode: string
|
||||||
|
user: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DifyResponse {
|
||||||
|
event: string
|
||||||
|
answer?: string
|
||||||
|
message?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function reviewContract(contract_text: string) {
|
||||||
|
const requestBody: DifyRequest = {
|
||||||
|
inputs: {
|
||||||
|
contract_text
|
||||||
|
},
|
||||||
|
query: '1',
|
||||||
|
response_mode: 'streaming',
|
||||||
|
user: 'contract-review-user'
|
||||||
|
}
|
||||||
|
|
||||||
|
return fetch('https://copilot.sino-bridge.com/v1/chat-messages', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Authorization': 'Bearer app-KmvKMii68gWP7Yh1sKVExGIw'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(requestBody)
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
@import 'tailwindcss';
|
|
||||||
|
|
||||||
.route-loading {
|
.route-loading {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
|
|||||||
375
src/pages/contract-review/index.tsx
Normal file
375
src/pages/contract-review/index.tsx
Normal file
@@ -0,0 +1,375 @@
|
|||||||
|
import { useState } from 'react'
|
||||||
|
import { Button, Card, Form, Input, message, Space, Typography } from 'antd'
|
||||||
|
import { PageHeader } from '@ant-design/pro-components'
|
||||||
|
import { reviewContract } from '@/api/contract-review'
|
||||||
|
|
||||||
|
const { Text, Paragraph, Title } = Typography
|
||||||
|
|
||||||
|
const { TextArea } = Input
|
||||||
|
|
||||||
|
const ContractReview = () => {
|
||||||
|
const [form] = Form.useForm()
|
||||||
|
const [loading, setLoading] = useState(false)
|
||||||
|
const [reviewResult, setReviewResult] = useState('')
|
||||||
|
const [currentInput, setCurrentInput] = useState('')
|
||||||
|
|
||||||
|
const maxChars = 5000
|
||||||
|
|
||||||
|
// 简单的markdown处理函数
|
||||||
|
const formatMarkdown = (text: string) => {
|
||||||
|
if (!text) return []
|
||||||
|
|
||||||
|
const lines = text.split('\n')
|
||||||
|
const elements: React.ReactNode[] = []
|
||||||
|
let currentList: string[] = []
|
||||||
|
let inTable = false
|
||||||
|
let tableHeaders: string[] = []
|
||||||
|
let tableRows: string[][] = []
|
||||||
|
|
||||||
|
const flushList = () => {
|
||||||
|
if (currentList.length > 0) {
|
||||||
|
elements.push(
|
||||||
|
<ul key={elements.length} style={{ paddingLeft: '20px', margin: '12px 0' }}>
|
||||||
|
{currentList.map((item, index) => (
|
||||||
|
<li key={index} style={{ margin: '4px 0' }}>{item}</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)
|
||||||
|
currentList = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const flushTable = () => {
|
||||||
|
if (tableHeaders.length > 0) {
|
||||||
|
elements.push(
|
||||||
|
<div key={elements.length} style={{ margin: '16px 0', overflowX: 'auto' }}>
|
||||||
|
<table style={{ width: '100%', borderCollapse: 'collapse' }}>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
{tableHeaders.map((header, index) => (
|
||||||
|
<th key={index} style={{
|
||||||
|
border: '1px solid #d9d9d9',
|
||||||
|
padding: '8px 12px',
|
||||||
|
backgroundColor: '#f5f5f5',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
textAlign: 'left'
|
||||||
|
}}>
|
||||||
|
{header}
|
||||||
|
</th>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{tableRows.map((row, rowIndex) => (
|
||||||
|
<tr key={rowIndex}>
|
||||||
|
{row.map((cell, cellIndex) => (
|
||||||
|
<td key={cellIndex} style={{
|
||||||
|
border: '1px solid #d9d9d9',
|
||||||
|
padding: '8px 12px'
|
||||||
|
}}>
|
||||||
|
{cell}
|
||||||
|
</td>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
tableHeaders = []
|
||||||
|
tableRows = []
|
||||||
|
inTable = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.forEach((line, index) => {
|
||||||
|
const trimmedLine = line.trim()
|
||||||
|
|
||||||
|
// 处理标题
|
||||||
|
if (trimmedLine.startsWith('# ')) {
|
||||||
|
flushList()
|
||||||
|
flushTable()
|
||||||
|
elements.push(
|
||||||
|
<Title key={elements.length} level={1} style={{ color: '#1890ff', borderBottom: '1px solid #d9d9d9', paddingBottom: '8px', marginBottom: '16px' }}>
|
||||||
|
{trimmedLine.substring(2)}
|
||||||
|
</Title>
|
||||||
|
)
|
||||||
|
} else if (trimmedLine.startsWith('## ')) {
|
||||||
|
flushList()
|
||||||
|
flushTable()
|
||||||
|
elements.push(
|
||||||
|
<Title key={elements.length} level={2} style={{ color: '#1890ff', marginTop: '24px', marginBottom: '12px' }}>
|
||||||
|
{trimmedLine.substring(3)}
|
||||||
|
</Title>
|
||||||
|
)
|
||||||
|
} else if (trimmedLine.startsWith('### ')) {
|
||||||
|
flushList()
|
||||||
|
flushTable()
|
||||||
|
elements.push(
|
||||||
|
<Title key={elements.length} level={3} style={{ color: '#1890ff', marginTop: '20px', marginBottom: '10px' }}>
|
||||||
|
{trimmedLine.substring(4)}
|
||||||
|
</Title>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// 处理分割线
|
||||||
|
else if (trimmedLine === '---') {
|
||||||
|
flushList()
|
||||||
|
flushTable()
|
||||||
|
elements.push(
|
||||||
|
<hr key={elements.length} style={{ border: 'none', borderTop: '1px solid #d9d9d9', margin: '24px 0' }} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// 处理列表项
|
||||||
|
else if (trimmedLine.startsWith('- ') || trimmedLine.startsWith('* ')) {
|
||||||
|
flushTable()
|
||||||
|
currentList.push(trimmedLine.substring(2))
|
||||||
|
}
|
||||||
|
// 处理表格
|
||||||
|
else if (trimmedLine.includes('|') && !inTable) {
|
||||||
|
flushList()
|
||||||
|
inTable = true
|
||||||
|
tableHeaders = trimmedLine.split('|').map(h => h.trim()).filter(h => h)
|
||||||
|
} else if (trimmedLine.includes('|') && inTable && !trimmedLine.includes('---')) {
|
||||||
|
const row = trimmedLine.split('|').map(c => c.trim()).filter(c => c)
|
||||||
|
if (row.length > 0) {
|
||||||
|
tableRows.push(row)
|
||||||
|
}
|
||||||
|
} else if (trimmedLine.includes('|') && inTable && trimmedLine.includes('---')) {
|
||||||
|
// 跳过分隔行
|
||||||
|
}
|
||||||
|
// 处理普通段落
|
||||||
|
else if (trimmedLine) {
|
||||||
|
flushList()
|
||||||
|
flushTable()
|
||||||
|
|
||||||
|
// 处理加粗文本
|
||||||
|
const boldRegex = /\*\*(.*?)\*\*/g
|
||||||
|
const parts = trimmedLine.split(boldRegex)
|
||||||
|
|
||||||
|
if (parts.length > 1) {
|
||||||
|
elements.push(
|
||||||
|
<Paragraph key={elements.length} style={{ margin: '12px 0' }}>
|
||||||
|
{parts.map((part, partIndex) =>
|
||||||
|
partIndex % 2 === 1 ?
|
||||||
|
<Text key={partIndex} strong style={{ color: '#262626' }}>{part}</Text> :
|
||||||
|
part
|
||||||
|
)}
|
||||||
|
</Paragraph>
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
elements.push(
|
||||||
|
<Paragraph key={elements.length} style={{ margin: '12px 0', lineHeight: '1.6' }}>
|
||||||
|
{trimmedLine}
|
||||||
|
</Paragraph>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 清理剩余的内容
|
||||||
|
flushList()
|
||||||
|
flushTable()
|
||||||
|
|
||||||
|
return elements
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleReview = async () => {
|
||||||
|
try {
|
||||||
|
const contract_text = form.getFieldValue('contract_text') || ''
|
||||||
|
|
||||||
|
if (!contract_text.trim()) {
|
||||||
|
message.warning('请输入合同片段内容')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(true)
|
||||||
|
setReviewResult('')
|
||||||
|
|
||||||
|
const response = await reviewContract(contract_text)
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorText = await response.text()
|
||||||
|
throw new Error(`审核请求失败: ${response.status} ${errorText}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!response.body) {
|
||||||
|
throw new Error('响应体为空')
|
||||||
|
}
|
||||||
|
|
||||||
|
const reader = response.body.getReader()
|
||||||
|
const decoder = new TextDecoder('utf-8')
|
||||||
|
let buffer = ''
|
||||||
|
let fullContent = ''
|
||||||
|
|
||||||
|
try {
|
||||||
|
while (true) {
|
||||||
|
const { done, value } = await reader.read()
|
||||||
|
if (done) break
|
||||||
|
|
||||||
|
buffer += decoder.decode(value, { stream: true })
|
||||||
|
const lines = buffer.split('\n')
|
||||||
|
buffer = lines.pop() || ''
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
const trimmedLine = line.trim()
|
||||||
|
if (!trimmedLine || trimmedLine === 'data: [DONE]') {
|
||||||
|
if (trimmedLine === 'data: [DONE]') {
|
||||||
|
message.success('审核完成')
|
||||||
|
setLoading(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trimmedLine.startsWith('data: ')) {
|
||||||
|
try {
|
||||||
|
const data = trimmedLine.slice(6)
|
||||||
|
const parsed = JSON.parse(data)
|
||||||
|
|
||||||
|
if (parsed.event === 'message' && parsed.answer) {
|
||||||
|
fullContent += parsed.answer
|
||||||
|
setReviewResult(fullContent)
|
||||||
|
} else if (parsed.event === 'error') {
|
||||||
|
throw new Error(parsed.message || 'Dify API 返回错误')
|
||||||
|
}
|
||||||
|
} catch (parseError) {
|
||||||
|
console.warn('跳过无法解析的行:', trimmedLine)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
reader.releaseLock()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fullContent) {
|
||||||
|
message.success('审核完成')
|
||||||
|
} else {
|
||||||
|
throw new Error('未收到审核结果')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('审核错误:', error)
|
||||||
|
message.error(error instanceof Error ? error.message : '审核失败,请稍后重试')
|
||||||
|
} finally {
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleReset = () => {
|
||||||
|
form.resetFields()
|
||||||
|
setReviewResult('')
|
||||||
|
setCurrentInput('')
|
||||||
|
message.info('已重置内容')
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleReReview = () => {
|
||||||
|
handleReview()
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ maxWidth: '1200px', margin: '0 auto', padding: '20px' }}>
|
||||||
|
<PageHeader
|
||||||
|
title="合同智能审核"
|
||||||
|
subTitle="通过AI技术对合同片段进行智能审核,提供专业建议"
|
||||||
|
style={{ marginBottom: '30px', textAlign: 'center' }}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Space direction="vertical" style={{ width: '100%' }} size="large">
|
||||||
|
{/* 参数输入区 */}
|
||||||
|
<Card title="参数输入区">
|
||||||
|
<Form form={form} layout="vertical">
|
||||||
|
<Form.Item
|
||||||
|
name="contract_text"
|
||||||
|
label={
|
||||||
|
<div style={{ display: 'flex', justifyContent: 'space-between', width: '100%' }}>
|
||||||
|
<span>合同片段文本</span>
|
||||||
|
<span style={{ color: currentInput.length > maxChars ? '#ff4d4f' : '#999' }}>
|
||||||
|
{currentInput.length} / {maxChars}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
rules={[
|
||||||
|
{ required: true, message: '请输入合同片段内容' },
|
||||||
|
{ max: maxChars, message: `输入内容不能超过${maxChars}个字符` }
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<TextArea
|
||||||
|
placeholder="请输入需要审核的合同片段内容(最多5000个字符)"
|
||||||
|
rows={8}
|
||||||
|
value={currentInput}
|
||||||
|
onChange={(e) => setCurrentInput(e.target.value)}
|
||||||
|
showCount
|
||||||
|
maxLength={maxChars}
|
||||||
|
style={{ fontSize: '14px', fontFamily: 'monospace' }}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* 操作按钮区 */}
|
||||||
|
<Space style={{ width: '100%', justifyContent: 'center' }} size="middle">
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
size="large"
|
||||||
|
onClick={handleReview}
|
||||||
|
loading={loading}
|
||||||
|
style={{ minWidth: '120px', height: '40px', fontSize: '16px' }}
|
||||||
|
>
|
||||||
|
开始审核
|
||||||
|
</Button>
|
||||||
|
{reviewResult && (
|
||||||
|
<Button
|
||||||
|
size="large"
|
||||||
|
onClick={handleReReview}
|
||||||
|
disabled={loading}
|
||||||
|
style={{ minWidth: '120px', height: '40px', fontSize: '16px' }}
|
||||||
|
>
|
||||||
|
重新审核
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
size="large"
|
||||||
|
onClick={handleReset}
|
||||||
|
disabled={loading}
|
||||||
|
style={{ minWidth: '120px', height: '40px', fontSize: '16px' }}
|
||||||
|
>
|
||||||
|
重置
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
|
||||||
|
{/* 内容展示区 */}
|
||||||
|
<Card title="审核结果">
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
backgroundColor: '#fafafa',
|
||||||
|
border: '1px solid #d9d9d9',
|
||||||
|
borderRadius: '4px',
|
||||||
|
padding: '15px',
|
||||||
|
minHeight: '300px',
|
||||||
|
maxHeight: '500px',
|
||||||
|
overflowY: 'auto',
|
||||||
|
fontSize: '14px',
|
||||||
|
lineHeight: '1.6'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{loading ? (
|
||||||
|
<div style={{ textAlign: 'center', color: '#999', padding: '20px' }}>
|
||||||
|
正在审核中,请稍候...
|
||||||
|
</div>
|
||||||
|
) : reviewResult ? (
|
||||||
|
<div>
|
||||||
|
{formatMarkdown(reviewResult)}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div style={{ textAlign: 'center', color: '#999' }}>
|
||||||
|
审核结果将在这里显示
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ContractReview
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Card, Row, Col, Typography, Space } from 'antd'
|
import { Card, Row, Col, Typography, Space } from 'antd'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import { RobotOutlined, TranslationOutlined, FileTextOutlined, EditOutlined } from '@ant-design/icons'
|
import { RobotOutlined, TranslationOutlined, FileTextOutlined, EditOutlined, SafetyCertificateOutlined } from '@ant-design/icons'
|
||||||
|
|
||||||
const { Title, Paragraph } = Typography
|
const { Title, Paragraph } = Typography
|
||||||
|
|
||||||
@@ -23,6 +23,12 @@ const cards = [
|
|||||||
icon: <TranslationOutlined style={{ fontSize: 32, color: '#722ed1' }} />,
|
icon: <TranslationOutlined style={{ fontSize: 32, color: '#722ed1' }} />,
|
||||||
link: '/zh-en-translator'
|
link: '/zh-en-translator'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '合同智能审核',
|
||||||
|
description: 'AI 驱动的合同智能审核系统,提供专业建议',
|
||||||
|
icon: <SafetyCertificateOutlined style={{ fontSize: 32, color: '#eb2f96' }} />,
|
||||||
|
link: '/contract-review'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '错别字检测',
|
title: '错别字检测',
|
||||||
description: '智能检测和纠正中文文本中的错别字',
|
description: '智能检测和纠正中文文本中的错别字',
|
||||||
|
|||||||
@@ -52,6 +52,10 @@ const router: RouteObject[] = [
|
|||||||
path: '/spell-check-system',
|
path: '/spell-check-system',
|
||||||
element: LazyLoad(lazy(() => import('@/pages/spell-check-system')))
|
element: LazyLoad(lazy(() => import('@/pages/spell-check-system')))
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/contract-review',
|
||||||
|
element: LazyLoad(lazy(() => import('@/pages/contract-review')))
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/404',
|
path: '/404',
|
||||||
element: <>404</>
|
element: <>404</>
|
||||||
|
|||||||
20
vite.config.cjs
Normal file
20
vite.config.cjs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
const react = require('@vitejs/plugin-react')
|
||||||
|
const path = require('path')
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
base: './',
|
||||||
|
plugins: [react()],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': path.resolve(__dirname, './src')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
host: '0.0.0.0',
|
||||||
|
port: 5173
|
||||||
|
},
|
||||||
|
preview: {
|
||||||
|
host: '0.0.0.0',
|
||||||
|
port: 3000
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
import { defineConfig } from 'vite'
|
|
||||||
import react from '@vitejs/plugin-react'
|
|
||||||
import tailwindcss from '@tailwindcss/vite'
|
|
||||||
import path from 'path'
|
|
||||||
|
|
||||||
// https://vite.dev/config/
|
|
||||||
export default defineConfig(() => {
|
|
||||||
return {
|
|
||||||
base: './',
|
|
||||||
plugins: [tailwindcss(), react()],
|
|
||||||
resolve: {
|
|
||||||
alias: {
|
|
||||||
'@': path.resolve(__dirname, './src')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
optimizeDeps: {
|
|
||||||
include: ['dayjs/locale/zh-cn']
|
|
||||||
},
|
|
||||||
server: {
|
|
||||||
host: '0.0.0.0',
|
|
||||||
allowedHosts: true as true,
|
|
||||||
cors: true,
|
|
||||||
hmr: false,
|
|
||||||
port: 5173
|
|
||||||
},
|
|
||||||
// 生产预览服务器配置 (vite preview) - 仅用于本地测试
|
|
||||||
preview: {
|
|
||||||
host: '0.0.0.0',
|
|
||||||
port: 3000,
|
|
||||||
cors: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
Reference in New Issue
Block a user