Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions WordFormatUI/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>WordFormat - 论文格式自动化处理工具</title>
<!-- Google Fonts: Crimson Pro (headings) + Atkinson Hyperlegible (body) -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Atkinson+Hyperlegible:wght@400;700&family=Crimson+Pro:wght@400;500;600;700&display=swap" rel="stylesheet" />
</head>

<body>
Expand Down
74 changes: 64 additions & 10 deletions WordFormatUI/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
<button class="btn secondary-btn" @click="loadConfig">
加载配置
</button>
<button class="btn secondary-btn" @click="saveConfigToServer" :disabled="!generatedConfig">
保存到服务器
</button>
</div>

<!-- 标签页切换 -->
Expand All @@ -34,16 +37,20 @@
</div>
</div>

<!-- 内容区域 -->
<div class="content-area">
<!-- 配置生成器 -->
<ConfigGenerator ref="configGeneratorRef" v-show="activeTab === 'config'" @config-updated="handleConfigUpdated"/>
<!-- 主区域:侧边栏 + 内容 -->
<div class="main-area">
<ConfigSidebar v-if="activeTab === 'config'" @config-selected="onServerConfigSelected" />

<div class="content-area">
<!-- 配置生成器 -->
<ConfigGenerator ref="configGeneratorRef" v-show="activeTab === 'config'" @config-updated="handleConfigUpdated"/>

<!-- 文档标签核对工具 -->
<DocTagChecker v-show="activeTab === 'checker'" :generated-config="generatedConfig"/>
<!-- 文档标签核对工具 -->
<DocTagChecker v-show="activeTab === 'checker'" :generated-config="generatedConfig"/>

<!-- 设置页面 -->
<SettingsPage v-show="activeTab === 'settings'" />
<!-- 设置页面 -->
<SettingsPage v-show="activeTab === 'settings'" />
</div>
</div>

<!-- 隐藏的 file input 用于加载配置 -->
Expand All @@ -62,6 +69,7 @@ import {ref, onMounted} from 'vue';
import GlobalToast from "./components/GlobalToast.vue";
import DocTagChecker from "./components/DocTagChecker.vue";
import ConfigGenerator from "./config-generator/ConfigGenerator.vue";
import ConfigSidebar from "./components/ConfigSidebar.vue";
import SettingsPage from "./components/SettingsPage.vue";
import yaml from 'js-yaml';
import {defaultConfig, mergeWithDefaults} from "./config-generator/utils";
Expand Down Expand Up @@ -149,6 +157,45 @@ const onConfigFileSelected = async (e) => {
}
};

// 保存配置到服务器 configs 目录
const API_BASE = window.__API_BASE__ || '';
const saveConfigToServer = async () => {
if (!generatedConfig.value) return;
try {
const yamlContent = yaml.dump(generatedConfig.value, { indent: 2, skipInvalid: true });
const fn = prompt('请输入配置文件名(如 my-thesis.yaml):', 'custom.yaml');
if (!fn) return;
const res = await fetch(`${API_BASE}/configs/save`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ filename: fn, content: yamlContent }),
});
const json = await res.json();
if (json.code === 200) {
toastRef.value?.toast.success(json.msg);
} else {
toastRef.value?.toast.error(json.msg || '保存失败');
}
} catch (error) {
toastRef.value?.toast.error('保存配置失败:' + error.message);
}
};

// 从服务器侧边栏加载配置
const onServerConfigSelected = ({ filename, content }) => {
try {
const config = yaml.load(content);
const merged = mergeWithDefaults(config, defaultConfig);
generatedConfig.value = merged;
if (configGeneratorRef.value) {
configGeneratorRef.value.importConfig(merged);
}
toastRef.value?.toast.success(`已加载配置: ${filename}`);
} catch (error) {
toastRef.value?.toast.error('解析配置失败:' + error.message);
}
};

