You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
566 lines
19 KiB
566 lines
19 KiB
<template>
|
|
<!-- 筛选与搜索区域 -->
|
|
<el-card class="card1" style="margin-bottom: 1vh;">
|
|
<div class="condition">
|
|
<div class="condition-item">
|
|
<el-text size="large">搜索:</el-text>
|
|
<el-input v-model="searchForm.chineseSimplified" style="width: 12vw" placeholder="请输入原文内容" clearable />
|
|
</div>
|
|
<!-- 移除语言状态筛选 -->
|
|
<div class="btn">
|
|
<el-button type="primary" @click="search">搜索</el-button>
|
|
<el-button type="success" @click="reset">重置</el-button>
|
|
</div>
|
|
</div>
|
|
</el-card>
|
|
|
|
<el-card class="card2">
|
|
<!-- 功能按钮区域 -->
|
|
<div class="add-item">
|
|
<el-button type="success" @click="handleAdd">添加</el-button>
|
|
<el-button class="add-item-export" @click="handleBatchImport">批量导入</el-button>
|
|
</div>
|
|
|
|
<div>
|
|
<el-table :data="tableData" style="width: 82vw;height:72vh;" :row-style="{ height: '50px' }">
|
|
<el-table-column type="index" label="序号" width="80px" fixed="left">
|
|
<template #default="scope">
|
|
<span>{{ scope.$index + 1 + (pagination.pageNum - 1) * pagination.pageSize }}</span>
|
|
</template>
|
|
</el-table-column>
|
|
|
|
<el-table-column prop="chineseSimplified" label="原文(中文)" width="180px">
|
|
<template #default="scope">
|
|
<el-tooltip :content="scope.row.chineseSimplified" placement="top"
|
|
v-if="scope.row.chineseSimplified && scope.row.chineseSimplified.length > 20">
|
|
<span>{{ truncateText(scope.row.chineseSimplified) }}</span>
|
|
</el-tooltip>
|
|
<span v-else>{{ scope.row.chineseSimplified }}</span>
|
|
</template>
|
|
</el-table-column>
|
|
|
|
<el-table-column prop="english" label="英文" width="200px" header-align="center">
|
|
<template #default="scope">
|
|
<div style="display: flex; align-items: center; justify-content: space-between;">
|
|
<div style="flex: 1;">
|
|
<el-tooltip :content="scope.row.english" placement="top"
|
|
v-if="scope.row.english && scope.row.english.length > 15">
|
|
<span>{{ truncateText(scope.row.english) }}</span>
|
|
</el-tooltip>
|
|
<span v-else>{{ scope.row.english }}</span>
|
|
</div>
|
|
<el-tag :type="scope.row.english ? 'success' : 'info'" size="small"
|
|
style="margin-left: 8px;">
|
|
{{ scope.row.english ? '已翻译' : '未翻译' }}
|
|
</el-tag>
|
|
</div>
|
|
</template>
|
|
</el-table-column>
|
|
|
|
<el-table-column prop="thai" label="泰语" width="200px" header-align="center">
|
|
<template #default="scope">
|
|
<div style="display: flex; align-items: center; justify-content: space-between;">
|
|
<div style="flex: 1;">
|
|
<el-tooltip :content="scope.row.thai" placement="top"
|
|
v-if="scope.row.thai && scope.row.thai.length > 15">
|
|
<span>{{ truncateText(scope.row.thai) }}</span>
|
|
</el-tooltip>
|
|
<span v-else>{{ scope.row.thai }}</span>
|
|
</div>
|
|
<el-tag :type="scope.row.thai ? 'success' : 'info'" size="small" style="margin-left: 8px;">
|
|
{{ scope.row.thai ? '已翻译' : '未翻译' }}
|
|
</el-tag>
|
|
</div>
|
|
</template>
|
|
</el-table-column>
|
|
|
|
<el-table-column prop="chineseTraditional" label="繁体中文" width="180px" header-align="center">
|
|
<template #default="scope">
|
|
<div style="display: flex; align-items: center; justify-content: space-between;">
|
|
<div style="flex: 1;">
|
|
<el-tooltip :content="scope.row.chineseTraditional" placement="top"
|
|
v-if="scope.row.chineseTraditional && scope.row.chineseTraditional.length > 15">
|
|
<span>{{ truncateText(scope.row.chineseTraditional) }}</span>
|
|
</el-tooltip>
|
|
<span v-else>{{ scope.row.chineseTraditional }}</span>
|
|
</div>
|
|
<el-tag :type="scope.row.chineseTraditional ? 'success' : 'info'" size="small"
|
|
style="margin-left: 8px;">
|
|
{{ scope.row.chineseTraditional ? '已翻译' : '未翻译' }}
|
|
</el-tag>
|
|
</div>
|
|
</template>
|
|
</el-table-column>
|
|
|
|
<el-table-column prop="malay" label="马来语" width="200px" header-align="center">
|
|
<template #default="scope">
|
|
<div style="display: flex; align-items: center; justify-content: space-between;">
|
|
<div style="flex: 1;">
|
|
<el-tooltip :content="scope.row.malay" placement="top"
|
|
v-if="scope.row.malay && scope.row.malay.length > 15">
|
|
<span>{{ truncateText(scope.row.malay) }}</span>
|
|
</el-tooltip>
|
|
<span v-else>{{ scope.row.malay }}</span>
|
|
</div>
|
|
<el-tag :type="scope.row.malay ? 'success' : 'info'" size="small" style="margin-left: 8px;">
|
|
{{ scope.row.malay ? '已翻译' : '未翻译' }}
|
|
</el-tag>
|
|
</div>
|
|
</template>
|
|
</el-table-column>
|
|
|
|
<el-table-column prop="vietnamese" label="越南语" width="200px" header-align="center">
|
|
<template #default="scope">
|
|
<div style="display: flex; align-items: center; justify-content: space-between;">
|
|
<div style="flex: 1;">
|
|
<el-tooltip :content="scope.row.vietnamese" placement="top"
|
|
v-if="scope.row.vietnamese && scope.row.vietnamese.length > 15">
|
|
<span>{{ truncateText(scope.row.vietnamese) }}</span>
|
|
</el-tooltip>
|
|
<span v-else>{{ scope.row.vietnamese }}</span>
|
|
</div>
|
|
<el-tag :type="scope.row.vietnamese ? 'success' : 'info'" size="small"
|
|
style="margin-left: 8px;">
|
|
{{ scope.row.vietnamese ? '已翻译' : '未翻译' }}
|
|
</el-tag>
|
|
</div>
|
|
</template>
|
|
</el-table-column>
|
|
|
|
<!-- 移除状态列 -->
|
|
<el-table-column prop="configTime" label="配置时间" width="180px" header-align="center">
|
|
<template #default="scope">
|
|
{{ moment(scope.row.configTime).format('YYYY-MM-DD HH:mm:ss') }}
|
|
</template>
|
|
</el-table-column>
|
|
|
|
<el-table-column prop="operation" label="操作" width="155px" fixed="right" header-align="center">
|
|
<template #default="scope">
|
|
<div style="display:flex; justify-content:center; ">
|
|
<el-button type="primary" text @click="handleEdit(scope.row)">编辑</el-button>
|
|
<el-button type="danger" text @click="handleDelete(scope.row)">删除</el-button>
|
|
</div>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
</div>
|
|
|
|
<!-- 分页组件 -->
|
|
<div style="margin-top: 10px;display: flex;">
|
|
<el-pagination background v-model:current-page="pagination.pageNum" v-model:page-size="pagination.pageSize"
|
|
layout="total, sizes, prev, pager, next, jumper" :total="pagination.total" style="margin-top: 1vh;"
|
|
@size-change="handleSizeChange" @current-change="handleCurrentChange" />
|
|
</div>
|
|
</el-card>
|
|
|
|
<!-- 确认删除对话框 -->
|
|
<ConfirmDialog v-model="showDeleteDialog" message="删除该翻译记录!" @confirm="handleDeleteConfirm"
|
|
@cancel="handleDeleteCancel" @close="handleDeleteClose" />
|
|
|
|
<!-- 编辑对话框 -->
|
|
<el-dialog v-model="showEditDialog" :title="editForm.id ? '编辑翻译' : '新增翻译'" width="30vw" draggable>
|
|
<el-form :model="editForm" label-width="120px">
|
|
<el-form-item label="原文(中文):">
|
|
<el-input v-model="editForm.chineseSimplified" placeholder="请输入原文内容" show-word-limit />
|
|
</el-form-item>
|
|
|
|
<el-form-item label="英文:">
|
|
<el-input v-model="editForm.english" placeholder="请输入英文翻译" show-word-limit />
|
|
</el-form-item>
|
|
|
|
<el-form-item label="泰语:">
|
|
<el-input v-model="editForm.thai" placeholder="请输入泰语翻译" show-word-limit />
|
|
</el-form-item>
|
|
|
|
<el-form-item label="繁体中文:">
|
|
<el-input v-model="editForm.chineseTraditional" placeholder="请输入繁体中文翻译" show-word-limit />
|
|
</el-form-item>
|
|
|
|
<el-form-item label="马来语:">
|
|
<el-input v-model="editForm.malay" placeholder="请输入马来语翻译" show-word-limit />
|
|
</el-form-item>
|
|
|
|
<el-form-item label="越南语:">
|
|
<el-input v-model="editForm.vietnamese" placeholder="请输入越南语翻译" show-word-limit />
|
|
</el-form-item>
|
|
|
|
</el-form>
|
|
|
|
<template #footer>
|
|
<el-button @click="showEditDialog = false">取消</el-button>
|
|
<el-button type="primary" @click="handleSave">保存</el-button>
|
|
</template>
|
|
</el-dialog>
|
|
|
|
<!-- 批量导入对话框 -->
|
|
<el-dialog v-model="showImportDialog" title="批量导入" width="40vw" draggable>
|
|
<div style="margin-bottom: 20px;">
|
|
<el-text>下载导入模板:</el-text>
|
|
<el-button type="text" @click="downloadTemplate">中文/英文/泰语/繁体中文/马来语/越南语模板</el-button>
|
|
</div>
|
|
|
|
<el-upload class="upload-demo" drag action="#" :auto-upload="false" :on-change="handleFileChange"
|
|
:show-file-list="false">
|
|
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
|
|
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
|
</el-upload>
|
|
|
|
<template #footer>
|
|
<el-button @click="showImportDialog = false">取消</el-button>
|
|
<el-button type="primary" @click="handleImport">导入</el-button>
|
|
</template>
|
|
</el-dialog>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ElMessage, ElMessageBox } from 'element-plus';
|
|
import { onMounted, ref, computed } from 'vue'
|
|
import API from "@/util/http.js"
|
|
import moment from 'moment'
|
|
import { UploadFilled } from '@element-plus/icons-vue'
|
|
import ConfirmDialog from '@/components/dialogs/ConfirmDialog.vue'
|
|
// 引入AdminStore
|
|
import { useAdminStore } from '@/store/index.js';
|
|
import { storeToRefs } from "pinia";
|
|
const adminStore = useAdminStore();
|
|
const { adminData } = storeToRefs(adminStore);
|
|
|
|
// 响应式数据
|
|
const tableData = ref([])
|
|
const pagination = ref({
|
|
pageNum: 1,
|
|
pageSize: 20,
|
|
total: 0
|
|
})
|
|
|
|
const searchForm = ref({
|
|
chineseSimplified: '',
|
|
// 移除languageStatus字段
|
|
})
|
|
|
|
const showEditDialog = ref(false)
|
|
const showImportDialog = ref(false)
|
|
const showDeleteDialog = ref(false)
|
|
const currentDeleteRow = ref(null) // 当前要删除的行数据
|
|
|
|
const editForm = ref({
|
|
id: '',
|
|
chineseSimplified: '',
|
|
english: '',
|
|
thai: '',
|
|
chineseTraditional: '',
|
|
malay: '',
|
|
vietnamese: '',
|
|
// modules: [],
|
|
configTime: new Date()
|
|
})
|
|
|
|
// 移除计算属性 - 不再需要统一的状态计算
|
|
// const translationStatus = computed(() => {
|
|
// return (row) => {
|
|
// const hasTranslation = row.english || row.thai || row.chineseTraditional || row.malay || row.vietnamese
|
|
// return hasTranslation ? 'translated' : 'untranslated'
|
|
// }
|
|
// })
|
|
|
|
// 方法定义
|
|
const truncateText = (text) => {
|
|
if (!text) return ''
|
|
return text.length > 20 ? text.substring(0, 20) + '...' : text
|
|
}
|
|
|
|
// 移除getStatusType方法,因为现在每个语言列单独判断状态
|
|
// const getStatusType = (status) => {
|
|
// return status === 'translated' ? 'success' : 'info'
|
|
// }
|
|
|
|
// 搜索功能
|
|
const search = async () => {
|
|
await getTranslationList()
|
|
}
|
|
|
|
// 重置功能
|
|
const reset = () => {
|
|
searchForm.value = {
|
|
chineseSimplified: '',
|
|
// 移除languageStatus
|
|
}
|
|
getTranslationList()
|
|
}
|
|
|
|
// 获取翻译列表
|
|
const getTranslationList = async () => {
|
|
try {
|
|
const params = {
|
|
pageNum: pagination.value.pageNum,
|
|
pageSize: pagination.value.pageSize,
|
|
...searchForm.value
|
|
}
|
|
|
|
// 这里调用实际的API接口
|
|
const res = await API({
|
|
url: '/language/getTranslation',
|
|
data: params
|
|
})
|
|
|
|
if (res.code === 200) {
|
|
// 不再需要设置统一的状态字段
|
|
tableData.value = res.data.list
|
|
pagination.value.total = res.data.total
|
|
}
|
|
} catch (error) {
|
|
console.error('获取翻译列表失败:', error)
|
|
ElMessage.error('获取数据失败')
|
|
}
|
|
}
|
|
|
|
// 编辑翻译
|
|
const handleEdit = (row) => {
|
|
editForm.value = { ...row }
|
|
showEditDialog.value = true
|
|
}
|
|
|
|
// 新增翻译
|
|
const handleAdd = () => {
|
|
editForm.value = {
|
|
id: '',
|
|
chineseSimplified: '',
|
|
english: '',
|
|
thai: '',
|
|
chineseTraditional: '',
|
|
malay: '',
|
|
vietnamese: '',
|
|
// modules: [],
|
|
configTime: new Date()
|
|
}
|
|
showEditDialog.value = true
|
|
}
|
|
|
|
const getMenuTree = async function () {
|
|
// 获取菜单树
|
|
try {
|
|
const result = await request({
|
|
url: '/menu/tree',
|
|
data: {
|
|
id: adminData.value.roleId,
|
|
|
|
}
|
|
})
|
|
if (result.code === 200) {
|
|
adminStore.setMenuTree(result.data)
|
|
|
|
}
|
|
|
|
return result.data // 直接返回接口响应数据
|
|
} catch (error) {
|
|
console.error('菜单数据请求失败:', error)
|
|
// return { code: 500, msg: '获取菜单失败' }
|
|
ElMessage.error('网络异常')
|
|
adminStore.clearState()
|
|
}
|
|
}
|
|
|
|
// 保存翻译
|
|
const handleSave = async () => {
|
|
// 原文必填校验
|
|
if (!editForm.value.chineseSimplified || editForm.value.chineseSimplified.trim() === '') {
|
|
ElMessage.error('原文为必填项')
|
|
return
|
|
}
|
|
|
|
// 纯文本校验
|
|
const fields = ['english', 'thai', 'chineseTraditional', 'malay', 'vietnamese']
|
|
for (const field of fields) {
|
|
if (editForm.value[field] && /<[^>]*>/.test(editForm.value[field])) {
|
|
ElMessage.error('译文仅支持纯文本,不支持HTML标签')
|
|
return
|
|
}
|
|
}
|
|
|
|
try {
|
|
const url = editForm.value.id ? '/language/updateTranslation' : '/language/addTranslation'
|
|
const res = await API({
|
|
url: url,
|
|
data: editForm.value
|
|
})
|
|
|
|
if (res.code === 200) {
|
|
ElMessage.success(editForm.value.id ? '编辑成功' : '添加成功')
|
|
showEditDialog.value = false
|
|
getTranslationList()
|
|
} else if (res.code === 0) {
|
|
// 处理后端返回的错误信息
|
|
ElMessage.error(res.msg || '操作失败')
|
|
} else {
|
|
// 处理其他错误码
|
|
ElMessage.error(res.msg || '操作失败')
|
|
}
|
|
} catch (error) {
|
|
console.error('保存失败:', error)
|
|
ElMessage.error('保存失败')
|
|
}
|
|
// 点击保存后,刷新菜单树
|
|
await getMenuTree()
|
|
}
|
|
|
|
// 删除翻译 - 打开确认对话框
|
|
const handleDelete = (row) => {
|
|
currentDeleteRow.value = row
|
|
showDeleteDialog.value = true
|
|
}
|
|
|
|
// 确认删除
|
|
const handleDeleteConfirm = async () => {
|
|
try {
|
|
const res = await API({
|
|
url: '/language/deleteTranslation',
|
|
data: { id: currentDeleteRow.value.id }
|
|
})
|
|
|
|
if (res.code === 200) {
|
|
ElMessage.success('删除成功')
|
|
getTranslationList()
|
|
}
|
|
} catch (error) {
|
|
console.error('删除失败:', error)
|
|
ElMessage.error('删除失败')
|
|
} finally {
|
|
showDeleteDialog.value = false
|
|
currentDeleteRow.value = null
|
|
}
|
|
// 点击删除后,刷新菜单树
|
|
await getMenuTree()
|
|
}
|
|
|
|
// 取消删除
|
|
const handleDeleteCancel = () => {
|
|
showDeleteDialog.value = false
|
|
currentDeleteRow.value = null
|
|
}
|
|
|
|
// 关闭删除对话框
|
|
const handleDeleteClose = () => {
|
|
showDeleteDialog.value = false
|
|
currentDeleteRow.value = null
|
|
}
|
|
|
|
// 批量导入
|
|
const handleBatchImport = () => {
|
|
showImportDialog.value = true
|
|
}
|
|
|
|
// 下载模板
|
|
const downloadTemplate = () => {
|
|
// 这里实现下载模板的逻辑
|
|
ElMessage.info('模板下载功能待实现')
|
|
}
|
|
|
|
// 文件变化处理
|
|
const handleFileChange = (file) => {
|
|
// 这里处理文件上传逻辑
|
|
console.log('文件变化:', file)
|
|
}
|
|
|
|
// 导入处理
|
|
const handleImport = () => {
|
|
// 这里实现导入逻辑
|
|
ElMessage.info('导入功能待实现')
|
|
}
|
|
|
|
// 分页处理
|
|
const handleSizeChange = (val) => {
|
|
pagination.value.pageSize = val
|
|
pagination.value.pageNum = 1
|
|
getTranslationList()
|
|
}
|
|
|
|
const handleCurrentChange = (val) => {
|
|
pagination.value.pageNum = val
|
|
getTranslationList()
|
|
}
|
|
|
|
// 生命周期
|
|
onMounted(() => {
|
|
getTranslationList()
|
|
})
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
// 搜索卡片样式 - 与活动管理一致
|
|
.card1 {
|
|
background: #F3FAFE;
|
|
}
|
|
|
|
// 数据表格卡片样式 - 与活动管理一致
|
|
.card2 {
|
|
background: #E7F4FD;
|
|
}
|
|
|
|
// 表头背景等 - 与活动管理一致
|
|
:deep(.el-table__header-wrapper),
|
|
:deep(.el-table__body-wrapper),
|
|
:deep(.el-table__cell),
|
|
/* 表格 */
|
|
:deep(.el-table__body td) {
|
|
background-color: #F3FAFE !important;
|
|
}
|
|
|
|
/* 表头 */
|
|
:deep(.el-table__header th) {
|
|
background-color: #F3FAFE !important;
|
|
}
|
|
|
|
/* 鼠标悬停 */
|
|
:deep(.el-table__row:hover > .el-table__cell) {
|
|
background-color: #E5EBFE !important;
|
|
}
|
|
|
|
.condition {
|
|
display: flex;
|
|
align-items: center;
|
|
flex-wrap: wrap;
|
|
gap: 16px;
|
|
}
|
|
|
|
.condition-item {
|
|
display: flex;
|
|
align-items: center;
|
|
min-width: 180px;
|
|
}
|
|
|
|
.btn {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
|
|
}
|
|
|
|
|
|
|
|
.add-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
margin-bottom: 1vh;
|
|
|
|
.add-item-export {
|
|
background-color: #5870FF;
|
|
color: white;
|
|
}
|
|
}
|
|
|
|
// 标签样式
|
|
.el-tag {
|
|
border: none;
|
|
}
|
|
|
|
// 上传组件样式
|
|
.upload-demo {
|
|
width: 100%;
|
|
}
|
|
|
|
:deep(.el-upload-dragger) {
|
|
width: 100%;
|
|
height: 180px;
|
|
}
|
|
</style>
|