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.

560 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="$t('common.deleteRecord')" @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 request 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. import { useI18n } from 'vue-i18n'
  205. const { t } = useI18n()
  206. // 响应式数据
  207. const tableData = ref([])
  208. const pagination = ref({
  209. pageNum: 1,
  210. pageSize: 20,
  211. total: 0
  212. })
  213. const searchForm = ref({
  214. chineseSimplified: '',
  215. // 移除languageStatus字段
  216. })
  217. const showEditDialog = ref(false)
  218. const showImportDialog = ref(false)
  219. const showDeleteDialog = ref(false)
  220. const currentDeleteRow = ref(null) // 当前要删除的行数据
  221. const editForm = ref({
  222. id: '',
  223. chineseSimplified: '',
  224. english: '',
  225. thai: '',
  226. chineseTraditional: '',
  227. malay: '',
  228. vietnamese: '',
  229. // modules: [],
  230. configTime: new Date()
  231. })
  232. // 方法定义
  233. const truncateText = (text) => {
  234. if (!text) return ''
  235. return text.length > 20 ? text.substring(0, 20) + '...' : text
  236. }
  237. // 搜索功能
  238. const search = async () => {
  239. await getTranslationList()
  240. }
  241. // 重置功能
  242. const reset = () => {
  243. searchForm.value = {
  244. chineseSimplified: '',
  245. // 移除languageStatus
  246. }
  247. getTranslationList()
  248. }
  249. // 获取翻译列表
  250. const getTranslationList = async () => {
  251. try {
  252. const params = {
  253. pageNum: pagination.value.pageNum,
  254. pageSize: pagination.value.pageSize,
  255. ...searchForm.value
  256. }
  257. // 这里调用实际的request接口
  258. const res = await request({
  259. url: '/language/getTranslation',
  260. data: params
  261. })
  262. if (res.code === 200) {
  263. // 不再需要设置统一的状态字段
  264. tableData.value = res.data.list
  265. pagination.value.total = res.data.total
  266. }
  267. } catch (error) {
  268. console.error('获取翻译列表失败:', error)
  269. ElMessage.error('获取数据失败')
  270. }
  271. }
  272. // 编辑翻译
  273. const handleEdit = (row) => {
  274. editForm.value = { ...row }
  275. showEditDialog.value = true
  276. }
  277. // 新增翻译
  278. const handleAdd = () => {
  279. editForm.value = {
  280. id: '',
  281. chineseSimplified: '',
  282. english: '',
  283. thai: '',
  284. chineseTraditional: '',
  285. malay: '',
  286. vietnamese: '',
  287. // modules: [],
  288. configTime: new Date()
  289. }
  290. showEditDialog.value = true
  291. }
  292. const getMenuTree = async function () {
  293. // 获取菜单树
  294. try {
  295. const result = await request({
  296. url: '/menu/tree',
  297. data: {
  298. id: adminData.value.roleId,
  299. }
  300. })
  301. if (result.code === 200) {
  302. adminStore.setMenuTree(result.data)
  303. }
  304. return result.data // 直接返回接口响应数据
  305. } catch (error) {
  306. console.error('菜单数据请求失败:', error)
  307. // return { code: 500, msg: '获取菜单失败' }
  308. ElMessage.error('网络异常')
  309. adminStore.clearState()
  310. }
  311. // console.log('1')
  312. }
  313. // 保存翻译
  314. const handleSave = async () => {
  315. // 原文必填校验
  316. if (!editForm.value.chineseSimplified || editForm.value.chineseSimplified.trim() === '') {
  317. ElMessage.error('原文为必填项')
  318. return
  319. }
  320. // 纯文本校验
  321. const fields = ['english', 'thai', 'chineseTraditional', 'malay', 'vietnamese']
  322. for (const field of fields) {
  323. if (editForm.value[field] && /<[^>]*>/.test(editForm.value[field])) {
  324. ElMessage.error('译文仅支持纯文本,不支持HTML标签')
  325. return
  326. }
  327. }
  328. try {
  329. const url = editForm.value.id ? '/language/updateTranslation' : '/language/addTranslation'
  330. const res = await request({
  331. url: url,
  332. data: editForm.value
  333. })
  334. if (res.code === 200) {
  335. ElMessage.success(editForm.value.id ? '编辑成功' : '添加成功')
  336. showEditDialog.value = false
  337. getTranslationList()
  338. } else if (res.code === 0) {
  339. // 处理后端返回的错误信息
  340. ElMessage.error(res.msg || '操作失败')
  341. } else {
  342. // 处理其他错误码
  343. ElMessage.error(res.msg || '操作失败')
  344. }
  345. } catch (error) {
  346. console.error('保存失败:', error)
  347. ElMessage.error('保存失败')
  348. }
  349. // 点击保存后,刷新菜单树
  350. await getMenuTree()
  351. }
  352. // 删除翻译 - 打开确认对话框
  353. const handleDelete = (row) => {
  354. currentDeleteRow.value = row
  355. showDeleteDialog.value = true
  356. }
  357. // 确认删除
  358. const handleDeleteConfirm = async () => {
  359. try {
  360. const res = await request({
  361. url: '/language/deleteTranslation',
  362. data: { id: currentDeleteRow.value.id }
  363. })
  364. if (res.code === 200) {
  365. ElMessage.success('删除成功')
  366. getTranslationList()
  367. }
  368. } catch (error) {
  369. console.error('删除失败:', error)
  370. ElMessage.error('删除失败')
  371. } finally {
  372. showDeleteDialog.value = false
  373. currentDeleteRow.value = null
  374. }
  375. // 点击删除后,刷新菜单树
  376. await getMenuTree()
  377. }
  378. // 取消删除
  379. const handleDeleteCancel = () => {
  380. showDeleteDialog.value = false
  381. currentDeleteRow.value = null
  382. }
  383. // 关闭删除对话框
  384. const handleDeleteClose = () => {
  385. showDeleteDialog.value = false
  386. currentDeleteRow.value = null
  387. }
  388. // 批量导入
  389. const handleBatchImport = () => {
  390. showImportDialog.value = true
  391. }
  392. // 下载模板
  393. const downloadTemplate = () => {
  394. // 这里实现下载模板的逻辑
  395. ElMessage.info('模板下载功能待实现')
  396. }
  397. // 文件变化处理
  398. const handleFileChange = (file) => {
  399. // 这里处理文件上传逻辑
  400. console.log('文件变化:', file)
  401. }
  402. // 导入处理
  403. const handleImport = () => {
  404. // 这里实现导入逻辑
  405. ElMessage.info('导入功能待实现')
  406. }
  407. // 分页处理
  408. const handleSizeChange = (val) => {
  409. pagination.value.pageSize = val
  410. pagination.value.pageNum = 1
  411. getTranslationList()
  412. }
  413. const handleCurrentChange = (val) => {
  414. pagination.value.pageNum = val
  415. getTranslationList()
  416. }
  417. // 生命周期
  418. onMounted(() => {
  419. getTranslationList()
  420. })
  421. </script>
  422. <style scoped lang="scss">
  423. // 搜索卡片样式 - 与活动管理一致
  424. .card1 {
  425. background: #F3FAFE;
  426. }
  427. // 数据表格卡片样式 - 与活动管理一致
  428. .card2 {
  429. background: #E7F4FD;
  430. }
  431. // 表头背景等 - 与活动管理一致
  432. :deep(.el-table__header-wrapper),
  433. :deep(.el-table__body-wrapper),
  434. :deep(.el-table__cell),
  435. /* 表格 */
  436. :deep(.el-table__body td) {
  437. background-color: #F3FAFE !important;
  438. }
  439. /* 表头 */
  440. :deep(.el-table__header th) {
  441. background-color: #F3FAFE !important;
  442. }
  443. /* 鼠标悬停 */
  444. :deep(.el-table__row:hover > .el-table__cell) {
  445. background-color: #E5EBFE !important;
  446. }
  447. .condition {
  448. display: flex;
  449. align-items: center;
  450. flex-wrap: wrap;
  451. gap: 16px;
  452. }
  453. .condition-item {
  454. display: flex;
  455. align-items: center;
  456. min-width: 180px;
  457. }
  458. .btn {
  459. display: flex;
  460. align-items: center;
  461. gap: 4px;
  462. }
  463. .add-item {
  464. display: flex;
  465. align-items: center;
  466. gap: 4px;
  467. margin-bottom: 1vh;
  468. .add-item-export {
  469. background-color: #5870FF;
  470. color: white;
  471. }
  472. }
  473. // 标签样式
  474. .el-tag {
  475. border: none;
  476. }
  477. // 上传组件样式
  478. .upload-demo {
  479. width: 100%;
  480. }
  481. :deep(.el-upload-dragger) {
  482. width: 100%;
  483. height: 180px;
  484. }
  485. </style>