111 lines
2.7 KiB
Vue
111 lines
2.7 KiB
Vue
<script setup>
|
|
/**
|
|
* 多语言名称编辑弹窗(通用组件)。
|
|
* 用于编辑一个对象在不同语言下的名称,支持任意语言列表。
|
|
*
|
|
* 用法:
|
|
* <I18nNameModal
|
|
* :open="open"
|
|
* :languages="I18N_LANGUAGES"
|
|
* :model-value="{ 'zh-CN': '名称', 'en-US': 'Name' }"
|
|
* @update:model-value="(v) => writeBack(v)"
|
|
* @update:open="(v) => (open = v)"
|
|
* />
|
|
*/
|
|
import { ref, watch } from 'vue'
|
|
import { Modal, Button, Input } from '@/components/ui'
|
|
|
|
defineOptions({ inheritAttrs: false })
|
|
|
|
const props = defineProps({
|
|
/** 受控打开状态 */
|
|
open: { type: Boolean, default: false },
|
|
/** 弹窗标题 */
|
|
title: { type: String, default: '多语言名称' },
|
|
/** 语言列表 [{ code, name, default }] */
|
|
languages: { type: Array, default: () => [] },
|
|
/** 各语言值 { [code]: value } */
|
|
modelValue: { type: Object, default: () => ({}) },
|
|
/** 底部提示文字 */
|
|
hint: { type: String, default: '' },
|
|
})
|
|
|
|
const emit = defineEmits(['update:open', 'update:modelValue', 'confirm'])
|
|
|
|
/** 本地草稿:打开时快照,确定时提交 */
|
|
const draft = ref({})
|
|
watch(
|
|
() => props.open,
|
|
(open) => {
|
|
if (open) draft.value = { ...props.modelValue }
|
|
},
|
|
)
|
|
|
|
function onValueChange(lang, value) {
|
|
draft.value = { ...draft.value, [lang.code]: value }
|
|
}
|
|
|
|
function onOk() {
|
|
emit('update:modelValue', { ...draft.value })
|
|
emit('confirm', { ...draft.value })
|
|
emit('update:open', false)
|
|
}
|
|
function onCancel() {
|
|
emit('update:open', false)
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<Modal :open="open" :title="title" :width="520" :mask-closable="false" @cancel="onCancel">
|
|
<div class="i18n-name-modal">
|
|
<div class="i18n-name-row" v-for="lang in languages" :key="lang.code">
|
|
<span class="i18n-name-row__label">{{ lang.name }}</span>
|
|
<Input
|
|
class="i18n-name-row__input"
|
|
:model-value="draft[lang.code] || ''"
|
|
:placeholder="`请输入${lang.name}名称`"
|
|
@update:model-value="(v) => onValueChange(lang, v)"
|
|
/>
|
|
</div>
|
|
<p v-if="hint" class="i18n-name-modal__hint">{{ hint }}</p>
|
|
</div>
|
|
<template #footer>
|
|
<Button type="outline" @click="onCancel">取消</Button>
|
|
<Button type="primary" @click="onOk">确定</Button>
|
|
</template>
|
|
</Modal>
|
|
</template>
|
|
|
|
<style scoped lang="scss">
|
|
.i18n-name-modal {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 10px;
|
|
}
|
|
|
|
.i18n-name-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
max-width: 480px;
|
|
}
|
|
|
|
.i18n-name-row__label {
|
|
flex-shrink: 0;
|
|
width: 80px;
|
|
font-size: 13px;
|
|
color: var(--fms-text-secondary);
|
|
}
|
|
|
|
.i18n-name-row__input {
|
|
flex: 1;
|
|
min-width: 0;
|
|
}
|
|
|
|
.i18n-name-modal__hint {
|
|
margin: 0;
|
|
font-size: 12px;
|
|
line-height: 1.7;
|
|
color: var(--fms-text-secondary);
|
|
}
|
|
</style> |