<template>
|
<div class="app-container">
|
<el-card class="box-card" shadow="hover">
|
<div slot="header" class="clearfix">
|
<span style="font-weight: bold; font-size: 16px;">医院分词管理与测试工具</span>
|
<el-tag type="info" size="small" style="margin-left: 10px;">使用 HanLP 专业中文分词</el-tag>
|
</div>
|
|
<!-- 批量分词区域 -->
|
<el-divider content-position="left">
|
<i class="el-icon-setting"></i> 批量分词管理
|
</el-divider>
|
|
<el-row :gutter="20">
|
<el-col :span="24">
|
<el-alert
|
title="提示"
|
type="info"
|
:closable="false"
|
style="margin-bottom: 15px;">
|
<template slot>
|
批量为所有医院生成分词数据。首次部署时必须执行一次,或在分词算法更新后重新生成。
|
</template>
|
</el-alert>
|
|
<el-button
|
type="primary"
|
icon="el-icon-cpu"
|
:loading="generating"
|
@click="handleGenerateKeywords"
|
size="medium">
|
{{ generating ? '正在生成分词...' : '批量生成所有医院分词' }}
|
</el-button>
|
|
<el-button
|
type="success"
|
icon="el-icon-refresh"
|
@click="resetStatus"
|
size="medium"
|
v-if="generateResult">
|
重置
|
</el-button>
|
|
<!-- 进度条 -->
|
<div v-if="generating && taskProgress" style="margin-top: 20px;">
|
<el-progress
|
:percentage="taskProgress.progress || 0"
|
:status="taskProgress.status === 'FAILED' ? 'exception' : null">
|
</el-progress>
|
<div style="margin-top: 10px; color: #606266; font-size: 14px;">
|
<span>进度: {{ taskProgress.processedCount || 0 }} / {{ taskProgress.totalCount || 0 }}</span>
|
<span style="margin-left: 20px;">成功: {{ taskProgress.successCount || 0 }}</span>
|
<span style="margin-left: 20px;">失败: {{ taskProgress.failedCount || 0 }}</span>
|
</div>
|
</div>
|
|
<div v-if="generateResult" style="margin-top: 15px;">
|
<el-result
|
:icon="generateResult.success ? 'success' : 'error'"
|
:title="generateResult.title"
|
:subTitle="generateResult.message">
|
</el-result>
|
</div>
|
</el-col>
|
</el-row>
|
|
<!-- 分词测试区域 -->
|
<el-divider content-position="left">
|
<i class="el-icon-search"></i> 医院匹配测试
|
</el-divider>
|
|
<el-row :gutter="20">
|
<el-col :span="24">
|
<el-alert
|
title="测试说明"
|
type="success"
|
:closable="false"
|
style="margin-bottom: 15px;">
|
<template slot>
|
输入医院名称、地址或关键词,系统将自动进行分词并匹配相似的医院。匹配结果按相关度排序。
|
</template>
|
</el-alert>
|
|
<el-form :model="searchForm" label-width="100px">
|
<el-form-item label="搜索文本">
|
<el-input
|
v-model="searchForm.searchText"
|
placeholder="请输入医院名称、地址或关键词,例如:北京协和医院、上海瑞金"
|
clearable
|
@keyup.enter.native="handleSearch"
|
style="width: 60%;">
|
<el-button
|
slot="append"
|
icon="el-icon-search"
|
@click="handleSearch"
|
:loading="searching">
|
搜索
|
</el-button>
|
</el-input>
|
|
<el-input-number
|
v-model="searchForm.pageSize"
|
:min="5"
|
:max="100"
|
:step="5"
|
controls-position="right"
|
style="width: 150px; margin-left: 10px;">
|
</el-input-number>
|
<span style="margin-left: 5px; color: #909399;">条结果</span>
|
</el-form-item>
|
|
<el-form-item label="分词结果" v-if="tokenizedKeywords">
|
<el-tag
|
v-for="(keyword, index) in tokenizedKeywords.split(',')"
|
:key="index"
|
type="success"
|
size="small"
|
style="margin-right: 5px; margin-bottom: 5px;">
|
{{ keyword }}
|
</el-tag>
|
</el-form-item>
|
</el-form>
|
|
<!-- 搜索结果表格 -->
|
<el-table
|
v-loading="searching"
|
:data="searchResults"
|
border
|
stripe
|
style="width: 100%; margin-top: 10px;"
|
:height="400"
|
v-if="searchResults.length > 0">
|
<el-table-column type="index" label="排名" width="60" align="center" />
|
<el-table-column prop="matchScore" label="匹配分数" width="100" align="center" sortable>
|
<template slot-scope="scope">
|
<el-tag :type="getScoreType(scope.row.matchScore)" size="medium">
|
{{ scope.row.matchScore }}
|
</el-tag>
|
</template>
|
</el-table-column>
|
<el-table-column prop="hospital.hospId" label="医院ID" width="80" align="center" />
|
<el-table-column prop="hospital.hospName" label="医院名称" min-width="180" show-overflow-tooltip />
|
<el-table-column prop="hospital.hospShort" label="简称" width="120" show-overflow-tooltip />
|
<el-table-column label="地址" min-width="220" show-overflow-tooltip>
|
<template slot-scope="scope">
|
{{ formatAddress(scope.row.hospital) }}
|
</template>
|
</el-table-column>
|
<el-table-column prop="hospital.hospTel" label="电话" width="130" />
|
<el-table-column label="状态" width="80" align="center">
|
<template slot-scope="scope">
|
<el-tag v-if="scope.row.hospital.hospState === 1" type="success" size="small">正常</el-tag>
|
<el-tag v-else type="info" size="small">未知</el-tag>
|
</template>
|
</el-table-column>
|
</el-table>
|
|
<!-- 无结果提示 -->
|
<el-empty
|
v-if="searched && searchResults.length === 0"
|
description="未找到匹配的医院"
|
:image-size="100">
|
</el-empty>
|
|
<!-- 统计信息 -->
|
<div v-if="searchResults.length > 0" style="margin-top: 10px; color: #909399;">
|
<i class="el-icon-info"></i>
|
共找到 <span style="color: #409EFF; font-weight: bold;">{{ searchResults.length }}</span> 个匹配的医院
|
</div>
|
</el-col>
|
</el-row>
|
</el-card>
|
</div>
|
</template>
|
|
<script>
|
import request from '@/utils/request'
|
|
export default {
|
name: 'HospitalTokenizer',
|
data() {
|
return {
|
// 批量生成状态
|
generating: false,
|
generateResult: null,
|
currentTaskId: null,
|
taskProgress: null,
|
progressTimer: null,
|
|
// 搜索表单
|
searchForm: {
|
searchText: '',
|
pageSize: 30
|
},
|
|
// 搜索状态
|
searching: false,
|
searched: false,
|
tokenizedKeywords: '',
|
searchResults: []
|
}
|
},
|
methods: {
|
/** 批量生成分词 */
|
handleGenerateKeywords() {
|
this.$confirm('确认要为所有医院生成分词吗?这可能需要几分钟时间。', '确认操作', {
|
confirmButtonText: '确定',
|
cancelButtonText: '取消',
|
type: 'warning'
|
}).then(() => {
|
this.generating = true
|
this.generateResult = null
|
this.taskProgress = null
|
|
request({
|
url: '/system/hospital/generateKeywords',
|
method: 'get'
|
}).then(response => {
|
if (response.code === 200) {
|
// 获取任务ID
|
this.currentTaskId = response.data.taskId
|
this.$message.success('分词任务已启动,正在后台执行...')
|
|
// 开始轮询任务进度
|
this.startProgressPolling()
|
} else {
|
this.generating = false
|
this.$message.error(response.msg || '任务启动失败')
|
}
|
}).catch(error => {
|
this.generating = false
|
this.$message.error('网络请求失败')
|
console.error('生成分词失败:', error)
|
})
|
}).catch(() => {
|
this.$message.info('已取消操作')
|
})
|
},
|
|
/** 开始轮询任务进度 */
|
startProgressPolling() {
|
this.queryTaskProgress()
|
|
// 每2秒轮询一次
|
this.progressTimer = setInterval(() => {
|
this.queryTaskProgress()
|
}, 2000)
|
},
|
|
/** 查询任务进度 */
|
queryTaskProgress() {
|
if (!this.currentTaskId) {
|
return
|
}
|
|
request({
|
url: '/system/hospital/getTaskProgress',
|
method: 'get',
|
params: {
|
taskId: this.currentTaskId
|
}
|
}).then(response => {
|
if (response.code === 200) {
|
this.taskProgress = response.data
|
|
// 判断任务是否完成
|
if (this.taskProgress.status === 'SUCCESS') {
|
this.stopProgressPolling()
|
this.generating = false
|
this.generateResult = {
|
success: true,
|
title: '生成成功',
|
message: `共处理 ${this.taskProgress.totalCount} 个区院,成功 ${this.taskProgress.successCount} 个,失败 ${this.taskProgress.failedCount} 个`
|
}
|
this.$message.success('医院分词生成完成!')
|
} else if (this.taskProgress.status === 'FAILED') {
|
this.stopProgressPolling()
|
this.generating = false
|
this.generateResult = {
|
success: false,
|
title: '生成失败',
|
message: this.taskProgress.errorMessage || '任务执行失败'
|
}
|
this.$message.error('医院分词生成失败!')
|
}
|
} else {
|
// 任务不存在或已过期
|
this.stopProgressPolling()
|
this.generating = false
|
}
|
}).catch(error => {
|
console.error('查询进度失败:', error)
|
})
|
},
|
|
/** 停止轮询 */
|
stopProgressPolling() {
|
if (this.progressTimer) {
|
clearInterval(this.progressTimer)
|
this.progressTimer = null
|
}
|
},
|
|
/** 重置状态 */
|
resetStatus() {
|
this.generateResult = null
|
this.taskProgress = null
|
this.currentTaskId = null
|
this.stopProgressPolling()
|
},
|
|
/** 搜索医院 */
|
handleSearch() {
|
if (!this.searchForm.searchText.trim()) {
|
this.$message.warning('请输入搜索文本')
|
return
|
}
|
|
this.searching = true
|
this.searched = false
|
this.tokenizedKeywords = ''
|
this.searchResults = []
|
|
request({
|
url: '/system/hospital/searchByKeywords',
|
method: 'get',
|
params: {
|
searchText: this.searchForm.searchText,
|
pageSize: this.searchForm.pageSize
|
}
|
}).then(response => {
|
this.searching = false
|
this.searched = true
|
|
if (response.code === 200) {
|
this.searchResults = response.data || []
|
|
// 提取分词结果(从日志中)
|
if (this.searchResults.length > 0) {
|
this.$message.success(`找到 ${this.searchResults.length} 个匹配的医院`)
|
// 模拟显示分词结果
|
this.tokenizedKeywords = this.generateMockKeywords(this.searchForm.searchText)
|
} else {
|
this.$message.info('未找到匹配的医院,请尝试其他关键词')
|
}
|
} else {
|
this.$message.error(response.msg || '搜索失败')
|
}
|
}).catch(error => {
|
this.searching = false
|
this.searched = true
|
this.$message.error('搜索失败:' + (error.message || '网络错误'))
|
console.error('搜索失败:', error)
|
})
|
},
|
|
/** 格式化地址 */
|
formatAddress(row) {
|
const parts = []
|
if (row.hopsProvince) parts.push(row.hopsProvince)
|
if (row.hopsCity) parts.push(row.hopsCity)
|
if (row.hopsArea) parts.push(row.hopsArea)
|
if (row.hospAddress) parts.push(row.hospAddress)
|
return parts.join(' ')
|
},
|
|
/** 获取分数标签类型 */
|
getScoreType(score) {
|
if (score >= 10) return 'danger' // 高匹配 - 红色
|
if (score >= 5) return 'warning' // 中匹配 - 橙色
|
if (score >= 3) return 'success' // 低匹配 - 绿色
|
return '' // 极低匹配 - 默认
|
},
|
|
/** 生成模拟分词结果(用于展示) */
|
generateMockKeywords(text) {
|
// 这里简单模拟,实际分词在后端完成
|
const keywords = []
|
for (let i = 0; i < text.length; i++) {
|
for (let len = 2; len <= Math.min(4, text.length - i); len++) {
|
keywords.push(text.substr(i, len))
|
}
|
}
|
return keywords.slice(0, 15).join(',')
|
}
|
},
|
|
beforeDestroy() {
|
// 组件销毁时停止轮询
|
this.stopProgressPolling()
|
}
|
}
|
</script>
|
|
<style scoped>
|
.box-card {
|
margin: 20px;
|
}
|
|
.clearfix:before,
|
.clearfix:after {
|
display: table;
|
content: "";
|
}
|
|
.clearfix:after {
|
clear: both;
|
}
|
|
.el-divider {
|
margin: 30px 0 20px 0;
|
}
|
|
.el-divider__text {
|
font-weight: bold;
|
font-size: 14px;
|
}
|
</style>
|