feat: 仓库添加翻译

This commit is contained in:
ChenZhaoYu 2023-03-12 20:49:10 +08:00
parent 7bebee202e
commit de6632d631
5 changed files with 176 additions and 89 deletions

View File

@ -1,11 +1,12 @@
<script setup lang='ts'> <script setup lang='ts'>
import type { DataTableColumns } from 'naive-ui' import type { DataTableColumns } from 'naive-ui'
import { computed, h, ref, watch } from 'vue' import { computed, h, ref, watch } from 'vue'
import { NButton, NCard, NDataTable, NDivider, NGi, NGrid, NInput, NLayoutContent, NMessageProvider, NModal, NPopconfirm, NSpace, NTabPane, NTabs, useMessage } from 'naive-ui' import { NButton, NCard, NDataTable, NDivider, NInput, NLayoutContent, NMessageProvider, NModal, NPopconfirm, NSpace, NTabPane, NTabs, useMessage } from 'naive-ui'
import PromptRecommend from '../../../assets/recommend.json' import PromptRecommend from '../../../assets/recommend.json'
import { SvgIcon } from '..' import { SvgIcon } from '..'
import { usePromptStore } from '@/store' import { usePromptStore } from '@/store'
import { useBasicLayout } from '@/hooks/useBasicLayout' import { useBasicLayout } from '@/hooks/useBasicLayout'
import { t } from '@/locales'
interface DataProps { interface DataProps {
renderKey: string renderKey: string
@ -35,6 +36,9 @@ const show = computed({
const showModal = ref(false) const showModal = ref(false)
const importLoading = ref(false)
const exportLoading = ref(false)
// //
const { isMobile } = useBasicLayout() const { isMobile } = useBasicLayout()
@ -87,16 +91,16 @@ const inputStatus = computed (() => tempPromptKey.value.trim().length < 1 || tem
const addPromptTemplate = () => { const addPromptTemplate = () => {
for (const i of promptList.value) { for (const i of promptList.value) {
if (i.key === tempPromptKey.value) { if (i.key === tempPromptKey.value) {
message.error('已存在重复标题,请重新输入') message.error(t('store.addRepeatTitleTips'))
return return
} }
if (i.value === tempPromptValue.value) { if (i.value === tempPromptValue.value) {
message.error(`已存在重复内容:${tempPromptKey.value},请重新输入`) message.error(t('store.addRepeatContentTips', { msg: tempPromptKey.value }))
return return
} }
} }
promptList.value.unshift({ key: tempPromptKey.value, value: tempPromptValue.value } as never) promptList.value.unshift({ key: tempPromptKey.value, value: tempPromptValue.value } as never)
message.success('添加 prompt 成功') message.success(t('common.addSuccess'))
changeShowModal('') changeShowModal('')
} }
@ -115,17 +119,17 @@ const modifyPromptTemplate = () => {
// //
for (const i of tempList) { for (const i of tempList) {
if (i.key === tempPromptKey.value) { if (i.key === tempPromptKey.value) {
message.error('检测修改 Prompt 标题冲突,请重新修改') message.error(t('store.editRepeatTitleTips'))
return return
} }
if (i.value === tempPromptValue.value) { if (i.value === tempPromptValue.value) {
message.error(`检测修改内容${i.key}冲突,请重新修改`) message.error(t('store.editRepeatContentTips', { msg: i.key }))
return return
} }
} }
promptList.value = [{ key: tempPromptKey.value, value: tempPromptValue.value }, ...tempList] as never promptList.value = [{ key: tempPromptKey.value, value: tempPromptValue.value }, ...tempList] as never
message.success('Prompt 信息修改成功') message.success(t('common.editSuccess'))
changeShowModal('') changeShowModal('')
} }
@ -133,12 +137,12 @@ const deletePromptTemplate = (row: { key: string; value: string }) => {
promptList.value = [ promptList.value = [
...promptList.value.filter((item: { key: string; value: string }) => item.key !== row.key), ...promptList.value.filter((item: { key: string; value: string }) => item.key !== row.key),
] as never ] as never
message.success('删除 Prompt 成功') message.success(t('common.deleteSuccess'))
} }
const clearPromptTemplate = () => { const clearPromptTemplate = () => {
promptList.value = [] promptList.value = []
message.success('清空 Prompt 成功') message.success(t('common.clearSuccess'))
} }
const importPromptTemplate = () => { const importPromptTemplate = () => {
@ -163,16 +167,16 @@ const importPromptTemplate = () => {
for (const i of jsonData) { for (const i of jsonData) {
if (!('key' in i) || !('value' in i)) if (!('key' in i) || !('value' in i))
throw new Error('键值不匹配') throw new Error(t('store.importError'))
let safe = true let safe = true
for (const j of promptList.value) { for (const j of promptList.value) {
if (j.key === i[key]) { if (j.key === i[key]) {
message.warning(`因标题重复跳过:${i[key]}`) message.warning(t('store.importRepeatTitle', { msg: i[key] }))
safe = false safe = false
break break
} }
if (j.value === i[value]) { if (j.value === i[value]) {
message.warning(`因内容重复跳过:${i[key]}`) message.warning(t('store.importRepeatContent', { msg: i[key] }))
safe = false safe = false
break break
} }
@ -180,7 +184,7 @@ const importPromptTemplate = () => {
if (safe) if (safe)
promptList.value.unshift({ key: i[key], value: i[value] } as never) promptList.value.unshift({ key: i[key], value: i[value] } as never)
} }
message.success('导入成功') message.success(t('common.importSuccess'))
changeShowModal('') changeShowModal('')
} }
catch { catch {
@ -191,6 +195,7 @@ const importPromptTemplate = () => {
// //
const exportPromptTemplate = () => { const exportPromptTemplate = () => {
exportLoading.value = true
const jsonDataStr = JSON.stringify(promptList.value) const jsonDataStr = JSON.stringify(promptList.value)
const blob = new Blob([jsonDataStr], { type: 'application/json' }) const blob = new Blob([jsonDataStr], { type: 'application/json' })
const url = URL.createObjectURL(blob) const url = URL.createObjectURL(blob)
@ -199,11 +204,13 @@ const exportPromptTemplate = () => {
link.download = 'ChatGPTPromptTemplate.json' link.download = 'ChatGPTPromptTemplate.json'
link.click() link.click()
URL.revokeObjectURL(url) URL.revokeObjectURL(url)
exportLoading.value = false
} }
// 线 // 线
const downloadPromptTemplate = async () => { const downloadPromptTemplate = async () => {
try { try {
importLoading.value = true
await fetch(downloadURL.value) await fetch(downloadURL.value)
.then(response => response.json()) .then(response => response.json())
.then((jsonData) => { .then((jsonData) => {
@ -214,7 +221,10 @@ const downloadPromptTemplate = async () => {
downloadURL.value = '' downloadURL.value = ''
} }
catch { catch {
message.error('网络导入出现问题,请检查网络状态与 JSON 文件有效性') message.error(t('store.downloadError'))
}
finally {
importLoading.value = false
} }
} }
@ -243,16 +253,16 @@ const pagination = computed(() => {
const createColumns = (): DataTableColumns<DataProps> => { const createColumns = (): DataTableColumns<DataProps> => {
return [ return [
{ {
title: '标题', title: t('store.title'),
key: 'renderKey', key: 'renderKey',
minWidth: 100, width: 160,
}, },
{ {
title: '内容', title: t('store.description'),
key: 'renderValue', key: 'renderValue',
}, },
{ {
title: '操作', title: t('common.action'),
key: 'actions', key: 'actions',
width: 100, width: 100,
align: 'center', align: 'center',
@ -266,7 +276,7 @@ const createColumns = (): DataTableColumns<DataProps> => {
type: 'info', type: 'info',
onClick: () => changeShowModal('modify', row), onClick: () => changeShowModal('modify', row),
}, },
{ default: () => '修改' }, { default: () => t('common.edit') },
), ),
h( h(
NButton, NButton,
@ -276,7 +286,7 @@ const createColumns = (): DataTableColumns<DataProps> => {
type: 'error', type: 'error',
onClick: () => deletePromptTemplate(row), onClick: () => deletePromptTemplate(row),
}, },
{ default: () => '删除' }, { default: () => t('common.delete') },
), ),
], ],
}) })
@ -301,26 +311,37 @@ watch(
<NCard> <NCard>
<div class="space-y-4"> <div class="space-y-4">
<NTabs type="segment"> <NTabs type="segment">
<NTabPane name="local" tab="本地管理"> <NTabPane name="local" :tab="$t('store.local')">
<NSpace justify="end"> <div class="flex items-center justify-end space-x-4">
<NButton type="primary" @click="changeShowModal('add')"> <NButton
添加 type="primary"
size="small"
@click="changeShowModal('add')"
>
{{ $t('common.add') }}
</NButton> </NButton>
<NButton @click="changeShowModal('local_import')"> <NButton
导入 size="small"
@click="changeShowModal('local_import')"
>
{{ $t('common.import') }}
</NButton> </NButton>
<NButton @click="exportPromptTemplate()"> <NButton
导出 size="small"
:loading="exportLoading"
@click="exportPromptTemplate()"
>
{{ $t('common.export') }}
</NButton> </NButton>
<NPopconfirm @positive-click="clearPromptTemplate"> <NPopconfirm @positive-click="clearPromptTemplate">
<template #trigger> <template #trigger>
<NButton> <NButton size="small">
清空 {{ $t('common.clear') }}
</NButton> </NButton>
</template> </template>
确认是否清空数据? {{ $t('store.clearStoreConfirm') }}
</NPopconfirm> </NPopconfirm>
</NSpace> </div>
<br> <br>
<NDataTable <NDataTable
:max-height="400" :max-height="400"
@ -330,20 +351,28 @@ watch(
:bordered="false" :bordered="false"
/> />
</NTabPane> </NTabPane>
<NTabPane name="download" tab="在线导入"> <NTabPane name="download" :tab="$t('store.online')">
注意请检查下载 JSON 文件来源恶意的JSON文件可能会破坏您的计算机<br><br> <p class="mb-4">
<NGrid x-gap="12" y-gap="12" :cols="24"> {{ $t('store.onlineImportWarning') }}
<NGi :span="isMobile ? 18 : 22"> </p>
<NInput v-model:value="downloadURL" placeholder="请输入正确的 JSON 地址" /> <div class="flex items-center gap-4">
</NGi> <NInput v-model:value="downloadURL" placeholder="" />
<NGi> <NButton
<NButton strong secondary :disabled="downloadDisabled" @click="downloadPromptTemplate()"> strong
下载 secondary
</NButton> :disabled="downloadDisabled"
</NGi> :loading="importLoading"
</NGrid> @click="downloadPromptTemplate()"
>
{{ $t('common.download') }}
</NButton>
</div>
<NDivider /> <NDivider />
<NLayoutContent v-if="isMobile" style="height: 360px" content-style=" background:none;" :native-scrollbar="false"> <NLayoutContent
style="height: 360px"
content-style="background: none;"
:native-scrollbar="false"
>
<NCard <NCard
v-for="info in promptRecommendList" v-for="info in promptRecommendList"
:key="info.key" :title="info.key" :key="info.key" :title="info.key"
@ -351,9 +380,14 @@ watch(
embedded embedded
:bordered="true" :bordered="true"
> >
{{ info.desc }} <p
class="overflow-hidden text-ellipsis whitespace-nowrap"
:title="info.desc"
>
{{ info.desc }}
</p>
<template #footer> <template #footer>
<NSpace justify="end"> <div class="flex items-center justify-end space-x-4">
<NButton text> <NButton text>
<a <a
:href="info.url" :href="info.url"
@ -365,39 +399,10 @@ watch(
<NButton text @click="setDownloadURL(info.downloadUrl) "> <NButton text @click="setDownloadURL(info.downloadUrl) ">
<SvgIcon class="text-xl" icon="ri:add-fill" /> <SvgIcon class="text-xl" icon="ri:add-fill" />
</NButton> </NButton>
</NSpace> </div>
</template> </template>
</NCard> </NCard>
</NLayoutContent> </NLayoutContent>
<NLayoutContent
v-else
style="height: 360px"
content-style="padding: 10px; background:none;"
:native-scrollbar="false"
>
<NGrid x-gap="12" y-gap="12" :cols="isMobile ? 1 : 3">
<NGi v-for="info in promptRecommendList" :key="info.key">
<NCard :title="info.key" embedded :bordered="true">
{{ info.desc }}
<template #footer>
<NSpace justify="end">
<NButton text>
<a
:href="info.url"
target="_blank"
>
<SvgIcon class="text-xl" icon="ri:link" />
</a>
</NButton>
<NButton text @click="setDownloadURL(info.downloadUrl) ">
<SvgIcon class="text-xl" icon="ri:add-fill" />
</NButton>
</NSpace>
</template>
</NCard>
</NGi>
</NGrid>
</NLayoutContent>
</NTabPane> </NTabPane>
</NTabs> </NTabs>
</div> </div>
@ -412,10 +417,10 @@ watch(
aria-modal="true" aria-modal="true"
> >
<NSpace v-if="modalMode === 'add' || modalMode === 'modify'" vertical> <NSpace v-if="modalMode === 'add' || modalMode === 'modify'" vertical>
模板标题 {{ t('store.title') }}
<NInput v-model:value="tempPromptKey" placeholder="搜索" /> <NInput v-model:value="tempPromptKey" />
模板内容 {{ t('store.description') }}
<NInput v-model:value="tempPromptValue" placeholder="搜索" type="textarea" /> <NInput v-model:value="tempPromptValue" type="textarea" />
<NButton <NButton
strong strong
secondary secondary
@ -423,13 +428,13 @@ watch(
:disabled="inputStatus" :disabled="inputStatus"
@click="() => { modalMode === 'add' ? addPromptTemplate() : modifyPromptTemplate() }" @click="() => { modalMode === 'add' ? addPromptTemplate() : modifyPromptTemplate() }"
> >
确定 {{ t('common.confirm') }}
</NButton> </NButton>
</NSpace> </NSpace>
<NSpace v-if="modalMode === 'local_import'" vertical> <NSpace v-if="modalMode === 'local_import'" vertical>
<NInput <NInput
v-model:value="tempPromptValue" v-model:value="tempPromptValue"
placeholder="请粘贴json文件内容" :placeholder="t('store.importPlaceholder')"
:autosize="{ minRows: 3, maxRows: 15 }" :autosize="{ minRows: 3, maxRows: 15 }"
type="textarea" type="textarea"
/> />
@ -440,7 +445,7 @@ watch(
:disabled="inputStatus" :disabled="inputStatus"
@click="() => { importPromptTemplate() }" @click="() => { importPromptTemplate() }"
> >
导入 {{ t('common.import') }}
</NButton> </NButton>
</NSpace> </NSpace>
</NCard> </NCard>

View File

@ -1,13 +1,25 @@
export default { export default {
common: { common: {
add: 'Add',
addSuccess: 'Add Success',
edit: 'Edit',
editSuccess: 'Edit Success',
delete: 'Delete', delete: 'Delete',
deleteSuccess: 'Delete Success',
save: 'Save', save: 'Save',
saveSuccess: 'Save Success',
reset: 'Reset', reset: 'Reset',
action: 'Action',
export: 'Export', export: 'Export',
exportSuccess: 'Export Success',
import: 'Import', import: 'Import',
importSuccess: 'Import Success',
clear: 'Clear', clear: 'Clear',
clearSuccess: 'Clear Success',
yes: 'Yes', yes: 'Yes',
no: 'No', no: 'No',
confirm: 'Confirm',
download: 'Download',
noData: 'No Data', noData: 'No Data',
wrong: 'Something went wrong, please try again later.', wrong: 'Something went wrong, please try again later.',
success: 'Success', success: 'Success',
@ -52,5 +64,21 @@ export default {
socks: 'Socks', socks: 'Socks',
httpsProxy: 'HTTPS Proxy', httpsProxy: 'HTTPS Proxy',
}, },
store: {
local: 'Local',
online: 'Online',
title: 'Title',
description: 'Description',
clearStoreConfirm: 'Whether to clear the data?',
importPlaceholder: 'Please paste the JSON data here',
addRepeatTitleTips: 'Title duplicate, please re-enter',
addRepeatContentTips: 'Content duplicate: {msg}, please re-enter',
editRepeatTitleTips: 'Title conflict, please revise',
editRepeatContentTips: 'Content conflict {msg} , please re-modify',
importError: 'Key value mismatch',
importRepeatTitle: 'Title repeatedly skipped: {msg}',
importRepeatContent: 'Content is repeatedly skipped: {msg}',
onlineImportWarning: 'Note: Please check the JSON file source!',
downloadError: 'Please check the network status and JSON file validity',
},
} }

View File

@ -21,9 +21,7 @@ const i18n = createI18n({
}, },
}) })
export function t(key: string) { export const t = i18n.global.t
return i18n.global.t(key)
}
export function setLocale(locale: Language) { export function setLocale(locale: Language) {
i18n.global.locale = locale i18n.global.locale = locale

View File

@ -1,13 +1,25 @@
export default { export default {
common: { common: {
add: '添加',
addSuccess: '添加成功',
edit: '编辑',
editSuccess: '编辑成功',
delete: '删除', delete: '删除',
deleteSuccess: '删除成功',
save: '保存', save: '保存',
saveSuccess: '保存成功',
reset: '重置', reset: '重置',
action: '操作',
export: '导出', export: '导出',
exportSuccess: '导出成功',
import: '导入', import: '导入',
importSuccess: '导入成功',
clear: '清空', clear: '清空',
clearSuccess: '清空成功',
yes: '是', yes: '是',
no: '否', no: '否',
confirm: '确定',
download: '下载',
noData: '暂无数据', noData: '暂无数据',
wrong: '好像出错了,请稍后再试。', wrong: '好像出错了,请稍后再试。',
success: '操作成功', success: '操作成功',
@ -52,5 +64,21 @@ export default {
socks: 'Socks', socks: 'Socks',
httpsProxy: 'HTTPS Proxy', httpsProxy: 'HTTPS Proxy',
}, },
store: {
local: '本地',
online: '在线',
title: '标题',
description: '描述',
clearStoreConfirm: '是否清空数据?',
importPlaceholder: '请粘贴 JSON 数据到此处',
addRepeatTitleTips: '标题重复,请重新输入',
addRepeatContentTips: '内容重复:{msg},请重新输入',
editRepeatTitleTips: '标题冲突,请重新修改',
editRepeatContentTips: '内容冲突{msg} ,请重新修改',
importError: '键值不匹配',
importRepeatTitle: '标题重复跳过:{msg}',
importRepeatContent: '内容重复跳过:{msg}',
onlineImportWarning: '注意:请检查 JSON 文件来源!',
downloadError: '请检查网络状态与 JSON 文件有效性',
},
} }

View File

@ -1,11 +1,21 @@
export default { export default {
common: { common: {
add: '添加',
addSuccess: '添加成功',
edit: '编辑',
editSuccess: '编辑成功',
delete: '刪除', delete: '刪除',
deleteSuccess: '删除成功',
save: '儲存', save: '儲存',
saveSuccess: '儲存成功',
reset: '重設', reset: '重設',
action: '操作',
export: '匯出', export: '匯出',
exportSuccess: '匯出成功',
import: '匯入', import: '匯入',
importSuccess: '匯入成功',
clear: '清除', clear: '清除',
clearSuccess: '清除成功',
yes: '是', yes: '是',
no: '否', no: '否',
noData: '暫無資料', noData: '暫無資料',
@ -49,5 +59,23 @@ export default {
reverseProxy: '反向代理', reverseProxy: '反向代理',
timeout: '逾時', timeout: '逾時',
socks: 'Socks', socks: 'Socks',
httpsProxy: 'HTTPS Proxy',
},
store: {
local: '本地',
online: '在线',
title: '標題',
description: '描述',
clearStoreConfirm: '是否清除數據?',
importPlaceholder: '請粘貼 JSON 數據到此處',
addRepeatTitleTips: '標題重複,請重新輸入',
addRepeatContentTips: '內容重複:{msg},請重新輸入',
editRepeatTitleTips: '標題衝突,請重新修改',
editRepeatContentTips: '內容冲突{msg} ,請重新修改',
importError: '鍵值不匹配',
importRepeatTitle: '因標題重複跳過:{msg}',
importRepeatContent: '因內容重複跳過:{msg}',
onlineImportWarning: '注意:請檢查 JSON 文件來源!',
downloadError: '請檢查網絡狀態與 JSON 文件有效性',
}, },
} }