翻译管理

This commit is contained in:
zhang zhuo 2025-12-04 14:51:07 +08:00
parent b293209126
commit 2441594f1c
6 changed files with 269 additions and 0 deletions

View File

@ -218,4 +218,18 @@ export default {
return await http.get("dict_data/option", data);
},
},
translation: {
list: async function (data = {}) {
return await http.get("translation/list", data);
},
add: async function (data = {}) {
return await http.post("translation/add", data);
},
edit: async function (data = {}) {
return await http.put("translation/edit", data);
},
del: async function (data = {}) {
return await http.delete("translation/del", data);
},
},
}

View File

@ -0,0 +1,3 @@
<template>
<svg t="1764646179370" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7597" width="256" height="256"><path d="M209.700571 950.857143h604.598858c75.337143 0 136.557714-62.537143 136.557714-139.337143v-599.04C950.857143 135.68 889.636571 73.142857 814.299429 73.142857H209.700571C134.363429 73.142857 73.142857 135.606857 73.142857 212.48v599.04C73.142857 888.32 134.363429 950.857143 209.700571 950.857143zM136.192 212.48c0-41.325714 32.914286-75.044571 73.508571-75.044571h604.598858c40.521143 0 73.508571 33.645714 73.508571 75.044571v599.04c0 41.325714-32.914286 75.044571-73.508571 75.044571H209.700571c-40.521143 0-73.508571-33.645714-73.508571-75.044571v-599.04z m403.748571 442.368c0 18.066286 13.385143 32.694857 29.915429 32.694857 16.530286 0 29.915429-14.628571 29.915429-32.694857V478.793143l121.270857 187.245714a28.598857 28.598857 0 0 0 33.426285 12.288 32.548571 32.548571 0 0 0 20.845715-31.158857V369.152c0-18.066286-13.385143-32.694857-29.915429-32.694857-16.457143 0-29.842286 14.628571-29.842286 32.694857v175.323429l-121.270857-187.245715a28.598857 28.598857 0 0 0-33.499428-12.288 32.548571 32.548571 0 0 0-20.845715 31.158857v278.747429zM485.888 512.731429c0 17.993143-13.385143 32.694857-29.915429 32.694857H308.443429v74.020571h147.602285c16.530286 0 29.915429 14.628571 29.915429 32.621714 0 18.066286-13.385143 32.694857-29.915429 32.694858H278.601143c-16.530286 0-29.915429-14.628571-29.915429-32.694858V369.152c0-18.066286 13.385143-32.694857 29.915429-32.694857h177.444571c16.530286 0 29.915429 14.628571 29.915429 32.694857 0 17.993143-13.385143 32.621714-29.915429 32.621714H308.443429v78.262857h147.529142c16.530286 0 29.988571 14.628571 29.988572 32.694858z" p-id="7598"></path></svg>
</template>

View File

