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.

565 lines
19 KiB

  1. <template>
  2. <!-- 筛选与搜索区域 -->
  3. <el-card class="card1" style="margin-bottom: 1vh;">
  4. <div class="condition">
  5. <div class="condition-item">
  6. <el-text size="large">搜索</el-text>
  7. <el-input v-model="searchForm.chineseSimplified" style="width: 12vw" placeholder="请输入原文内容" clearable />
  8. </div>
  9. <!-- 移除语言状态筛选 -->
  10. <div class="btn">
  11. <el-button type="primary" @click="search">搜索</el-button>
  12. <el-button type="success" @click="reset">重置</el-button>
  13. </div>
  14. </div>
  15. </el-card>
  16. <el-card class="card2">
  17. <!-- 功能按钮区域 -->
  18. <div class="add-item">
  19. <el-button type="success" @click="handleAdd">添加</el-button>
  20. <el-button class="add-item-export" @click="handleBatchImport">批量导入</el-button>
  21. </div>
  22. <div>
  23. <el-table :data="tableData" style="width: 82vw;height:72vh;" :row-style="{ height: '50px' }">
  24. <el-table-column type="index" label="序号" width="80px" fixed="left">
  25. <template #default="scope">
  26. <span>{{ scope.$index + 1 + (pagination.pageNum - 1) * pagination.pageSize }}</span>
  27. </template>
  28. </el-table-column>
  29. <el-table-column prop="chineseSimplified" label="原文(中文)" width="180px">
  30. <template #default="scope">
  31. <el-tooltip :content="scope.row.chineseSimplified" placement="top"
  32. v-if="scope.row.chineseSimplified && scope.row.chineseSimplified.length > 20">
  33. <span>{{ truncateText(scope.row.chineseSimplified) }}</span>
  34. </el-tooltip>
  35. <span v-else>{{ scope.row.chineseSimplified }}</span>
  36. </template>
  37. </el-table-column>
  38. <el-table-column prop="english" label="英文" width="200px" header-align="center">
  39. <template #default="scope">
  40. <div style="display: flex; align-items: center; justify-content: space-between;">
  41. <div style="flex: 1;">
  42. <el-tooltip :content="scope.row.english" placement="top"
  43. v-if="scope.row.english && scope.row.english.length > 15">
  44. <span>{{ truncateText(scope.row.english) }}</span>
  45. </el-tooltip>
  46. <span v-else>{{ scope.row.english }}</span>
  47. </div>
  48. <el-tag :type="scope.row.english ? 'success' : 'info'" size="small"
  49. style="margin-left: 8px;">
  50. {{ scope.row.english ? '已翻译' : '未翻译' }}
  51. </el-tag>
  52. </div>
  53. </template>
  54. </el-table-column>
  55. <el-table-column prop="thai" label="泰语" width="200px" header-align="center">
  56. <template #default="scope">
  57. <div style="display: flex; align-items: center; justify-content: space-between;">
  58. <div style="flex: 1;">
  59. <el-tooltip :content="scope.row.thai" placement="top"
  60. v-if="scope.row.thai && scope.row.thai.length > 15">
  61. <span>{{ truncateText(scope.row.thai) }}</span>
  62. </el-tooltip>
  63. <span v-else>{{ scope.row.thai }}</span>
  64. </div>
  65. <el-tag :type="scope.row.thai ? 'success' : 'info'" size="small" style="margin-left: 8px;">
  66. {{ scope.row.thai ? '已翻译' : '未翻译' }}
  67. </el-tag>
  68. </div>
  69. </template>
  70. </el-table-column>
  71. <el-table-column prop="chineseTraditional" label="繁体中文" width="180px" header-align="center">
  72. <template #default="scope">
  73. <div style="display: flex; align-items: center; justify-content: space-between;">
  74. <div style="flex: 1;">
  75. <el-tooltip :content="scope.row.chineseTraditional" placement="top"
  76. v-if="scope.row.chineseTraditional && scope.row.chineseTraditional.length > 15">
  77. <span>{{ truncateText(scope.row.chineseTraditional) }}</span>
  78. </el-tooltip>
  79. <span v-else>{{ scope.row.chineseTraditional }}</span>
  80. </div>
  81. <el-tag :type="scope.row.chineseTraditional ? 'success' : 'info'" size="small"
  82. style="margin-left: 8px;">
  83. {{ scope.row.chineseTraditional ? '已翻译' : '未翻译' }}
  84. </el-tag>
  85. </div>
  86. </template>
  87. </el-table-column>
  88. <el-table-column prop="malay" label="马来语" width="200px" header-align="center">
  89. <template #default="scope">
  90. <div style="display: flex; align-items: center; justify-content: space-between;">
  91. <div style="flex: 1;">
  92. <el-tooltip :content="scope.row.malay" placement="top"
  93. v-if="scope.row.malay && scope.row.malay.length > 15">
  94. <span>{{ truncateText(scope.row.malay) }}</span>
  95. </el-tooltip>
  96. <span v-else>{{ scope.row.malay }}</span>
  97. </div>
  98. <el-tag :type="scope.row.malay ? 'success' : 'info'" size="small" style="margin-left: 8px;">
  99. {{ scope.row.malay ? '已翻译' : '未翻译' }}
  100. </el-tag>
  101. </div>
  102. </template>
  103. </el-table-column>
  104. <el-table-column prop="vietnamese" label="越南语" width="200px" header-align="center">
  105. <template #default="scope">
  106. <div style="display: flex; align-items: center; justify-content: space-between;">
  107. <div style="flex: 1;">
  108. <el-tooltip :content="scope.row.vietnamese" placement="top"
  109. v-if="scope.row.vietnamese && scope.row.vietnamese.length > 15">
  110. <span>{{ truncateText(scope.row.vietnamese) }}</span>
  111. </el-tooltip>
  112. <span v-else>{{ scope.row.vietnamese }}</span>
  113. </div>
  114. <el-tag :type="scope.row.vietnamese ? 'success' : 'info'" size="small"
  115. style="margin-left: 8px;">
  116. {{ scope.row.vietnamese ? '已翻译' : '未翻译' }}
  117. </el-tag>
  118. </div>
  119. </template>
  120. </el-table-column>
  121. <!-- 移除状态列 -->
  122. <el-table-column prop="configTime" label="配置时间" width="180px" header-align="center">
  123. <template #default="scope">
  124. {{ moment(scope.row.configTime).format('YYYY-MM-DD HH:mm:ss') }}
  125. </template>
  126. </el-table-column>
  127. <el-table-column prop="operation" label="操作" width="155px" fixed="right" header-align="center">
  128. <template #default="scope">
  129. <div style="display:flex; justify-content:center; ">
  130. <el-button type="primary" text @click="handleEdit(scope.row)">编辑</el-button>
  131. <el-button type="danger" text @click="handleDelete(scope.row)">删除</el-button>
  132. </div>
  133. </template>
  134. </el-table-column>
  135. </el-table>
  136. </div>
  137. <!-- 分页组件 -->
  138. <div style="margin-top: 10px;display: flex;">
  139. <el-pagination background v-model:current-page="pagination.pageNum" v-model:page-size="pagination.pageSize"
  140. layout="total, sizes, prev, pager, next, jumper" :total="pagination.total" style="margin-top: 1vh;"
  141. @size-change="handleSizeChange" @current-change="handleCurrentChange" />
  142. </div>
  143. </el-card>
  144. <!-- 确认删除对话框 -->
  145. <ConfirmDialog v-model="showDeleteDialog" message="删除该翻译记录!" @confirm="handleDeleteConfirm"
  146. @cancel="handleDeleteCancel" @close="handleDeleteClose" />
  147. <!-- 编辑对话框 -->
  148. <el-dialog v-model="showEditDialog" :title="editForm.id ? '编辑翻译' : '新增翻译'" width="30vw" draggable>
  149. <el-form :model="editForm" label-width="120px">
  150. <el-form-item label="原文(中文):">
  151. <el-input v-model="editForm.chineseSimplified" placeholder="请输入原文内容" show-word-limit />
  152. </el-form-item>
  153. <el-form-item label="英文:">
  154. <el-input v-model="editForm.english" placeholder="请输入英文翻译" show-word-limit />
  155. </el-form-item>
  156. <el-form-item label="泰语:">
  157. <el-input v-model="editForm.thai" placeholder="请输入泰语翻译" show-word-limit />
  158. </el-form-item>
  159. <el-form-item label="繁体中文:">
  160. <el-input v-model="editForm.chineseTraditional" placeholder="请输入繁体中文翻译" show-word-limit />
  161. </el-form-item>
  162. <el-form-item label="马来语:">
  163. <el-input v-model="editForm.malay" placeholder="请输入马来语翻译" show-word-limit />
  164. </el-form-item>
  165. <el-form-item label="越南语:">
  166. <el-input v-model="editForm.vietnamese" placeholder="请输入越南语翻译" show-word-limit />
  167. </el-form-item>
  168. </el-form>
  169. <template #footer>
  170. <el-button @click="showEditDialog = false">取消</el-button>
  171. <el-button type="primary" @click="handleSave">保存</el-button>
  172. </template>
  173. </el-dialog>
  174. <!-- 批量导入对话框 -->
  175. <el-dialog v-model="showImportDialog" title="批量导入" width="40vw" draggable>
  176. <div style="margin-bottom: 20px;">
  177. <el-text>下载导入模板</el-text>
  178. <el-button type="text" @click="downloadTemplate">中文/英文/泰语/繁体中文/马来语/越南语模板</el-button>
  179. </div>
  180. <el-upload class="upload-demo" drag action="#" :auto-upload="false" :on-change="handleFileChange"
  181. :show-file-list="false">
  182. <el-icon class="el-icon--upload"><upload-filled /></el-icon>
  183. <div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
  184. </el-upload>
  185. <template #footer>
  186. <el-button @click="showImportDialog = false">取消</el-button>
  187. <el-button type="primary" @click="handleImport">导入</el-button>
  188. </template>
  189. </el-dialog>
  190. </template>
  191. <script setup>
  192. import { ElMessage, ElMessageBox } from 'element-plus';
  193. import { onMounted, ref, computed } from 'vue'
  194. import API from "@/util/http.js"
  195. import moment from 'moment'
  196. import { UploadFilled } from '@element-plus/icons-vue'
  197. import ConfirmDialog from '@/components/dialogs/ConfirmDialog.vue'
  198. // 引入AdminStore
  199. import { useAdminStore } from '@/store/index.js';
  200. import { storeToRefs } from "pinia";
  201. const adminStore = useAdminStore();
  202. const { adminData } = storeToRefs(adminStore);
  203. // 响应式数据
  204. const tableData = ref([])
  205. const pagination = ref({
  206. pageNum: 1,
  207. pageSize: 20,
  208. total: 0
  209. })
  210. const searchForm = ref({
  211. chineseSimplified: '',
  212. // 移除languageStatus字段
  213. })
  214. const showEditDialog = ref(false)
  215. const showImportDialog = ref(false)
  216. const showDeleteDialog = ref(false)
  217. const currentDeleteRow = ref(null) // 当前要删除的行数据
  218. const editForm = ref({
  219. id: '',
  220. chineseSimplified: '',
  221. english: '',
  222. thai: '',
  223. chineseTraditional: '',
  224. malay: '',
  225. vietnamese: '',
  226. // modules: [],
  227. configTime: new Date()
  228. })
  229. // 移除计算属性 - 不再需要统一的状态计算
  230. // const translationStatus = computed(() => {
  231. // return (row) => {
  232. // const hasTranslation = row.english || row.thai || row.chineseTraditional || row.malay || row.vietnamese
  233. // return hasTranslation ? 'translated' : 'untranslated'
  234. // }
  235. // })
  236. // 方法定义
  237. const truncateText = (text) => {
  238. if (!text) return ''
  239. return text.length > 20 ? text.substring(0, 20) + '...' : text
  240. }
  241. // 移除getStatusType方法,因为现在每个语言列单独判断状态
  242. // const getStatusType = (status) => {
  243. // return status === 'translated' ? 'success' : 'info'
  244. // }
  245. // 搜索功能
  246. const search = async () => {
  247. await getTranslationList()
  248. }
  249. // 重置功能
  250. const reset = () => {
  251. searchForm.value = {
  252. chineseSimplified: '',
  253. // 移除languageStatus
  254. }
  255. getTranslationList()
  256. }
  257. // 获取翻译列表
  258. const getTranslationList = async () => {
  259. try {
  260. const params = {
  261. pageNum: pagination.value.pageNum,
  262. pageSize: pagination.value.pageSize,
  263. ...searchForm.value
  264. }
  265. // 这里调用实际的API接口
  266. const res = await API({
  267. url: '/language/getTranslation',
  268. data: params
  269. })
  270. if (res.code === 200) {
  271. // 不再需要设置统一的状态字段
  272. tableData.value = res.data.list
  273. pagination.value.total = res.data.total
  274. }
  275. } catch (error) {
  276. console.error('获取翻译列表失败:', error)
  277. ElMessage.error('获取数据失败')
  278. }
  279. }
  280. // 编辑翻译
  281. const handleEdit = (row) => {
  282. editForm.value = { ...row }
  283. showEditDialog.value = true
  284. }
  285. // 新增翻译
  286. const handleAdd = () => {
  287. editForm.value = {
  288. id: '',
  289. chineseSimplified: '',
  290. english: '',
  291. thai: '',
  292. chineseTraditional: '',
  293. malay: '',
  294. vietnamese: '',
  295. // modules: [],
  296. configTime: new Date()
  297. }
  298. showEditDialog.value = true
  299. }
  300. const getMenuTree = async function () {
  301. // 获取菜单树
  302. try {
  303. const result = await request({
  304. url: '/menu/tree',
  305. data: {
  306. id: adminData.value.roleId,
  307. }
  308. })
  309. if (result.code === 200) {
  310. adminStore.setMenuTree(result.data)
  311. }
  312. return result.data // 直接返回接口响应数据
  313. } catch (error) {
  314. console.error('菜单数据请求失败:', error)
  315. // return { code: 500, msg: '获取菜单失败' }
  316. ElMessage.error('网络异常')
  317. adminStore.clearState()
  318. }
  319. }
  320. // 保存翻译
  321. const handleSave = async () => {
  322. // 原文必填校验
  323. if (!editForm.value.chineseSimplified || editForm.value.chineseSimplified.trim() === '') {
  324. ElMessage.error('原文为必填项')
  325. return
  326. }
  327. // 纯文本校验
  328. const fields = ['english', 'thai', 'chineseTraditional', 'malay', 'vietnamese']
  329. for (const field of fields) {
  330. if (editForm.value[field] && /<[^>]*>/.test(editForm.value[field])) {
  331. ElMessage.error('译文仅支持纯文本,不支持HTML标签')
  332. return
  333. }
  334. }
  335. try {
  336. const url = editForm.value.id ? '/language/updateTranslation' : '/language/addTranslation'
  337. const res = await API({
  338. url: url,
  339. data: editForm.value
  340. })
  341. if (res.code === 200) {
  342. ElMessage.success(editForm.value.id ? '编辑成功' : '添加成功')
  343. showEditDialog.value = false
  344. getTranslationList()
  345. } else if (res.code === 0) {
  346. // 处理后端返回的错误信息
  347. ElMessage.error(res.msg || '操作失败')
  348. } else {
  349. // 处理其他错误码
  350. ElMessage.error(res.msg || '操作失败')
  351. }
  352. } catch (error) {
  353. console.error('保存失败:', error)
  354. ElMessage.error('保存失败')
  355. }
  356. // 点击保存后,刷新菜单树
  357. await getMenuTree()
  358. }
  359. // 删除翻译 - 打开确认对话框
  360. const handleDelete = (row) => {
  361. currentDeleteRow.value = row
  362. showDeleteDialog.value = true
  363. }
  364. // 确认删除
  365. const handleDeleteConfirm = async () => {
  366. try {
  367. const res = await API({
  368. url: '/language/deleteTranslation',
  369. data: { id: currentDeleteRow.value.id }
  370. })
  371. if (res.code === 200) {
  372. ElMessage.success('删除成功')
  373. getTranslationList()
  374. }
  375. } catch (error) {
  376. console.error('删除失败:', error)
  377. ElMessage.error('删除失败')
  378. } finally {
  379. showDeleteDialog.value = false
  380. currentDeleteRow.value = null
  381. }
  382. // 点击删除后,刷新菜单树
  383. await getMenuTree()
  384. }
  385. // 取消删除
  386. const handleDeleteCancel = () => {
  387. showDeleteDialog.value = false
  388. currentDeleteRow.value = null
  389. }
  390. // 关闭删除对话框
  391. const handleDeleteClose = () => {
  392. showDeleteDialog.value = false
  393. currentDeleteRow.value = null
  394. }
  395. // 批量导入
  396. const handleBatchImport = () => {
  397. showImportDialog.value = true
  398. }
  399. // 下载模板
  400. const downloadTemplate = () => {
  401. // 这里实现下载模板的逻辑
  402. ElMessage.info('模板下载功能待实现')
  403. }
  404. // 文件变化处理
  405. const handleFileChange = (file) => {
  406. // 这里处理文件上传逻辑
  407. console.log('文件变化:', file)
  408. }
  409. // 导入处理
  410. const handleImport = () => {
  411. // 这里实现导入逻辑
  412. ElMessage.info('导入功能待实现')
  413. }
  414. // 分页处理
  415. const handleSizeChange = (val) => {
  416. pagination.value.pageSize = val
  417. pagination.value.pageNum = 1
  418. getTranslationList()
  419. }
  420. const handleCurrentChange = (val) => {
  421. pagination.value.pageNum = val
  422. getTranslationList()
  423. }
  424. // 生命周期
  425. onMounted(() => {
  426. getTranslationList()
  427. })
  428. </script>
  429. <style scoped lang="scss">
  430. // 搜索卡片样式 - 与活动管理一致
  431. .card1 {
  432. background: #F3FAFE;
  433. }
  434. // 数据表格卡片样式 - 与活动管理一致
  435. .card2 {
  436. background: #E7F4FD;
  437. }
  438. // 表头背景等 - 与活动管理一致
  439. :deep(.el-table__header-wrapper),
  440. :deep(.el-table__body-wrapper),
  441. :deep(.el-table__cell),
  442. /* 表格 */
  443. :deep(.el-table__body td) {
  444. background-color: #F3FAFE !important;
  445. }
  446. /* 表头 */
  447. :deep(.el-table__header th) {
  448. background-color: #F3FAFE !important;
  449. }
  450. /* 鼠标悬停 */
  451. :deep(.el-table__row:hover > .el-table__cell) {
  452. background-color: #E5EBFE !important;
  453. }
  454. .condition {
  455. display: flex;
  456. align-items: center;
  457. flex-wrap: wrap;
  458. gap: 16px;
  459. }
  460. .condition-item {
  461. display: flex;
  462. align-items: center;
  463. min-width: 180px;
  464. }
  465. .btn {
  466. display: flex;
  467. align-items: center;
  468. gap: 4px;
  469. }
  470. .add-item {
  471. display: flex;
  472. align-items: center;
  473. gap: 4px;
  474. margin-bottom: 1vh;
  475. .add-item-export {
  476. background-color: #5870FF;
  477. color: white;
  478. }
  479. }
  480. // 标签样式
  481. .el-tag {
  482. border: none;
  483. }
  484. // 上传组件样式
  485. .upload-demo {
  486. width: 100%;
  487. }
  488. :deep(.el-upload-dragger) {
  489. width: 100%;
  490. height: 180px;
  491. }
  492. </style>