1227 lines
68 KiB
HTML
1227 lines
68 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>标伙伴 · AI 标书助手</title>
|
||
<script src="https://cdn.tailwindcss.com"></script>
|
||
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
||
<link rel="stylesheet" href="/static/style.css">
|
||
<style>
|
||
[x-cloak]{display:none!important}
|
||
body{font-family:'PingFang SC','Microsoft YaHei',sans-serif;background:#f0f4f8}
|
||
</style>
|
||
</head>
|
||
<body class="min-h-screen" x-data="app()" x-init="init()">
|
||
|
||
<!-- ── 顶栏 ── -->
|
||
<header class="bg-white border-b border-gray-200 sticky top-0 z-50 shadow-sm">
|
||
<div class="max-w-7xl mx-auto px-6 h-16 flex items-center justify-between">
|
||
<div class="flex items-center gap-3">
|
||
<div class="w-9 h-9 rounded-xl bg-gradient-to-br from-blue-600 to-indigo-600 flex items-center justify-center shadow">
|
||
<svg class="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
|
||
</svg>
|
||
</div>
|
||
<div>
|
||
<span class="text-lg font-bold text-gray-900">标伙伴</span>
|
||
<span class="ml-2 text-xs text-gray-400 font-medium">AI 标书助手</span>
|
||
</div>
|
||
</div>
|
||
<div class="flex items-center gap-3">
|
||
<button @click="showConfig=true"
|
||
class="p-2 text-gray-500 hover:text-blue-600 hover:bg-blue-50 rounded-lg transition">
|
||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||
d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/>
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
|
||
</svg>
|
||
</button>
|
||
<!-- 文件样式设置按钮 (首页菜单) -->
|
||
<button @click="showStyleSettings = true"
|
||
class="flex items-center gap-1.5 px-4 py-2 text-sm font-medium text-gray-700 hover:text-indigo-600 hover:bg-indigo-50 rounded-lg transition border border-transparent hover:border-indigo-200">
|
||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 21a4 4 0 01-4-4V5a2 2 0 012-2 2 2 0 01-2-2 2 2 0 012-2 2 2 0 01-2-2 2 2 0 012-2 2 2 0 01-2-2 2 2 0 012-2zM17 21a4 4 0 01-4-4V5a2 2 0 012-2 2 2 0 01-2-2 2 2 0 012-2 2 2 0 01-2-2 2 2 0 012-2z"/>
|
||
</svg>
|
||
文件样式设置
|
||
</button>
|
||
<button @click="showCreate=true"
|
||
class="flex items-center gap-2 px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium rounded-lg shadow-sm transition">
|
||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"/>
|
||
</svg>
|
||
新建项目
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
<!-- ── 主内容 ── -->
|
||
<main class="max-w-7xl mx-auto px-6 py-8">
|
||
|
||
<!-- 篇幅目标仅存在于「标书项目 → 步骤1 解析」;本页不重复控件 -->
|
||
<div x-show="!loading" x-cloak class="mb-6 p-3.5 bg-slate-50 border border-slate-200 rounded-xl text-sm text-slate-600 leading-relaxed">
|
||
<p><strong>篇幅目标(按页数粗略换算)</strong>请进入某标书项目,在 <strong>步骤1「解析」</strong> 中设置:100/150/200/250/300 页、自定义、保存页数设置、使用原档位、当前页等,保存后用于后续章节生成。</p>
|
||
</div>
|
||
|
||
<!-- 欢迎横幅(无项目时显示) -->
|
||
<template x-if="projects.length === 0 && !loading">
|
||
<div class="text-center py-20">
|
||
<div class="w-24 h-24 mx-auto mb-6 rounded-3xl bg-gradient-to-br from-blue-100 to-indigo-100 flex items-center justify-center">
|
||
<svg class="w-12 h-12 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"
|
||
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
|
||
</svg>
|
||
</div>
|
||
<h2 class="text-2xl font-bold text-gray-800 mb-2">欢迎使用标伙伴</h2>
|
||
<p class="text-gray-500 mb-8 max-w-md mx-auto">AI 驱动的标书写作助手,上传招标文件,一键生成专业技术标书</p>
|
||
<button @click="showCreate=true"
|
||
class="px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white font-medium rounded-xl shadow-md transition">
|
||
创建第一个项目
|
||
</button>
|
||
|
||
<!-- 功能介绍 -->
|
||
<div class="grid grid-cols-3 gap-6 mt-16 max-w-3xl mx-auto text-left">
|
||
<div class="bg-white rounded-2xl p-6 shadow-sm border border-gray-100">
|
||
<div class="w-10 h-10 bg-blue-100 rounded-xl flex items-center justify-center mb-4">
|
||
<svg class="w-5 h-5 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12"/>
|
||
</svg>
|
||
</div>
|
||
<h3 class="font-semibold text-gray-800 mb-1">智能解析招标文件</h3>
|
||
<p class="text-sm text-gray-500">自动提取评分要求、资质条件、技术参数</p>
|
||
</div>
|
||
<div class="bg-white rounded-2xl p-6 shadow-sm border border-gray-100">
|
||
<div class="w-10 h-10 bg-green-100 rounded-xl flex items-center justify-center mb-4">
|
||
<svg class="w-5 h-5 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"/>
|
||
</svg>
|
||
</div>
|
||
<h3 class="font-semibold text-gray-800 mb-1">自动生成标书大纲</h3>
|
||
<p class="text-sm text-gray-500">按评分权重生成四级章节结构,精准对标要求</p>
|
||
</div>
|
||
<div class="bg-white rounded-2xl p-6 shadow-sm border border-gray-100">
|
||
<div class="w-10 h-10 bg-purple-100 rounded-xl flex items-center justify-center mb-4">
|
||
<svg class="w-5 h-5 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
|
||
</svg>
|
||
</div>
|
||
<h3 class="font-semibold text-gray-800 mb-1">一键导出 Word 文档</h3>
|
||
<p class="text-sm text-gray-500">专业排版,直接交付使用</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<!-- 加载中 -->
|
||
<template x-if="loading">
|
||
<div class="flex justify-center py-20">
|
||
<div class="w-8 h-8 border-4 border-blue-200 border-t-blue-600 rounded-full animate-spin"></div>
|
||
</div>
|
||
</template>
|
||
|
||
<!-- 项目列表 -->
|
||
<template x-if="projects.length > 0">
|
||
<div>
|
||
<div class="flex items-center justify-between mb-6">
|
||
<h2 class="text-xl font-bold text-gray-800">我的项目
|
||
<span class="ml-2 text-sm font-normal text-gray-400">共 <span x-text="projects.length"></span> 个</span>
|
||
</h2>
|
||
</div>
|
||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5">
|
||
<template x-for="p in projects" :key="p.id">
|
||
<div class="bg-white rounded-2xl border border-gray-100 shadow-sm hover:shadow-md transition-shadow cursor-pointer group"
|
||
@click="window.location='/project/'+p.id">
|
||
<div class="p-5">
|
||
<!-- 状态徽标 -->
|
||
<div class="flex items-start justify-between mb-3">
|
||
<div class="flex-1 min-w-0">
|
||
<h3 class="font-semibold text-gray-900 group-hover:text-blue-600 transition truncate" x-text="p.name"></h3>
|
||
<p class="text-xs text-gray-400 mt-1" x-text="formatDate(p.created_at)"></p>
|
||
</div>
|
||
<span class="ml-2 flex-shrink-0 px-2 py-0.5 rounded-full text-xs font-medium"
|
||
:class="statusBadge(p.parse_status).cls" x-text="statusBadge(p.parse_status).text"></span>
|
||
</div>
|
||
|
||
<!-- 文件名 -->
|
||
<div x-show="p.file_name" class="flex items-center gap-1.5 text-xs text-gray-500 mb-3">
|
||
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
|
||
</svg>
|
||
<span class="truncate" x-text="p.file_name"></span>
|
||
</div>
|
||
|
||
<!-- 进度条 -->
|
||
<div x-show="p.section_count > 0" class="mb-3">
|
||
<div class="flex justify-between text-xs text-gray-500 mb-1">
|
||
<span>章节生成进度</span>
|
||
<span x-text="p.done_count + '/' + p.section_count"></span>
|
||
</div>
|
||
<div class="h-1.5 bg-gray-100 rounded-full overflow-hidden">
|
||
<div class="h-full bg-blue-500 rounded-full transition-all"
|
||
:style="'width:' + (p.section_count ? p.done_count/p.section_count*100 : 0) + '%'"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 操作按钮 -->
|
||
<div class="flex gap-2 pt-3 border-t border-gray-50">
|
||
<button class="flex-1 text-xs text-blue-600 hover:text-blue-700 font-medium py-1 hover:bg-blue-50 rounded-lg transition"
|
||
@click.stop="window.location='/project/'+p.id">
|
||
进入项目
|
||
</button>
|
||
<button class="text-xs text-red-400 hover:text-red-600 font-medium px-3 py-1 hover:bg-red-50 rounded-lg transition"
|
||
@click.stop="deleteProject(p.id, p.name)">
|
||
删除
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
</main>
|
||
|
||
<!-- ══ 新建项目弹窗 ══ -->
|
||
<div x-show="showCreate" x-cloak class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50 backdrop-blur-sm">
|
||
<div class="bg-white rounded-2xl shadow-2xl w-full max-w-md p-6" @click.stop>
|
||
<h2 class="text-lg font-bold text-gray-900 mb-4">新建标书项目</h2>
|
||
<div class="mb-4">
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">项目名称</label>
|
||
<input type="text" x-model="newProjectName" @keydown.enter="createProject()"
|
||
placeholder="例如:XX智慧城市信息化建设项目"
|
||
class="w-full px-4 py-2.5 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-sm">
|
||
</div>
|
||
<div class="flex gap-3">
|
||
<button @click="showCreate=false" class="flex-1 px-4 py-2 border border-gray-200 text-gray-600 rounded-xl text-sm hover:bg-gray-50 transition">取消</button>
|
||
<button @click="createProject()" :disabled="creating"
|
||
class="flex-1 px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-xl text-sm font-medium transition disabled:opacity-60">
|
||
<span x-show="!creating">创建项目</span>
|
||
<span x-show="creating">创建中...</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ══ AI 配置弹窗 ══ -->
|
||
<div x-show="showConfig" x-cloak class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50 backdrop-blur-sm">
|
||
<div class="bg-white rounded-2xl shadow-2xl w-full max-w-lg p-6" @click.stop>
|
||
<h2 class="text-lg font-bold text-gray-900 mb-4">AI 模型配置</h2>
|
||
|
||
<div class="mb-4">
|
||
<label class="block text-sm font-medium text-gray-700 mb-2">选择模型提供商</label>
|
||
<div class="grid grid-cols-3 gap-2">
|
||
<label class="flex items-center gap-2 p-3 border-2 rounded-xl cursor-pointer transition"
|
||
:class="cfg.model_provider==='qwen' ? 'border-blue-500 bg-blue-50' : 'border-gray-200 hover:border-gray-300'">
|
||
<input type="radio" name="provider" value="qwen" x-model="cfg.model_provider" class="accent-blue-600">
|
||
<div>
|
||
<p class="font-medium text-sm leading-tight">通义千问</p>
|
||
<p class="text-xs text-gray-400">Qwen · 阿里云</p>
|
||
</div>
|
||
</label>
|
||
<label class="flex items-center gap-2 p-3 border-2 rounded-xl cursor-pointer transition"
|
||
:class="cfg.model_provider==='deepseek' ? 'border-blue-500 bg-blue-50' : 'border-gray-200 hover:border-gray-300'">
|
||
<input type="radio" name="provider" value="deepseek" x-model="cfg.model_provider" class="accent-blue-600">
|
||
<div>
|
||
<p class="font-medium text-sm leading-tight">DeepSeek</p>
|
||
<p class="text-xs text-gray-400">高性价比 · 云端</p>
|
||
</div>
|
||
</label>
|
||
<label class="flex items-center gap-2 p-3 border-2 rounded-xl cursor-pointer transition"
|
||
:class="cfg.model_provider==='openai' ? 'border-blue-500 bg-blue-50' : 'border-gray-200 hover:border-gray-300'">
|
||
<input type="radio" name="provider" value="openai" x-model="cfg.model_provider" class="accent-blue-600">
|
||
<div>
|
||
<p class="font-medium text-sm leading-tight">OpenAI</p>
|
||
<p class="text-xs text-gray-400">GPT-4.1 · 云端</p>
|
||
</div>
|
||
</label>
|
||
<label class="flex items-center gap-2 p-3 border-2 rounded-xl cursor-pointer transition"
|
||
:class="cfg.model_provider==='doubao' ? 'border-blue-500 bg-blue-50' : 'border-gray-200 hover:border-gray-300'">
|
||
<input type="radio" name="provider" value="doubao" x-model="cfg.model_provider" class="accent-blue-600">
|
||
<div>
|
||
<p class="font-medium text-sm leading-tight">豆包</p>
|
||
<p class="text-xs text-gray-400">字节跳动 · 云端</p>
|
||
</div>
|
||
</label>
|
||
<label class="flex items-center gap-2 p-3 border-2 rounded-xl cursor-pointer transition"
|
||
:class="cfg.model_provider==='kimi' ? 'border-blue-500 bg-blue-50' : 'border-gray-200 hover:border-gray-300'">
|
||
<input type="radio" name="provider" value="kimi" x-model="cfg.model_provider" class="accent-blue-600">
|
||
<div>
|
||
<p class="font-medium text-sm leading-tight">Kimi</p>
|
||
<p class="text-xs text-gray-400">Moonshot · 长文本</p>
|
||
</div>
|
||
</label>
|
||
<label class="flex items-center gap-2 p-3 border-2 rounded-xl cursor-pointer transition"
|
||
:class="cfg.model_provider==='ollama' ? 'border-green-500 bg-green-50' : 'border-gray-200 hover:border-gray-300'">
|
||
<input type="radio" name="provider" value="ollama" x-model="cfg.model_provider" class="accent-green-600">
|
||
<div>
|
||
<p class="font-medium text-sm leading-tight">Ollama 本地</p>
|
||
<p class="text-xs text-gray-400">免费 · 离线</p>
|
||
</div>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
|
||
<template x-if="cfg.model_provider==='qwen'">
|
||
<div class="space-y-3 mb-4">
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">Qwen API Key
|
||
<a href="https://dashscope.aliyun.com/" target="_blank" class="ml-1 text-blue-500 text-xs hover:underline font-normal">申请地址 ↗</a>
|
||
</label>
|
||
<input type="password" x-model="cfg.qwen_api_key" placeholder="sk-..."
|
||
class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||
<p x-show="cfg.has_qwen_key" class="text-xs text-green-600 mt-1">✓ 已配置</p>
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">模型</label>
|
||
<select x-model="cfg.qwen_model" class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||
<optgroup label="─── Qwen3.6(本项默认:生成+解析)───">
|
||
<option value="qwen3.6-plus">qwen3.6-plus ★ 默认</option>
|
||
</optgroup>
|
||
<optgroup label="─── 旗舰版 ───">
|
||
<option value="qwen-max">qwen-max ★ 推荐</option>
|
||
<option value="qwen-max-latest">qwen-max-latest(自动追踪最新)</option>
|
||
</optgroup>
|
||
<optgroup label="─── 均衡版 ───">
|
||
<option value="qwen-plus">qwen-plus</option>
|
||
<option value="qwen-plus-latest">qwen-plus-latest(自动追踪最新)</option>
|
||
</optgroup>
|
||
<optgroup label="─── 快速版 ───">
|
||
<option value="qwen-turbo">qwen-turbo</option>
|
||
<option value="qwen-turbo-latest">qwen-turbo-latest(自动追踪最新)</option>
|
||
</optgroup>
|
||
<optgroup label="─── 超长上下文 ───">
|
||
<option value="qwen-long">qwen-long(1M tokens)</option>
|
||
</optgroup>
|
||
<optgroup label="─── Qwen3 系列 API ───">
|
||
<option value="qwen3-235b-a22b">qwen3-235b-a22b(MoE 旗舰)</option>
|
||
<option value="qwen3-32b">qwen3-32b</option>
|
||
<option value="qwen3-30b-a3b">qwen3-30b-a3b(MoE 高效)</option>
|
||
<option value="qwen3-14b">qwen3-14b</option>
|
||
<option value="qwen3-8b">qwen3-8b</option>
|
||
</optgroup>
|
||
<optgroup label="─── 自定义 ───">
|
||
<option value="">手动输入模型名</option>
|
||
</optgroup>
|
||
</select>
|
||
<div x-show="!qwenPresets.includes(cfg.qwen_model)" class="mt-2">
|
||
<input type="text" x-model="cfg.qwen_model" placeholder="输入模型名,如 qwen-max-2025-01-25"
|
||
class="w-full px-3 py-2 border border-blue-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 font-mono">
|
||
<p class="text-xs text-gray-400 mt-1">输入任意 DashScope 兼容的模型名</p>
|
||
</div>
|
||
</div>
|
||
<!-- 自定义 API 地址(可选,供代理/中转使用) -->
|
||
<div>
|
||
<button type="button" @click="cfg._qwen_adv = !cfg._qwen_adv"
|
||
class="text-xs text-gray-400 hover:text-gray-600 flex items-center gap-1">
|
||
<span x-text="cfg._qwen_adv ? '▾' : '▸'"></span>
|
||
高级:自定义 API 地址(代理/中转)
|
||
</button>
|
||
<div x-show="cfg._qwen_adv" class="mt-2">
|
||
<input type="text" x-model="cfg.qwen_base_url"
|
||
placeholder="https://dashscope.aliyuncs.com/compatible-mode/v1"
|
||
class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 font-mono">
|
||
<p class="text-xs text-gray-400 mt-1">默认:https://dashscope.aliyuncs.com/compatible-mode/v1</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<template x-if="cfg.model_provider==='deepseek'">
|
||
<div class="space-y-3 mb-4">
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">DeepSeek API Key
|
||
<a href="https://platform.deepseek.com/" target="_blank" class="ml-1 text-blue-500 text-xs hover:underline font-normal">申请地址 ↗</a>
|
||
</label>
|
||
<input type="password" x-model="cfg.deepseek_api_key" placeholder="sk-..."
|
||
class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||
<p x-show="cfg.has_deepseek_key" class="text-xs text-green-600 mt-1">✓ 已配置</p>
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">模型</label>
|
||
<select x-model="cfg.deepseek_model" class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||
<optgroup label="─── 对话模型 ───">
|
||
<option value="deepseek-chat">deepseek-chat ★ 推荐(V3 最新)</option>
|
||
</optgroup>
|
||
<optgroup label="─── 推理模型 ───">
|
||
<option value="deepseek-reasoner">deepseek-reasoner(R1)</option>
|
||
</optgroup>
|
||
<optgroup label="─── 自定义 ───">
|
||
<option value="">手动输入模型名</option>
|
||
</optgroup>
|
||
</select>
|
||
<div x-show="!deepseekPresets.includes(cfg.deepseek_model)" class="mt-2">
|
||
<input type="text" x-model="cfg.deepseek_model" placeholder="输入模型名,如 deepseek-chat-v3-0324"
|
||
class="w-full px-3 py-2 border border-blue-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 font-mono">
|
||
<p class="text-xs text-gray-400 mt-1">输入任意 DeepSeek 兼容的模型名</p>
|
||
</div>
|
||
</div>
|
||
<!-- 自定义 API 地址 -->
|
||
<div>
|
||
<button type="button" @click="cfg._ds_adv = !cfg._ds_adv"
|
||
class="text-xs text-gray-400 hover:text-gray-600 flex items-center gap-1">
|
||
<span x-text="cfg._ds_adv ? '▾' : '▸'"></span>
|
||
高级:自定义 API 地址(代理/中转)
|
||
</button>
|
||
<div x-show="cfg._ds_adv" class="mt-2">
|
||
<input type="text" x-model="cfg.deepseek_base_url"
|
||
placeholder="https://api.deepseek.com/v1"
|
||
class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 font-mono">
|
||
<p class="text-xs text-gray-400 mt-1">默认:https://api.deepseek.com/v1</p>
|
||
</div>
|
||
</div>
|
||
<div class="flex items-start gap-2 p-3 bg-amber-50 rounded-lg border border-amber-200 text-xs text-amber-700">
|
||
<svg class="w-4 h-4 flex-shrink-0 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||
</svg>
|
||
<span>DeepSeek 暂不提供 Embedding API,知识库功能将自动使用本地默认模型(需下载约 90MB 模型)。其他功能不受影响。</span>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<!-- ── 豆包配置面板 ── -->
|
||
<template x-if="cfg.model_provider==='doubao'">
|
||
<div class="space-y-3 mb-4">
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">豆包 API Key
|
||
<a href="https://console.volcengine.com/ark/" target="_blank" class="ml-1 text-blue-500 text-xs hover:underline font-normal">申请地址 ↗</a>
|
||
</label>
|
||
<input type="password" x-model="cfg.doubao_api_key" placeholder="sk-..."
|
||
class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||
<p x-show="cfg.has_doubao_key" class="text-xs text-green-600 mt-1">✓ 已配置</p>
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">模型</label>
|
||
<select x-model="cfg.doubao_model" class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||
<optgroup label="─── 豆包 1.5 系列(2025 最新)───">
|
||
<option value="doubao-1-5-pro-32k">doubao-1-5-pro-32k ★ 推荐</option>
|
||
<option value="doubao-1-5-pro-128k">doubao-1-5-pro-128k(超长上下文)</option>
|
||
<option value="doubao-1-5-lite-32k">doubao-1-5-lite-32k(快速低价)</option>
|
||
</optgroup>
|
||
<optgroup label="─── 豆包 Pro 系列 ───">
|
||
<option value="doubao-pro-32k">doubao-pro-32k</option>
|
||
<option value="doubao-pro-128k">doubao-pro-128k</option>
|
||
<option value="doubao-pro-256k">doubao-pro-256k(超长)</option>
|
||
</optgroup>
|
||
<optgroup label="─── 豆包 Lite 系列 ───">
|
||
<option value="doubao-lite-32k">doubao-lite-32k</option>
|
||
<option value="doubao-lite-128k">doubao-lite-128k</option>
|
||
</optgroup>
|
||
<optgroup label="─── 自定义 ───">
|
||
<option value="">手动输入模型名</option>
|
||
</optgroup>
|
||
</select>
|
||
<div x-show="!doubaoPresets.includes(cfg.doubao_model)" class="mt-2">
|
||
<input type="text" x-model="cfg.doubao_model" placeholder="输入模型名,如 doubao-1-5-pro-32k"
|
||
class="w-full px-3 py-2 border border-blue-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 font-mono">
|
||
<p class="text-xs text-gray-400 mt-1">输入火山引擎方舟平台支持的任意模型名</p>
|
||
</div>
|
||
</div>
|
||
<!-- 自定义 API 地址 -->
|
||
<div>
|
||
<button type="button" @click="cfg._doubao_adv = !cfg._doubao_adv"
|
||
class="text-xs text-gray-400 hover:text-gray-600 flex items-center gap-1">
|
||
<span x-text="cfg._doubao_adv ? '▾' : '▸'"></span>
|
||
高级:自定义 API 地址(代理/中转)
|
||
</button>
|
||
<div x-show="cfg._doubao_adv" class="mt-2">
|
||
<input type="text" x-model="cfg.doubao_base_url"
|
||
placeholder="https://ark.cn-beijing.volces.com/api/v3"
|
||
class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 font-mono">
|
||
<p class="text-xs text-gray-400 mt-1">默认:https://ark.cn-beijing.volces.com/api/v3</p>
|
||
</div>
|
||
</div>
|
||
<div class="flex items-start gap-2 p-3 bg-amber-50 rounded-lg border border-amber-200 text-xs text-amber-700">
|
||
<svg class="w-4 h-4 flex-shrink-0 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||
</svg>
|
||
<span>豆包暂不提供通用 Embedding API,知识库将自动使用关键词检索模式。其他功能完全正常。</span>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<!-- ── Kimi 配置面板 ── -->
|
||
<template x-if="cfg.model_provider==='kimi'">
|
||
<div class="space-y-3 mb-4">
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">Kimi API Key
|
||
<a href="https://platform.moonshot.cn/" target="_blank" class="ml-1 text-blue-500 text-xs hover:underline font-normal">申请地址 ↗</a>
|
||
</label>
|
||
<input type="password" x-model="cfg.kimi_api_key" placeholder="sk-..."
|
||
class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||
<p x-show="cfg.has_kimi_key" class="text-xs text-green-600 mt-1">✓ 已配置</p>
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">模型</label>
|
||
<select x-model="cfg.kimi_model" class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||
<optgroup label="─── Moonshot 系列 ───">
|
||
<option value="moonshot-v1-32k">moonshot-v1-32k ★ 推荐(均衡)</option>
|
||
<option value="moonshot-v1-128k">moonshot-v1-128k(超长上下文)</option>
|
||
<option value="moonshot-v1-8k">moonshot-v1-8k(快速低价)</option>
|
||
<option value="moonshot-v1-auto">moonshot-v1-auto(自动选择)</option>
|
||
</optgroup>
|
||
<optgroup label="─── 自定义 ───">
|
||
<option value="">手动输入模型名</option>
|
||
</optgroup>
|
||
</select>
|
||
<div x-show="!kimiPresets.includes(cfg.kimi_model)" class="mt-2">
|
||
<input type="text" x-model="cfg.kimi_model" placeholder="输入模型名,如 moonshot-v1-128k"
|
||
class="w-full px-3 py-2 border border-blue-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 font-mono">
|
||
<p class="text-xs text-gray-400 mt-1">输入 Moonshot 平台支持的任意模型名</p>
|
||
</div>
|
||
</div>
|
||
<!-- 自定义 API 地址 -->
|
||
<div>
|
||
<button type="button" @click="cfg._kimi_adv = !cfg._kimi_adv"
|
||
class="text-xs text-gray-400 hover:text-gray-600 flex items-center gap-1">
|
||
<span x-text="cfg._kimi_adv ? '▾' : '▸'"></span>
|
||
高级:自定义 API 地址(代理/中转)
|
||
</button>
|
||
<div x-show="cfg._kimi_adv" class="mt-2">
|
||
<input type="text" x-model="cfg.kimi_base_url"
|
||
placeholder="https://api.moonshot.cn/v1"
|
||
class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 font-mono">
|
||
<p class="text-xs text-gray-400 mt-1">默认:https://api.moonshot.cn/v1</p>
|
||
</div>
|
||
</div>
|
||
<div class="flex items-start gap-2 p-3 bg-teal-50 rounded-lg border border-teal-200 text-xs text-teal-700">
|
||
<svg class="w-4 h-4 flex-shrink-0 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||
</svg>
|
||
<span>Kimi 支持 Embedding API(moonshot-v1-embedding),知识库将使用语义向量检索,效果更佳。</span>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<template x-if="cfg.model_provider==='openai'">
|
||
<div class="space-y-3 mb-4">
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">OpenAI API Key
|
||
<a href="https://platform.openai.com/" target="_blank" class="ml-1 text-blue-500 text-xs hover:underline font-normal">申请地址 ↗</a>
|
||
</label>
|
||
<input type="password" x-model="cfg.openai_api_key" placeholder="sk-..."
|
||
class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||
<p x-show="cfg.has_openai_key" class="text-xs text-green-600 mt-1">✓ 已配置</p>
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">模型</label>
|
||
<select x-model="cfg.openai_model" class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||
<optgroup label="─── GPT-4.1 系列(2025)───">
|
||
<option value="gpt-4.1">gpt-4.1 ★ 推荐(旗舰,1M 上下文)</option>
|
||
<option value="gpt-4.1-mini">gpt-4.1-mini(快速均衡)</option>
|
||
<option value="gpt-4.1-nano">gpt-4.1-nano(最轻量低价)</option>
|
||
</optgroup>
|
||
<optgroup label="─── o 推理系列(深度推理,适合复杂标书)───">
|
||
<option value="o4-mini">o4-mini(推理,高性价比)</option>
|
||
<option value="o3">o3(最强推理,较慢)</option>
|
||
<option value="o3-mini">o3-mini(快速推理)</option>
|
||
<option value="o1">o1(深度推理)</option>
|
||
<option value="o1-mini">o1-mini(推理入门)</option>
|
||
<option value="o1-pro">o1-pro(最高质量推理)</option>
|
||
</optgroup>
|
||
<optgroup label="─── GPT-4o 系列 ───">
|
||
<option value="gpt-4o">gpt-4o</option>
|
||
<option value="gpt-4o-mini">gpt-4o-mini</option>
|
||
</optgroup>
|
||
<optgroup label="─── 旧版 ───">
|
||
<option value="gpt-4-turbo">gpt-4-turbo</option>
|
||
</optgroup>
|
||
<optgroup label="─── 自定义 ───">
|
||
<option value="">手动输入模型名</option>
|
||
</optgroup>
|
||
</select>
|
||
<div x-show="!openaiPresets.includes(cfg.openai_model)" class="mt-2">
|
||
<input type="text" x-model="cfg.openai_model" placeholder="输入模型名,如 gpt-5、gpt-4.1-2025-04-14"
|
||
class="w-full px-3 py-2 border border-blue-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 font-mono">
|
||
<p class="text-xs text-gray-400 mt-1">输入 OpenAI 平台支持的任意模型名</p>
|
||
</div>
|
||
</div>
|
||
<!-- 自定义 API 地址 -->
|
||
<div>
|
||
<button type="button" @click="cfg._oai_adv = !cfg._oai_adv"
|
||
class="text-xs text-gray-400 hover:text-gray-600 flex items-center gap-1">
|
||
<span x-text="cfg._oai_adv ? '▾' : '▸'"></span>
|
||
高级:自定义 API 地址(Azure / 代理 / 中转)
|
||
</button>
|
||
<div x-show="cfg._oai_adv" class="mt-2">
|
||
<input type="text" x-model="cfg.openai_base_url"
|
||
placeholder="https://api.openai.com/v1"
|
||
class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 font-mono">
|
||
<p class="text-xs text-gray-400 mt-1">默认:https://api.openai.com/v1 | Azure 示例:https://YOUR.openai.azure.com/openai/deployments/MODEL</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<template x-if="cfg.model_provider==='ollama'">
|
||
<div class="space-y-3 mb-4">
|
||
<!-- 状态检测 -->
|
||
<div class="flex items-center justify-between p-3 bg-green-50 rounded-lg border border-green-200">
|
||
<div class="flex items-center gap-2 text-xs text-green-700">
|
||
<svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||
</svg>
|
||
<span>本地运行,数据不上传云端,完全免费</span>
|
||
</div>
|
||
<button type="button" @click="testOllama()"
|
||
class="text-xs px-2 py-1 bg-green-600 hover:bg-green-700 text-white rounded-lg transition flex-shrink-0">
|
||
检测连接
|
||
</button>
|
||
</div>
|
||
|
||
<!-- Ollama 服务地址 -->
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">
|
||
Ollama 服务地址
|
||
<span class="ml-1 text-xs text-gray-400 font-normal">(默认本机)</span>
|
||
</label>
|
||
<input type="text" x-model="cfg.ollama_base_url" placeholder="http://localhost:11434/v1"
|
||
class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-green-500 font-mono">
|
||
</div>
|
||
|
||
<!-- 模型选择 -->
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">选择模型</label>
|
||
<select x-model="cfg.ollama_model"
|
||
class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-green-500">
|
||
<optgroup label="★ 推荐:标书写作首选">
|
||
<option value="qwen3:8b">qwen3:8b ★ 推荐入门(约 5 GB)</option>
|
||
<option value="qwen3:14b">qwen3:14b ★ 推荐均衡(约 9 GB)</option>
|
||
<option value="qwen3:32b">qwen3:32b ★ 推荐高质量(约 20 GB)</option>
|
||
<option value="deepseek-r1:14b">deepseek-r1:14b ★ 推荐推理(约 9 GB)</option>
|
||
<option value="deepseek-r1:32b">deepseek-r1:32b ★ 推荐高质量推理(约 20 GB)</option>
|
||
</optgroup>
|
||
<optgroup label="─── Qwen3 系列(阿里,2025最新)───">
|
||
<option value="qwen3:0.6b">qwen3:0.6b(最轻量,约 0.5 GB)</option>
|
||
<option value="qwen3:1.7b">qwen3:1.7b(约 1 GB)</option>
|
||
<option value="qwen3:4b">qwen3:4b(约 2.5 GB)</option>
|
||
<option value="qwen3:8b">qwen3:8b(约 5 GB)</option>
|
||
<option value="qwen3:14b">qwen3:14b(约 9 GB)</option>
|
||
<option value="qwen3:32b">qwen3:32b(约 20 GB)</option>
|
||
<option value="qwen3:30b-a3b">qwen3:30b-a3b(MoE 高效,约 19 GB)</option>
|
||
<option value="qwen3:235b-a22b">qwen3:235b-a22b(MoE 旗舰,约 142 GB)</option>
|
||
</optgroup>
|
||
<optgroup label="─── Qwen2.5 系列(阿里)───">
|
||
<option value="qwen2.5:0.5b">qwen2.5:0.5b(约 0.4 GB)</option>
|
||
<option value="qwen2.5:1.5b">qwen2.5:1.5b(约 1 GB)</option>
|
||
<option value="qwen2.5:3b">qwen2.5:3b(约 2 GB)</option>
|
||
<option value="qwen2.5:7b">qwen2.5:7b(约 4.7 GB)</option>
|
||
<option value="qwen2.5:14b">qwen2.5:14b(约 9 GB)</option>
|
||
<option value="qwen2.5:32b">qwen2.5:32b(约 20 GB)</option>
|
||
<option value="qwen2.5:72b">qwen2.5:72b(约 47 GB)</option>
|
||
</optgroup>
|
||
<optgroup label="─── Qwen2.5-Coder 系列(代码增强)───">
|
||
<option value="qwen2.5-coder:1.5b">qwen2.5-coder:1.5b(约 1 GB)</option>
|
||
<option value="qwen2.5-coder:3b">qwen2.5-coder:3b(约 2 GB)</option>
|
||
<option value="qwen2.5-coder:7b">qwen2.5-coder:7b(约 4.7 GB)</option>
|
||
<option value="qwen2.5-coder:14b">qwen2.5-coder:14b(约 9 GB)</option>
|
||
<option value="qwen2.5-coder:32b">qwen2.5-coder:32b(约 20 GB)</option>
|
||
</optgroup>
|
||
<optgroup label="─── QwQ 系列(阿里深度推理)───">
|
||
<option value="qwq:32b">qwq:32b(深度推理,约 20 GB)</option>
|
||
</optgroup>
|
||
<optgroup label="─── DeepSeek R1 系列(推理增强)───">
|
||
<option value="deepseek-r1:1.5b">deepseek-r1:1.5b(约 1 GB)</option>
|
||
<option value="deepseek-r1:7b">deepseek-r1:7b(约 4.7 GB)</option>
|
||
<option value="deepseek-r1:8b">deepseek-r1:8b(约 5 GB)</option>
|
||
<option value="deepseek-r1:14b">deepseek-r1:14b(约 9 GB)</option>
|
||
<option value="deepseek-r1:32b">deepseek-r1:32b(约 20 GB)</option>
|
||
<option value="deepseek-r1:70b">deepseek-r1:70b(约 43 GB)</option>
|
||
<option value="deepseek-r1:671b">deepseek-r1:671b(原版,需超大显存)</option>
|
||
</optgroup>
|
||
<optgroup label="─── DeepSeek V2 系列 ───">
|
||
<option value="deepseek-v2:16b">deepseek-v2:16b(Lite,约 10 GB)</option>
|
||
<option value="deepseek-v2:236b">deepseek-v2:236b(全量,约 150 GB)</option>
|
||
</optgroup>
|
||
<optgroup label="─── DeepSeek V3 系列 ───">
|
||
<option value="deepseek-v3:7b">deepseek-v3:7b(约 4.7 GB)</option>
|
||
<option value="deepseek-v3:671b">deepseek-v3:671b(完整版,需超大显存)</option>
|
||
</optgroup>
|
||
<optgroup label="─── 自定义 ───">
|
||
<option value="">手动输入模型名</option>
|
||
</optgroup>
|
||
</select>
|
||
</div>
|
||
|
||
<!-- 自定义模型名(选中"手动输入"或填入了预设外的值时显示) -->
|
||
<div x-show="!ollamaPresets.includes(cfg.ollama_model)">
|
||
<input type="text" x-model="cfg.ollama_model" placeholder="例如:qwen3:latest 或 deepseek-r1:latest"
|
||
class="w-full px-3 py-2 border border-green-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-green-500 font-mono">
|
||
<p class="text-xs text-gray-400 mt-1">请输入已通过 <code class="bg-gray-100 px-1 rounded">ollama pull <模型名></code> 下载的模型名</p>
|
||
</div>
|
||
|
||
<div class="flex items-start gap-2 p-3 bg-amber-50 rounded-lg border border-amber-200 text-xs text-amber-700">
|
||
<svg class="w-4 h-4 flex-shrink-0 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||
</svg>
|
||
<span>使用前请先安装 <a href="https://ollama.com/" target="_blank" class="underline">Ollama</a> 并下载模型,例如:<br>
|
||
<code class="bg-amber-100 px-1 rounded">ollama pull qwen3:14b</code>(Qwen3 推荐)
|
||
<code class="bg-amber-100 px-1 rounded">ollama pull deepseek-r1:14b</code>(DeepSeek R1 推荐)<br>
|
||
Ollama 本地不支持知识库 Embedding,该功能将自动回退到本地模型。推理模型(R1/QwQ)可能输出 <code class="bg-amber-100 px-1 rounded"><think></code> 标签,不影响正文使用。</span>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<!-- 标书篇幅设置 -->
|
||
<div class="mb-4 pt-3 border-t border-gray-100">
|
||
<p class="text-xs text-gray-500 mb-3 p-2 bg-slate-50 rounded-lg">
|
||
全稿「目标总页数」在<strong>已打开的标书项目</strong>中,到 <strong>步骤1「解析」</strong> 里设置,与下方「每节字数档」是两项不同设置。
|
||
</p>
|
||
<label class="block text-sm font-medium text-gray-700 mb-2">
|
||
标书篇幅预期
|
||
<span class="ml-1 text-xs text-gray-400 font-normal">(控制每个章节生成内容的字数)</span>
|
||
</label>
|
||
<div class="grid grid-cols-2 gap-2">
|
||
<label class="flex items-center gap-2.5 p-3 border-2 rounded-xl cursor-pointer transition"
|
||
:class="cfg.content_volume==='concise' ? 'border-blue-500 bg-blue-50' : 'border-gray-200 hover:border-gray-300'">
|
||
<input type="radio" name="volume" value="concise" x-model="cfg.content_volume" class="accent-blue-600">
|
||
<div>
|
||
<p class="font-medium text-sm leading-tight">精简版</p>
|
||
<p class="text-xs text-gray-400">每节约 1200 字</p>
|
||
</div>
|
||
</label>
|
||
<label class="flex items-center gap-2.5 p-3 border-2 rounded-xl cursor-pointer transition"
|
||
:class="cfg.content_volume==='standard' ? 'border-blue-500 bg-blue-50' : 'border-gray-200 hover:border-gray-300'">
|
||
<input type="radio" name="volume" value="standard" x-model="cfg.content_volume" class="accent-blue-600">
|
||
<div>
|
||
<p class="font-medium text-sm leading-tight">标准版(推荐)</p>
|
||
<p class="text-xs text-gray-400">每节约 2000 字</p>
|
||
</div>
|
||
</label>
|
||
<label class="flex items-center gap-2.5 p-3 border-2 rounded-xl cursor-pointer transition"
|
||
:class="cfg.content_volume==='detailed' ? 'border-blue-500 bg-blue-50' : 'border-gray-200 hover:border-gray-300'">
|
||
<input type="radio" name="volume" value="detailed" x-model="cfg.content_volume" class="accent-blue-600">
|
||
<div>
|
||
<p class="font-medium text-sm leading-tight">详细版</p>
|
||
<p class="text-xs text-gray-400">每节约 3000 字</p>
|
||
</div>
|
||
</label>
|
||
<label class="flex items-center gap-2.5 p-3 border-2 rounded-xl cursor-pointer transition"
|
||
:class="cfg.content_volume==='full' ? 'border-blue-500 bg-blue-50' : 'border-gray-200 hover:border-gray-300'">
|
||
<input type="radio" name="volume" value="full" x-model="cfg.content_volume" class="accent-blue-600">
|
||
<div>
|
||
<p class="font-medium text-sm leading-tight">充实版</p>
|
||
<p class="text-xs text-gray-400">每节约 4000 字</p>
|
||
</div>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 并发生成设置 -->
|
||
<div class="mb-4 pt-3 border-t border-gray-100">
|
||
<label class="block text-sm font-medium text-gray-700 mb-2">
|
||
并发生成章节数
|
||
<span class="ml-1 text-xs text-gray-400 font-normal">(同时调用 AI 的线程数,越大越快但需注意 API 限流)</span>
|
||
</label>
|
||
<div class="flex items-center gap-3">
|
||
<input type="range" min="1" max="20" step="1" x-model.number="cfg.max_concurrent"
|
||
class="flex-1 accent-blue-600">
|
||
<span class="w-12 text-center text-sm font-bold text-blue-600 bg-blue-50 rounded-lg py-1"
|
||
x-text="cfg.max_concurrent + ' 路'"></span>
|
||
</div>
|
||
<div class="flex justify-between text-xs text-gray-400 mt-1 px-0.5">
|
||
<span>保守(1路)</span>
|
||
<span>推荐(8-12路)</span>
|
||
<span>极速(20路)</span>
|
||
</div>
|
||
<p class="text-[10px] text-amber-600 mt-1">全局LLM信号量上限20,已集成速率保护。Qwen云建议≤15避免限流。</p>
|
||
</div>
|
||
|
||
<div class="flex gap-3 mt-2">
|
||
<button @click="showConfig=false" class="flex-1 px-4 py-2 border border-gray-200 text-gray-600 rounded-xl text-sm hover:bg-gray-50 transition">取消</button>
|
||
<button @click="saveConfig()" class="flex-1 px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-xl text-sm font-medium transition">保存配置</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 文件样式设置 Modal (full replica of the provided image UI) -->
|
||
<div x-show="showStyleSettings" class="fixed inset-0 bg-black/60 flex items-center justify-center z-[9999] p-4" @click.self="showStyleSettings = false">
|
||
<div class="bg-white rounded-3xl shadow-2xl max-w-6xl w-full max-h-[92vh] overflow-hidden flex flex-col" @click.stop>
|
||
<!-- Header -->
|
||
<div class="px-8 py-5 border-b flex items-center justify-between bg-gradient-to-r from-indigo-50 to-blue-50">
|
||
<div class="flex items-center gap-3">
|
||
<div class="w-8 h-8 bg-indigo-600 text-white rounded-2xl flex items-center justify-center text-xl">📐</div>
|
||
<div>
|
||
<h2 class="text-2xl font-bold text-gray-900">文件样式设置</h2>
|
||
<p class="text-sm text-gray-500">定义标书导出Word的完整格式规范,支持方案保存与复用</p>
|
||
</div>
|
||
</div>
|
||
<div class="flex items-center gap-4">
|
||
<select x-model="stylePreset" class="px-4 py-2 border border-gray-300 rounded-2xl text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500">
|
||
<option value="standard">标准投标格式 (100页, 宋体小四, 黑体标题)</option>
|
||
<option value="detailed">详细技术方案 (200页, 宋体小四, 更多图表)</option>
|
||
<option value="concise">精简版 (50页, 紧凑布局)</option>
|
||
<option value="engineering">工程施工组织设计 (标准工程标)</option>
|
||
<option value="service">服务实施方案 (服务类专用)</option>
|
||
<option value="goods">货物技术规格书 (货物类专用)</option>
|
||
<option value="long">超长版 (300页, 详细目录)</option>
|
||
<option value="custom">自定义方案</option>
|
||
</select>
|
||
<button @click="saveStylePreset()" class="px-6 py-2 bg-indigo-600 hover:bg-indigo-700 text-white text-sm font-medium rounded-2xl transition flex items-center gap-2">
|
||
<span>保存方案</span>
|
||
</button>
|
||
<button @click="showStyleSettings = false" class="text-gray-400 hover:text-gray-600">✕</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Body - Replica of image layout -->
|
||
<div class="flex-1 overflow-auto p-8 bg-slate-50">
|
||
<div class="grid grid-cols-12 gap-8">
|
||
|
||
<!-- Left column: Type and presets -->
|
||
<div class="col-span-5 space-y-8">
|
||
<!-- 正文类型 -->
|
||
<div>
|
||
<div class="flex items-center justify-between mb-4">
|
||
<h3 class="font-semibold text-lg">正文类型</h3>
|
||
<div class="flex gap-6 text-sm">
|
||
<label class="flex items-center gap-2 cursor-pointer">
|
||
<input type="radio" :checked="bodyMode==='text_only'" @change="bodyMode='text_only'" class="accent-indigo-600">
|
||
<span>正文生成</span>
|
||
</label>
|
||
<label class="flex items-center gap-2 cursor-pointer">
|
||
<input type="radio" :checked="bodyMode==='with_figures_tables'" @change="bodyMode='with_figures_tables'" class="accent-indigo-600">
|
||
<span>图文并茂</span>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
<div class="grid grid-cols-4 gap-3">
|
||
<div @click="figureEnabled = !figureEnabled" class="bg-white border-2 rounded-2xl p-3 cursor-pointer transition" :class="figureEnabled ? 'border-indigo-500 shadow' : 'border-gray-200'">
|
||
<img src="https://via.placeholder.com/80x110/4f46e5/ffffff?text=图文" class="mx-auto mb-2 rounded">
|
||
<p class="text-center text-xs font-medium">图文</p>
|
||
</div>
|
||
<div class="bg-white border-2 border-indigo-500 rounded-2xl p-3 shadow ring-2 ring-indigo-200">
|
||
<img src="https://via.placeholder.com/80x110/10b981/ffffff?text=纯文本" class="mx-auto mb-2 rounded">
|
||
<p class="text-center text-xs font-medium text-indigo-600">纯文本</p>
|
||
</div>
|
||
<!-- More layout presets from image -->
|
||
<div class="bg-white border border-gray-200 rounded-2xl p-3 cursor-pointer hover:border-gray-300 transition">
|
||
<img src="https://via.placeholder.com/80x110/64748b/ffffff?text=左右" class="mx-auto mb-2 rounded">
|
||
<p class="text-center text-xs">左右栏</p>
|
||
</div>
|
||
<div class="bg-white border border-gray-200 rounded-2xl p-3 cursor-pointer hover:border-gray-300 transition">
|
||
<img src="https://via.placeholder.com/80x110/64748b/ffffff?text=上下" class="mx-auto mb-2 rounded">
|
||
<p class="text-center text-xs">上下栏</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 序号样式 (Word目录格式 presets) -->
|
||
<div>
|
||
<h3 class="font-semibold text-lg mb-4">序号样式</h3>
|
||
<div class="grid grid-cols-5 gap-3">
|
||
<div class="bg-white border-2 border-emerald-500 rounded-2xl p-2 cursor-pointer shadow">
|
||
<div class="h-20 bg-emerald-100 rounded flex items-center justify-center text-xs font-mono">一、</div>
|
||
<p class="text-center text-[10px] mt-1 text-emerald-700">标准</p>
|
||
</div>
|
||
<!-- Additional presets from image (1.1, (一), etc.) -->
|
||
<div class="bg-white border border-gray-200 rounded-2xl p-2 cursor-pointer hover:border-gray-300">
|
||
<div class="h-20 bg-gray-100 rounded flex items-center justify-center text-xs font-mono">1.1</div>
|
||
<p class="text-center text-[10px] mt-1">数字</p>
|
||
</div>
|
||
<div class="bg-white border border-gray-200 rounded-2xl p-2 cursor-pointer hover:border-gray-300">
|
||
<div class="h-20 bg-gray-100 rounded flex items-center justify-center text-xs font-mono">(一)</div>
|
||
<p class="text-center text-[10px] mt-1">括号</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 封面样式 presets -->
|
||
<div>
|
||
<h3 class="font-semibold text-lg mb-4">封面样式</h3>
|
||
<div class="grid grid-cols-5 gap-3">
|
||
<div class="border-2 border-blue-500 rounded-2xl overflow-hidden cursor-pointer shadow">
|
||
<img src="https://via.placeholder.com/80x110/1e40af/ffffff?text=投标文件" class="w-full">
|
||
<p class="text-center text-xs py-1 bg-blue-50 text-blue-700">投标文件</p>
|
||
</div>
|
||
<div class="border border-gray-200 rounded-2xl overflow-hidden cursor-pointer hover:border-gray-300">
|
||
<img src="https://via.placeholder.com/80x110/334155/ffffff?text=投标书" class="w-full">
|
||
<p class="text-center text-xs py-1">投标书</p>
|
||
</div>
|
||
<!-- More from image: 投标文件, 投标技术文件, etc. -->
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Right column: Detailed controls (正文文字, 标题, 落款, 目录, 页眉页脚, 页数) -->
|
||
<div class="col-span-7 space-y-8 bg-white p-6 rounded-3xl border">
|
||
<!-- 正文文字 -->
|
||
<div class="border-b pb-6">
|
||
<h4 class="font-semibold mb-4 flex items-center gap-2"><span class="text-emerald-500">正文文字</span></h4>
|
||
<div class="grid grid-cols-2 gap-x-8 gap-y-4 text-sm">
|
||
<div>
|
||
<label class="block text-xs text-gray-500 mb-1">字体</label>
|
||
<select x-model="bodyFont" class="w-full border border-gray-300 rounded-xl px-3 py-2">
|
||
<option>宋体</option>
|
||
<option>黑体</option>
|
||
<option>仿宋_GB2312</option>
|
||
<option>楷体_GB2312</option>
|
||
<option>隶书</option>
|
||
<option>微软雅黑</option>
|
||
<option>等线</option>
|
||
<option>方正小标宋</option>
|
||
<option>华文细黑</option>
|
||
<option>Arial</option>
|
||
<option>Times New Roman</option>
|
||
<option>Calibri</option>
|
||
<option>Georgia</option>
|
||
<option>Verdana</option>
|
||
<option>Courier New</option>
|
||
</select>
|
||
</div>
|
||
<div>
|
||
<label class="block text-xs text-gray-500 mb-1">字号</label>
|
||
<select x-model="bodySize" class="w-full border border-gray-300 rounded-xl px-3 py-2">
|
||
<option>初号 (42pt)</option>
|
||
<option>一号 (26pt)</option>
|
||
<option>二号 (22pt)</option>
|
||
<option>三号 (16pt)</option>
|
||
<option>小三 (15pt)</option>
|
||
<option>四号 (14pt)</option>
|
||
<option>小四 (12pt)</option>
|
||
<option>五号 (10.5pt)</option>
|
||
<option>小五 (9pt)</option>
|
||
<option>六号 (7.5pt)</option>
|
||
<option>小六 (6.5pt)</option>
|
||
<option>七号 (5.5pt)</option>
|
||
<option>八号 (5pt)</option>
|
||
</select>
|
||
</div>
|
||
<div>
|
||
<label class="block text-xs text-gray-500 mb-1">行距</label>
|
||
<select x-model="bodyLineSpacing" class="w-full border border-gray-300 rounded-xl px-3 py-2">
|
||
<option value="1.0">1.0倍 (单倍)</option>
|
||
<option value="1.15">1.15倍 (Office默认)</option>
|
||
<option value="1.5" selected>1.5倍 (标准标书)</option>
|
||
<option value="2.0">2.0倍 (宽松)</option>
|
||
<option value="18">固定 18pt</option>
|
||
<option value="20">固定 20pt</option>
|
||
<option value="22">固定 22pt</option>
|
||
<option value="24">固定 24pt (推荐)</option>
|
||
<option value="26">固定 26pt</option>
|
||
<option value="28">固定 28pt</option>
|
||
<option value="30">固定 30pt</option>
|
||
</select>
|
||
</div>
|
||
<div>
|
||
<label class="block text-xs text-gray-500 mb-1">首行缩进</label>
|
||
<select x-model="bodyIndent" class="w-full border border-gray-300 rounded-xl px-3 py-2">
|
||
<option value="0">无缩进</option>
|
||
<option value="1">1字符</option>
|
||
<option value="2" selected>2字符 (标准)</option>
|
||
<option value="3">3字符</option>
|
||
<option value="24">24磅</option>
|
||
<option value="28">28磅</option>
|
||
<option value="32">32磅</option>
|
||
<option value="36">36磅 (宽松)</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 标题文字, 落款, 目录, 页眉页脚 (abbreviated for space but full in real) -->
|
||
<div class="grid grid-cols-2 gap-8">
|
||
<div>
|
||
<h4 class="font-semibold mb-3">标题文字</h4>
|
||
<div class="grid grid-cols-2 gap-4 text-xs">
|
||
<div>
|
||
<label class="block text-[10px] text-gray-500">一级标题</label>
|
||
<select x-model="heading1Font" class="w-full border border-gray-300 rounded-xl px-2 py-1 text-[10px]">
|
||
<option>黑体</option>
|
||
<option>宋体</option>
|
||
<option>仿宋</option>
|
||
<option>楷体</option>
|
||
<option>隶书</option>
|
||
<option>微软雅黑</option>
|
||
</select>
|
||
<select x-model="heading1Size" class="w-full border border-gray-300 rounded-xl px-2 py-1 text-[10px] mt-1">
|
||
<option>一号 (26pt)</option>
|
||
<option>二号 (22pt)</option>
|
||
<option selected>三号 (16pt)</option>
|
||
<option>小三 (15pt)</option>
|
||
<option>四号 (14pt)</option>
|
||
</select>
|
||
</div>
|
||
<div>
|
||
<label class="block text-[10px] text-gray-500">二级标题</label>
|
||
<select x-model="heading2Font" class="w-full border border-gray-300 rounded-xl px-2 py-1 text-[10px]">
|
||
<option>黑体</option>
|
||
<option>宋体</option>
|
||
<option selected>仿宋</option>
|
||
<option>楷体</option>
|
||
</select>
|
||
<select x-model="heading2Size" class="w-full border border-gray-300 rounded-xl px-2 py-1 text-[10px] mt-1">
|
||
<option>三号 (16pt)</option>
|
||
<option selected>四号 (14pt)</option>
|
||
<option>小四 (12pt)</option>
|
||
<option>五号 (10.5pt)</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<h4 class="font-semibold mb-3">页眉页脚</h4>
|
||
<div class="bg-slate-50 p-3 rounded-2xl text-xs space-y-2">
|
||
<div>页眉: <span class="font-medium" x-text="headerText"></span></div>
|
||
<input x-model="headerText" class="w-full text-xs border rounded-xl px-3 py-1">
|
||
<div>页脚: <span class="font-medium" x-text="footerText"></span></div>
|
||
<input x-model="footerText" class="w-full text-xs border rounded-xl px-3 py-1">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 方案页数 slider (bottom of image) -->
|
||
<div class="pt-4 border-t">
|
||
<div class="flex justify-between text-sm mb-2">
|
||
<span class="font-medium">方案页数</span>
|
||
<span class="font-mono text-indigo-600" x-text="pageCountTarget + ' 页'"></span>
|
||
</div>
|
||
<input type="range" min="5" max="5000" step="5" x-model.number="pageCountTarget" class="w-full accent-indigo-600">
|
||
<div class="flex justify-between text-[10px] text-gray-400 mt-1">
|
||
<span>5</span>
|
||
<span>5000</span>
|
||
</div>
|
||
<p class="text-xs text-gray-500 mt-3">选择页数范围后,系统将根据设置自动调整字数、行距、页眉等参数,生成符合文件样式规范的Word文档。</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Footer actions -->
|
||
<div class="px-8 py-4 border-t bg-white flex justify-end gap-3">
|
||
<button @click="showStyleSettings = false" class="px-6 py-2.5 text-gray-600 hover:bg-gray-100 rounded-2xl">取消</button>
|
||
<button @click="saveStylePreset(); showStyleSettings = false" class="px-8 py-2.5 bg-indigo-600 hover:bg-indigo-700 text-white rounded-2xl font-medium">应用到当前项目并保存</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
function app() {
|
||
return {
|
||
projects: [],
|
||
loading: true,
|
||
showCreate: false,
|
||
showConfig: false,
|
||
showStyleSettings: false,
|
||
newProjectName: '',
|
||
creating: false,
|
||
// Style settings state (full replica of image UI with many dropdown options)
|
||
stylePreset: 'standard',
|
||
bodyMode: 'text_only',
|
||
figureEnabled: true,
|
||
tableEnabled: true,
|
||
bodyFont: '宋体',
|
||
bodySize: '小四',
|
||
bodyLineSpacing: 1.5,
|
||
bodyIndent: 2,
|
||
heading1Font: '黑体',
|
||
heading1Size: '三号',
|
||
heading2Font: '黑体',
|
||
heading2Size: '四号',
|
||
footerFont: '宋体',
|
||
footerSize: '五号',
|
||
pageCountTarget: 100,
|
||
margins: { top: 2.54, bottom: 2.54, left: 3.18, right: 3.18 },
|
||
headerText: '标桥AI编标',
|
||
footerText: '第 X 页 / 共 Y 页',
|
||
alignment: '两端对齐',
|
||
cfg: {
|
||
model_provider: 'qwen',
|
||
target_pages: 0,
|
||
qwen_api_key: '', qwen_model: 'qwen3.6-plus', qwen_base_url: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
|
||
openai_api_key: '', openai_model: 'gpt-4.1', openai_base_url: 'https://api.openai.com/v1',
|
||
deepseek_api_key: '', deepseek_model: 'deepseek-chat', deepseek_base_url: 'https://api.deepseek.com/v1',
|
||
ollama_base_url: 'http://localhost:11434/v1', ollama_model: 'qwen3:8b',
|
||
doubao_api_key: '', doubao_model: 'doubao-1-5-pro-32k', doubao_base_url: 'https://ark.cn-beijing.volces.com/api/v3',
|
||
kimi_api_key: '', kimi_model: 'moonshot-v1-32k', kimi_base_url: 'https://api.moonshot.cn/v1',
|
||
max_concurrent: 12, content_volume: 'standard', // 默认提升至12,支持极速20并发
|
||
_qwen_adv: false, _ds_adv: false, _oai_adv: false, _doubao_adv: false, _kimi_adv: false,
|
||
},
|
||
qwenPresets: [
|
||
'qwen3.6-plus',
|
||
'qwen-max','qwen-max-latest','qwen-plus','qwen-plus-latest',
|
||
'qwen-turbo','qwen-turbo-latest','qwen-long',
|
||
'qwen3-235b-a22b','qwen3-32b','qwen3-30b-a3b','qwen3-14b','qwen3-8b',
|
||
],
|
||
deepseekPresets: ['deepseek-chat','deepseek-reasoner'],
|
||
openaiPresets: [
|
||
'gpt-4.1','gpt-4.1-mini','gpt-4.1-nano',
|
||
'o4-mini','o3','o3-mini','o1','o1-mini','o1-pro',
|
||
'gpt-4o','gpt-4o-mini','gpt-4-turbo',
|
||
],
|
||
doubaoPresets: [
|
||
'doubao-1-5-pro-32k', 'doubao-1-5-pro-128k', 'doubao-1-5-lite-32k',
|
||
'doubao-pro-32k', 'doubao-pro-128k', 'doubao-pro-256k',
|
||
'doubao-lite-32k', 'doubao-lite-128k',
|
||
],
|
||
kimiPresets: [
|
||
'moonshot-v1-8k', 'moonshot-v1-32k', 'moonshot-v1-128k', 'moonshot-v1-auto',
|
||
],
|
||
ollamaPresets: [
|
||
// 推荐
|
||
'qwen3:8b','qwen3:14b','qwen3:32b','deepseek-r1:14b','deepseek-r1:32b',
|
||
// Qwen3
|
||
'qwen3:0.6b','qwen3:1.7b','qwen3:4b','qwen3:30b-a3b','qwen3:235b-a22b',
|
||
// Qwen2.5
|
||
'qwen2.5:0.5b','qwen2.5:1.5b','qwen2.5:3b','qwen2.5:7b','qwen2.5:14b','qwen2.5:32b','qwen2.5:72b',
|
||
// Qwen2.5-Coder
|
||
'qwen2.5-coder:1.5b','qwen2.5-coder:3b','qwen2.5-coder:7b','qwen2.5-coder:14b','qwen2.5-coder:32b',
|
||
// QwQ
|
||
'qwq:32b',
|
||
// DeepSeek R1
|
||
'deepseek-r1:1.5b','deepseek-r1:7b','deepseek-r1:8b','deepseek-r1:70b','deepseek-r1:671b',
|
||
// DeepSeek V2
|
||
'deepseek-v2:16b','deepseek-v2:236b',
|
||
// DeepSeek V3
|
||
'deepseek-v3:7b','deepseek-v3:671b',
|
||
],
|
||
|
||
async init() {
|
||
await Promise.all([this.loadProjects(), this.loadConfig()])
|
||
this.loadDefaultStyle() // Load style settings for the new homepage menu
|
||
this.loading = false
|
||
},
|
||
|
||
async loadProjects() {
|
||
const res = await fetch('/api/projects')
|
||
const data = await res.json()
|
||
this.projects = data.projects || []
|
||
},
|
||
|
||
async loadConfig() {
|
||
const res = await fetch('/api/config')
|
||
const data = await res.json()
|
||
this.cfg = { ...this.cfg, ...data }
|
||
},
|
||
|
||
async createProject() {
|
||
if (!this.newProjectName.trim()) return
|
||
this.creating = true
|
||
const res = await fetch('/api/projects', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ name: this.newProjectName.trim() })
|
||
})
|
||
const data = await res.json()
|
||
this.creating = false
|
||
this.showCreate = false
|
||
this.newProjectName = ''
|
||
if (data.id) {
|
||
window.location = '/project/' + data.id
|
||
}
|
||
},
|
||
|
||
async deleteProject(id, name) {
|
||
if (!confirm(`确定要删除项目"${name}"吗?此操作不可恢复。`)) return
|
||
await fetch('/api/projects/' + id, { method: 'DELETE' })
|
||
this.projects = this.projects.filter(p => p.id !== id)
|
||
},
|
||
|
||
async saveConfig() {
|
||
await fetch('/api/config', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify(this.cfg)
|
||
})
|
||
this.showConfig = false
|
||
alert('配置已保存')
|
||
},
|
||
|
||
// Style settings methods for homepage menu
|
||
async saveStylePreset() {
|
||
const presetData = {
|
||
name: this.stylePreset,
|
||
config: {
|
||
bodyMode: this.bodyMode,
|
||
figureEnabled: this.figureEnabled,
|
||
tableEnabled: this.tableEnabled,
|
||
bodyFont: this.bodyFont,
|
||
bodySize: this.bodySize,
|
||
bodyLineSpacing: this.bodyLineSpacing,
|
||
headingFont: this.headingFont,
|
||
headingSize: this.headingSize,
|
||
footerFont: this.footerFont,
|
||
footerSize: this.footerSize,
|
||
pageCountTarget: this.pageCountTarget,
|
||
margins: this.margins,
|
||
headerText: this.headerText,
|
||
footerText: this.footerText,
|
||
}
|
||
}
|
||
const res = await fetch('/api/styles', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify(presetData)
|
||
})
|
||
const data = await res.json()
|
||
if (data.success) {
|
||
alert('样式方案已保存!可在导出时选择使用。')
|
||
} else {
|
||
alert('保存失败: ' + (data.error || '未知错误'))
|
||
}
|
||
},
|
||
|
||
loadDefaultStyle() {
|
||
// Load from API or default
|
||
this.stylePreset = 'standard'
|
||
this.bodyMode = 'text_only'
|
||
this.figureEnabled = true
|
||
this.tableEnabled = true
|
||
this.bodyFont = '宋体'
|
||
this.bodySize = '小四'
|
||
this.bodyLineSpacing = 1.5
|
||
this.headingFont = '黑体'
|
||
this.headingSize = '三号'
|
||
this.footerFont = '宋体'
|
||
this.footerSize = '五号'
|
||
this.pageCountTarget = 100
|
||
this.margins = { top: 2.54, bottom: 2.54, left: 3.18, right: 3.18 }
|
||
this.headerText = '标桥AI编标'
|
||
this.footerText = '第 X 页 / 共 Y 页'
|
||
},
|
||
|
||
async testOllama() {
|
||
const baseUrl = (this.cfg.ollama_base_url || 'http://localhost:11434/v1').replace(/\/v1\/?$/, '')
|
||
try {
|
||
const res = await fetch(baseUrl + '/api/tags', { signal: AbortSignal.timeout(3000) })
|
||
if (res.ok) {
|
||
const data = await res.json()
|
||
const models = (data.models || []).map(m => m.name).join('、') || '(暂无已下载模型)'
|
||
alert('✅ Ollama 连接成功!\n已下载模型:' + models)
|
||
} else {
|
||
alert('⚠️ Ollama 已启动,但返回状态异常:' + res.status)
|
||
}
|
||
} catch (e) {
|
||
alert('❌ 无法连接到 Ollama(' + (this.cfg.ollama_base_url || 'http://localhost:11434/v1') + ')\n\n请确认:\n1. 已安装 Ollama(https://ollama.com)\n2. Ollama 服务正在运行\n3. 服务地址填写正确')
|
||
}
|
||
},
|
||
|
||
formatDate(dt) {
|
||
if (!dt) return ''
|
||
const d = new Date(dt)
|
||
return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`
|
||
},
|
||
|
||
statusBadge(status) {
|
||
const map = {
|
||
'none': { text: '未上传', cls: 'bg-gray-100 text-gray-500' },
|
||
'uploaded': { text: '待解析', cls: 'bg-yellow-100 text-yellow-700' },
|
||
'parsing': { text: '解析中', cls: 'bg-blue-100 text-blue-700' },
|
||
'done': { text: '已解析', cls: 'bg-green-100 text-green-700' },
|
||
'error': { text: '解析失败', cls: 'bg-red-100 text-red-600' },
|
||
}
|
||
return map[status] || map['none']
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<!-- 页脚版权声明 -->
|
||
<footer class="mt-auto py-4 px-6 border-t border-gray-200 bg-white text-center text-xs text-gray-500 space-y-1">
|
||
<p class="font-medium text-gray-600">© 标书老崔</p>
|
||
<p>本工具仅限学习交流免费使用,生成的技术方案请人工核对。本工具不会在任何平台售卖,请注意甄别。</p>
|
||
</footer>
|
||
</body>
|
||
</html>
|