@ -46,6 +46,10 @@ let defaultProps = ref({
})
let tableData = ref([])
watch(() => props.modelValue, (val) => {
defaultValue.value = val
}, {deep: true, immediate: true})
watch(defaultValue, () => {
emit('update:modelValue', defaultValue.value)
emit('change', defaultValue.value)

View File

@ -12,6 +12,7 @@ import errorHandler from "@/utils/errorHandler";
import piDialog from "@/components/piDialog"
import piTable from "@/components/piTable"
import piUpload from "@/components/piUpload"
import piSelect from "@/components/piSelect"
export default {
install(app: App) {
@ -19,6 +20,7 @@ export default {
app.component('piDialog', piDialog)
app.component('piTable', piTable)
app.component('piUpload', piUpload)
app.component('piSelect', piSelect)
//注册全局指令
app.directive('auth', auth)

View File

@ -0,0 +1,149 @@
<template>
<pi-table ref="tableRef" :apiObj="api.system.translation.list" @selection-change="selectionChange">
<template #do>
<el-button v-auth="'translation:add'" type="primary" icon="el-icon-plus" @click="add"></el-button>
<el-button v-auth="'translation:edit'" type="success" icon="el-icon-edit" @click="edit"
:disabled="selection.length!==1"></el-button>
<el-button v-auth="'translation:del'" type="danger" plain icon="el-icon-delete"
:disabled="selection.length===0" @click="batch_del"></el-button>
</template>
<template #search>
<pi-select v-model="search.group" :api-obj="api.system.dict_data.option" :params="{key: 'sys_langs_group'}"
:props="{label: 'dict_label', value: 'dict_value'}" style="width: 200px;"></pi-select>
<el-input v-model="search.translation_key" placeholder="key" clearable style="width: 200px;"></el-input>
<el-button type="primary" icon="el-icon-search" @click="upsearch"></el-button>
</template>
<el-table-column type="selection" width="50"></el-table-column>
<el-table-column label="ID" prop="translation_id" width="60"></el-table-column>
<el-table-column label="分组" prop="group" width="120"></el-table-column>
<el-table-column label="key" prop="translation_key" width="120"></el-table-column>
<el-table-column label="描述" prop="remark" width="150"></el-table-column>
<el-table-column v-for="item in langs" :key="item.dict_value" :label="item.dict_label + item.dict_value"
:prop="item.dict_value" width="180">
<template #default="scope">
{{getValue(scope.row, item)}}
</template>
</el-table-column>
<el-table-column label="创建时间" prop="create_time"></el-table-column>
<el-table-column label="更新时间" prop="update_time"></el-table-column>
<el-table-column label="操作" fixed="right" align="right" width="170">
<template #default="scope">
<el-button-group>
<el-button text type="primary" size="small" @click="show(scope.row, scope.$index)">查看
</el-button>
<el-button v-auth="'translation:edit'" text type="success" size="small"
@click="edit(scope.row, scope.$index)">编辑
</el-button>
<el-popconfirm title="确定删除吗?" @confirm="del(scope.row, scope.$index)">
<template #reference>
<el-button v-auth="'translation:del'" text type="danger" size="small">删除</el-button>
</template>
</el-popconfirm>
</el-button-group>
</template>
</el-table-column>
</pi-table>
<el-drawer v-model="drawerShow" :size="450" title="翻译管理" destroy-on-close>
<save ref="saveRef" @success="tableRef.refresh()" @closed="drawerShow=false"></save>
</el-drawer>
</template>
<script setup>
import save from './save'
import api from "@/api/index";
import {getCurrentInstance, nextTick, ref, onMounted} from "vue";
defineOptions({
name: "translation"
})
const {proxy} = getCurrentInstance()
const tableRef = ref(null)
const saveRef = ref(null)
let langs = ref([])
let drawerShow = ref(false)
let selection = ref([])
let search = ref({
group: null,
translation_key: null,
})
onMounted(() => {
loadLangs()
})
function getValue(row, item) {
let arr = {}
row.values.forEach(i => {
if (langs.value.map(j => j.dict_value).includes(i.lang_code)) {
arr[i.lang_code] = i.lang_value
}
})
return arr[item.dict_value];
}
async function loadLangs() {
const res = await api.system.dict_data.option({key: 'sys_langs'})
langs.value = res.data
}
//
function add() {
drawerShow.value = true
nextTick(() => {
saveRef.value.setLangs(langs.value).open()
})
}
//
function edit(row) {
drawerShow.value = true
nextTick(() => {
if (row instanceof PointerEvent) {
row = selection.value[0]
}
saveRef.value.setLangs(langs.value).open('edit', row)
})
}
//
function show(row) {
drawerShow.value = true
nextTick(() => {
saveRef.value.setLangs(langs.value).open('show', row)
})
}
//
async function del(row) {
const loading = proxy.$loading();
const res = await api.system.translation.del({ids: [row.translation_id]});
tableRef.value.refresh()
loading.close();
proxy.$message.success(res.msg)
}
//
async function batch_del() {
proxy.$confirm(`确定删除选中的 ${selection.value.length} 项吗?如果删除项中含有子集将会被一并删除`, '提示', {
type: 'warning'
}).then(async () => {
const loading = proxy.$loading();
const res = await api.system.translation.del({ids: selection.value.map(item => item.translation_id)});
tableRef.value.refresh()
loading.close();
proxy.$message.success(res.msg)
})
}
//
function selectionChange(e) {
selection.value = e;
}
//
function upsearch() {
tableRef.value.upData(search.value)
}
</script>

View File

@ -0,0 +1,97 @@
<template>
<el-container>
<el-main>
<el-form :model="form" :rules="rules" :disabled="mode==='show'" ref="formRef" label-width="100px">
<el-form-item label="分组" prop="group">
<pi-select v-model="form.group" :api-obj="api.system.dict_data.option"
:params="{key: 'sys_langs_group'}"
:props="{label: 'dict_label', value: 'dict_value'}"
placeholder="请输入分组" clearable/>
</el-form-item>
<el-form-item label="key" prop="translation_key">
<el-input type="text" v-model="form.translation_key" placeholder="请输入key" clearable></el-input>
</el-form-item>
<el-form-item label="描述" prop="remark">
<el-input type="text" v-model="form.remark" placeholder="请输入描述" clearable></el-input>
</el-form-item>
<el-form-item v-for="item in langs" :key="item.dict_value" :label="item.dict_label+item.dict_value"
prop="item.dict_value">
<el-input type="text" v-model="values[item.dict_value]" placeholder="请输入译文"
clearable></el-input>
</el-form-item>
</el-form>
</el-main>
<el-footer style="padding:10px;text-align: right;height: auto;">
<el-button @click="emit('closed')"> </el-button>
<el-button v-if="mode!=='show'" type="primary" @click="submit()"> </el-button>
</el-footer>
</el-container>
</template>
<script setup>
import {getCurrentInstance, ref} from 'vue'
import api from "@/api/index"
defineExpose({open, setLangs})
const emit = defineEmits(['success', 'closed'])
const formRef = ref(null)
const {proxy} = getCurrentInstance()
let mode = ref('add')
let form = ref({
translation_id: null,
group: null,
translation_key: null,
remark: null,
values: []
})
let langs = ref([])
const rules = ref({
group: [
{required: true, message: '请填写分组'}
],
translation_key: [
{required: true, message: '请填写key'}
],
})
const values = ref({})
function open(m = 'add', data = null) {
mode.value = m
Object.assign(form.value, data)
//
let arr = {}
form.value.values.forEach(i => {
if (langs.value.map(j => j.dict_value).includes(i.lang_code)) {
arr[i.lang_code] = i.lang_value
}
})
values.value = arr
}
function setLangs(data) {
langs.value = data
return {open}
}
async function submit() {
let arr = []
Object.keys(values.value).forEach(i => {
arr.push({
lang_code: i,
lang_value: values.value[i]
})
})
form.value.values = arr
//
const validate = await formRef.value.validate().catch(() => {
});
if (!validate) {
return false
}
const res = form.value.translation_id ? await api.system.translation.edit(form.value) : await api.system.translation.add(form.value);
emit('success')
emit('closed')
proxy.$message.success(res.msg)
}
</script>