</script>
<style>
* {
Expand All @@ -160,7 +207,7 @@ const onConfigFileSelected = async (e) => {
body {
background-color: #0f172a;
color: #e2e8f0;
font-family: 'Atkinson Hyperlegible', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'PingFang SC', 'Microsoft YaHei', sans-serif;
-webkit-font-smoothing: antialiased;
}

Expand Down Expand Up @@ -193,7 +240,7 @@ body {
}

.app-title {
font-family: 'Crimson Pro', serif;
font-family: 'Georgia', 'Times New Roman', serif;
font-size: 1.4rem;
font-weight: 600;
color: #f1f5f9;
Expand Down Expand Up @@ -281,6 +328,13 @@ body {
color: #052e16;
}

/* ── 主区域(侧边栏 + 内容)── */
.main-area {
display: flex;
flex: 1;
overflow: hidden;
}

/* ── 内容区域 ── */
.content-area {
flex: 1;
Expand Down
113 changes: 113 additions & 0 deletions WordFormatUI/src/components/ConfigSidebar.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<template>
<div class="config-sidebar">
<div class="sidebar-header">
<h3>配置模板</h3>
<button class="btn-refresh" @click="fetchConfigs" title="刷新列表">↻</button>
</div>
<div class="config-list">
<div
v-for="cfg in configs"
:key="cfg"
class="config-item"
:class="{ active: cfg === activeConfig }"
@click="selectConfig(cfg)"
>
<span class="config-name">{{ cfg }}</span>
</div>
<div v-if="loading" class="loading-text">加载中...</div>
<div v-if="!loading && configs.length === 0" class="empty-text">
暂无配置文件<br/>将 YAML 放入 configs/ 目录
</div>
</div>
</div>
</template>

<script setup>
import { ref, onMounted } from 'vue'

const emit = defineEmits(['config-selected'])
const configs = ref([])
const loading = ref(false)
const activeConfig = ref('')

const API_BASE = window.__API_BASE__ || ''

async function fetchConfigs() {
loading.value = true
try {
const res = await fetch(`${API_BASE}/configs`)
const json = await res.json()
if (json.code === 200) {
configs.value = json.data || []
}
} catch (e) {
console.error('获取配置列表失败:', e)
} finally {
loading.value = false
}
}

async function selectConfig(filename) {
try {
const res = await fetch(`${API_BASE}/configs/${encodeURIComponent(filename)}`)
const json = await res.json()
if (json.code === 200) {
activeConfig.value = filename
emit('config-selected', { filename, content: json.data })
}
} catch (e) {
console.error('读取配置失败:', e)
}
}

onMounted(fetchConfigs)
</script>

<style scoped>
.config-sidebar {
width: 200px;
min-width: 180px;
background: #1e293b;
border-right: 1px solid #334155;
padding: 1rem 0;
height: 100%;
display: flex;
flex-direction: column;
}
.sidebar-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 1rem 0.5rem;
}
.sidebar-header h3 {
font-size: 0.85rem;
color: #94a3b8;
font-weight: 600;
}
.btn-refresh {
background: none;
border: 1px solid #475569;
color: #94a3b8;
border-radius: 4px;
cursor: pointer;
font-size: 1rem;
padding: 0 6px;
}
.btn-refresh:hover { color: #e2e8f0; background: #334155; }
.config-list { flex: 1; overflow-y: auto; padding: 0 0.5rem; }
.config-item {
padding: 0.4rem 0.75rem;
cursor: pointer;
border-radius: 4px;
font-size: 0.8rem;
color: #cbd5e1;
margin-bottom: 2px;
transition: background 0.15s;
}
.config-item:hover { background: #334155; }
.config-item.active { background: #22c55e; color: #052e16; font-weight: 600; }
.loading-text, .empty-text {
padding: 0.5rem; font-size: 0.75rem; color: #64748b; text-align: center;
}
</style>
2 changes: 1 addition & 1 deletion WordFormatUI/src/components/DocTagChecker.vue
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ watch(isFileLoaded, (loaded) => {
.header-bar { background-color: #1e293b; border-bottom: 1px solid #334155; padding: 0.75rem 1.5rem; }
.header-content { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 1rem; max-width: 1280px; margin: 0 auto; width: 100%; }
.header-left { display: flex; flex-direction: column; gap: 0.25rem; }
.tool-title { font-size: 1.15rem; font-weight: 600; color: #f1f5f9; margin: 0; font-family: 'Crimson Pro', serif; }
.tool-title { font-size: 1.15rem; font-weight: 600; color: #f1f5f9; margin: 0; font-family: 'Georgia, Times New Roman', serif; }
.stats-info { font-size: 0.8125rem; color: #64748b; }
.stats-info span { font-weight: 600; color: #22c55e; }
.header-right { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; }
Expand Down
2 changes: 1 addition & 1 deletion WordFormatUI/src/components/SettingsPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ function resetDefaults() { resetSettings(); host.value = backendSettings.host; p
<style scoped>
.settings-page { max-width: 520px; margin: 0 auto; }
.settings-card { background: #1e293b; border: 1px solid #334155; border-radius: 12px; padding: 2rem; }
.settings-title { font-size: 1.15rem; font-weight: 600; color: #f1f5f9; margin-bottom: 0.35rem; font-family: 'Crimson Pro', serif; }
.settings-title { font-size: 1.15rem; font-weight: 600; color: #f1f5f9; margin-bottom: 0.35rem; font-family: 'Georgia, Times New Roman', serif; }
.settings-desc { font-size: 0.8125rem; color: #64748b; margin-bottom: 1.5rem; }
.connection-bar { display: flex; align-items: center; gap: 0.5rem; padding: 0.75rem 1rem; border-radius: 8px; font-size: 0.8125rem; margin-bottom: 1.5rem; }
.connection-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
Expand Down
8 changes: 8 additions & 0 deletions WordFormatUI/src/config-generator/ConfigGenerator.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@
<div class="config-generator">
<div class="app-content">
<div class="config-sections">
<!-- 模板名称 -->
<ConfigSection title="模板信息">
<div class="form-item" style="margin-bottom:10px">
<label>模板名称:</label>
<input type="text" v-model="userConfig.template_name" style="width:300px">
</div>
</ConfigSection>

<!-- 警告字段配置 -->
<ConfigSection title="警告字段配置">
<WarningFieldsConfig :config="userConfig.style_checks_warning" />
Expand Down
19 changes: 11 additions & 8 deletions WordFormatUI/src/config-generator/components/AbstractConfig.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,19 @@
<h3 class="mt-4">关键词配置</h3>
<h4>中文关键词</h4>
<div class="grid grid-cols-2 gap-4 mb-4">
<div class="form-item">
<label><input type="checkbox" v-model="config.keywords.chinese.rules.keyword_count.enabled"> 启用数量检查</label>
</div>
<div class="form-item">
<label>最少数量:</label>
<input type="number" v-model.number="config.keywords.chinese.count_min">
<input type="number" v-model.number="config.keywords.chinese.rules.keyword_count.count_min">
</div>
<div class="form-item">
<label>最大数量:</label>
<input type="number" v-model.number="config.keywords.chinese.count_max">
<input type="number" v-model.number="config.keywords.chinese.rules.keyword_count.count_max">
</div>
<div class="form-item">
<label><input type="checkbox" v-model="config.keywords.chinese.trailing_punct_forbidden"> 禁止末尾标点</label>
<label><input type="checkbox" v-model="config.keywords.chinese.rules.trailing_punctuation.enabled"> 禁止末尾标点</label>
</div>
</div>
<h5 class="subsection-title">中文关键词内容格式</h5>
Expand All @@ -42,15 +45,15 @@
<h4 class="mt-4">英文关键词</h4>
<div class="grid grid-cols-2 gap-4 mb-4">
<div class="form-item">
<label>最少数量:</label>
<input type="number" v-model.number="config.keywords.english.count_min">
<label><input type="checkbox" v-model="config.keywords.english.rules.keyword_count.enabled"> 启用数量检查</label>
</div>
<div class="form-item">
<label>最大数量:</label>
<input type="number" v-model.number="config.keywords.english.count_max">
<label>最少数量:</label>
<input type="number" v-model.number="config.keywords.english.rules.keyword_count.count_min">
</div>
<div class="form-item">
<label><input type="checkbox" v-model="config.keywords.english.trailing_punct_forbidden"> 禁止末尾标点</label>
<label>最大数量:</label>
<input type="number" v-model.number="config.keywords.english.rules.keyword_count.count_max">
</div>
</div>
<h5 class="subsection-title">英文关键词内容格式</h5>
Expand Down
Loading