Merge pull request #334 from Rewrite0/i18n

Optimizing webui i18n
This commit is contained in:
Rewrite0
2023-06-14 23:16:41 +08:00
committed by GitHub
32 changed files with 405 additions and 227 deletions

View File

@@ -30,6 +30,7 @@
"devDependencies": {
"@antfu/eslint-config": "^0.38.6",
"@icon-park/vue-next": "^1.4.2",
"@intlify/unplugin-vue-i18n": "^0.11.0",
"@storybook/addon-essentials": "^7.0.12",
"@storybook/addon-interactions": "^7.0.12",
"@storybook/addon-links": "^7.0.12",

168
webui/pnpm-lock.yaml generated
View File

@@ -1,4 +1,4 @@
lockfileVersion: '6.1'
lockfileVersion: '6.0'
settings:
autoInstallPeers: true
@@ -40,6 +40,9 @@ devDependencies:
'@icon-park/vue-next':
specifier: ^1.4.2
version: 1.4.2(vue@3.3.4)
'@intlify/unplugin-vue-i18n':
specifier: ^0.11.0
version: 0.11.0(vue-i18n@9.2.2)
'@storybook/addon-essentials':
specifier: ^7.0.12
version: 7.0.12(react-dom@18.2.0)(react@18.2.0)
@@ -1847,6 +1850,31 @@ packages:
- supports-color
dev: true
/@intlify/bundle-utils@6.0.1(vue-i18n@9.2.2):
resolution: {integrity: sha512-BkeZNKZiC0B7K3OYMwiPLoAqsZmKH3SxTL75vYAkuQ//XWR8WO0NpfjXhTxgLTVFHxMcNb2agAopC0DP6fqDrg==}
engines: {node: '>= 14.16'}
peerDependencies:
petite-vue-i18n: '*'
vue-i18n: '*'
peerDependenciesMeta:
petite-vue-i18n:
optional: true
vue-i18n:
optional: true
dependencies:
'@intlify/message-compiler': 9.3.0-beta.17
'@intlify/shared': 9.3.0-beta.17
acorn: 8.8.2
escodegen: 2.0.0
estree-walker: 2.0.2
jsonc-eslint-parser: 1.4.1
magic-string: 0.30.0
mlly: 1.2.1
source-map: 0.6.1
vue-i18n: 9.2.2(vue@3.3.4)
yaml-eslint-parser: 0.3.2
dev: true
/@intlify/core-base@9.2.2:
resolution: {integrity: sha512-JjUpQtNfn+joMbrXvpR4hTF8iJQ2sEFzzK3KIESOx+f+uwIjgw20igOyaIdhfsVVBCds8ZM64MoeNSx+PHQMkA==}
engines: {node: '>= 14'}
@@ -1855,14 +1883,12 @@ packages:
'@intlify/message-compiler': 9.2.2
'@intlify/shared': 9.2.2
'@intlify/vue-devtools': 9.2.2
dev: false
/@intlify/devtools-if@9.2.2:
resolution: {integrity: sha512-4ttr/FNO29w+kBbU7HZ/U0Lzuh2cRDhP8UlWOtV9ERcjHzuyXVZmjyleESK6eVP60tGC9QtQW9yZE+JeRhDHkg==}
engines: {node: '>= 14'}
dependencies:
'@intlify/shared': 9.2.2
dev: false
/@intlify/message-compiler@9.2.2:
resolution: {integrity: sha512-IUrQW7byAKN2fMBe8z6sK6riG1pue95e5jfokn8hA5Q3Bqy4MBJ5lJAofUsawQJYHeoPJ7svMDyBaVJ4d0GTtA==}
@@ -1870,12 +1896,56 @@ packages:
dependencies:
'@intlify/shared': 9.2.2
source-map: 0.6.1
dev: false
/@intlify/message-compiler@9.3.0-beta.17:
resolution: {integrity: sha512-i7hvVIRk1Ax2uKa9xLRJCT57to08OhFMhFXXjWN07rmx5pWQYQ23MfX1xgggv9drnWTNhqEiD+u4EJeHoS5+Ww==}
engines: {node: '>= 14'}
dependencies:
'@intlify/shared': 9.3.0-beta.17
source-map: 0.6.1
dev: true
/@intlify/shared@9.2.2:
resolution: {integrity: sha512-wRwTpsslgZS5HNyM7uDQYZtxnbI12aGiBZURX3BTR9RFIKKRWpllTsgzHWvj3HKm3Y2Sh5LPC1r0PDCKEhVn9Q==}
engines: {node: '>= 14'}
dev: false
/@intlify/shared@9.3.0-beta.17:
resolution: {integrity: sha512-mscf7RQsUTOil35jTij4KGW1RC9SWQjYScwLxP53Ns6g24iEd5HN7ksbt9O6FvTmlQuX77u+MXpBdfJsGqizLQ==}
engines: {node: '>= 14'}
dev: true
/@intlify/unplugin-vue-i18n@0.11.0(vue-i18n@9.2.2):
resolution: {integrity: sha512-ivcLZo08fvepHWV8o5lcKfhcKFSWqhwrqIAU6pUIbvq2ICo9fnXnIPYIZj7FeuHDLW1G3ADm44ZhQC3nYmvDlg==}
engines: {node: '>= 14.16'}
peerDependencies:
petite-vue-i18n: '*'
vue-i18n: '*'
vue-i18n-bridge: '*'
peerDependenciesMeta:
petite-vue-i18n:
optional: true
vue-i18n:
optional: true
vue-i18n-bridge:
optional: true
dependencies:
'@intlify/bundle-utils': 6.0.1(vue-i18n@9.2.2)
'@intlify/shared': 9.3.0-beta.17
'@rollup/pluginutils': 5.0.2
'@vue/compiler-sfc': 3.3.4
debug: 4.3.4
fast-glob: 3.2.12
js-yaml: 4.1.0
json5: 2.2.3
pathe: 1.1.0
picocolors: 1.0.0
source-map: 0.6.1
unplugin: 1.3.1
vue-i18n: 9.2.2(vue@3.3.4)
transitivePeerDependencies:
- rollup
- supports-color
dev: true
/@intlify/vue-devtools@9.2.2:
resolution: {integrity: sha512-+dUyqyCHWHb/UcvY1MlIpO87munedm3Gn6E9WWYdWrMuYLcoIoOEVDWSS8xSwtlPU+kA+MEQTP6Q1iI/ocusJg==}
@@ -1883,7 +1953,6 @@ packages:
dependencies:
'@intlify/core-base': 9.2.2
'@intlify/shared': 9.2.2
dev: false
/@istanbuljs/load-nyc-config@1.1.0:
resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==}
@@ -3769,6 +3838,14 @@ packages:
negotiator: 0.6.3
dev: true
/acorn-jsx@5.3.2(acorn@7.4.1):
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
peerDependencies:
acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
dependencies:
acorn: 7.4.1
dev: true
/acorn-jsx@5.3.2(acorn@8.8.2):
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
peerDependencies:
@@ -5050,6 +5127,19 @@ packages:
engines: {node: '>=12'}
dev: true
/escodegen@2.0.0:
resolution: {integrity: sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==}
engines: {node: '>=6.0'}
hasBin: true
dependencies:
esprima: 4.0.1
estraverse: 5.3.0
esutils: 2.0.3
optionator: 0.8.3
optionalDependencies:
source-map: 0.6.1
dev: true
/eslint-config-prettier@8.8.0(eslint@8.41.0):
resolution: {integrity: sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==}
hasBin: true
@@ -5435,6 +5525,15 @@ packages:
- supports-color
dev: true
/espree@6.2.1:
resolution: {integrity: sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==}
engines: {node: '>=6.0.0'}
dependencies:
acorn: 7.4.1
acorn-jsx: 5.3.2(acorn@7.4.1)
eslint-visitor-keys: 1.3.0
dev: true
/espree@9.5.2:
resolution: {integrity: sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -6676,6 +6775,17 @@ packages:
hasBin: true
dev: true
/jsonc-eslint-parser@1.4.1:
resolution: {integrity: sha512-hXBrvsR1rdjmB2kQmUjf1rEIa+TqHBGMge8pwi++C+Si1ad7EjZrJcpgwym+QGK/pqTx+K7keFAtLlVNdLRJOg==}
engines: {node: '>=8.10.0'}
dependencies:
acorn: 7.4.1
eslint-utils: 2.1.0
eslint-visitor-keys: 1.3.0
espree: 6.2.1
semver: 6.3.0
dev: true
/jsonc-eslint-parser@2.3.0:
resolution: {integrity: sha512-9xZPKVYp9DxnM3sd1yAsh/d59iIaswDkai8oTxbursfKYbg/ibjX0IzFt35+VZ8iEW453TVTXztnRvYUQlAfUQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -6733,6 +6843,14 @@ packages:
engines: {node: '>=6'}
dev: true
/levn@0.3.0:
resolution: {integrity: sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==}
engines: {node: '>= 0.8.0'}
dependencies:
prelude-ls: 1.1.2
type-check: 0.3.2
dev: true
/levn@0.4.1:
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
engines: {node: '>= 0.8.0'}
@@ -7321,6 +7439,18 @@ packages:
is-wsl: 2.2.0
dev: true
/optionator@0.8.3:
resolution: {integrity: sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==}
engines: {node: '>= 0.8.0'}
dependencies:
deep-is: 0.1.4
fast-levenshtein: 2.0.6
levn: 0.3.0
prelude-ls: 1.1.2
type-check: 0.3.2
word-wrap: 1.2.3
dev: true
/optionator@0.9.1:
resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==}
engines: {node: '>= 0.8.0'}
@@ -7578,6 +7708,11 @@ packages:
picocolors: 1.0.0
source-map-js: 1.0.2
/prelude-ls@1.1.2:
resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==}
engines: {node: '>= 0.8.0'}
dev: true
/prelude-ls@1.2.1:
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
engines: {node: '>= 0.8.0'}
@@ -8667,6 +8802,13 @@ packages:
typescript: 4.9.5
dev: true
/type-check@0.3.2:
resolution: {integrity: sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==}
engines: {node: '>= 0.8.0'}
dependencies:
prelude-ls: 1.1.2
dev: true
/type-check@0.4.0:
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
engines: {node: '>= 0.8.0'}
@@ -9283,7 +9425,6 @@ packages:
'@intlify/vue-devtools': 9.2.2
'@vue/devtools-api': 6.5.0
vue: 3.3.4
dev: false
/vue-inbrowser-compiler-independent-utils@4.71.1(vue@3.3.4):
resolution: {integrity: sha512-K3wt3iVmNGaFEOUR4JIThQRWfqokxLfnPslD41FDZB2ajXp789+wCqJyGYlIFsvEQ2P61PInw6/ph5iiqg51gg==}
@@ -9541,6 +9682,14 @@ packages:
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
dev: true
/yaml-eslint-parser@0.3.2:
resolution: {integrity: sha512-32kYO6kJUuZzqte82t4M/gB6/+11WAuHiEnK7FreMo20xsCKPeFH5tDBU7iWxR7zeJpNnMXfJyXwne48D0hGrg==}
dependencies:
eslint-visitor-keys: 1.3.0
lodash: 4.17.21
yaml: 1.10.2
dev: true
/yaml-eslint-parser@1.2.2:
resolution: {integrity: sha512-pEwzfsKbTrB8G3xc/sN7aw1v6A6c/pKxLAkjclnAyo5g5qOh6eL9WGu0o3cSDQZKrTNk4KL4lQSwZW+nBkANEg==}
engines: {node: ^14.17.0 || >=16.0.0}
@@ -9550,6 +9699,11 @@ packages:
yaml: 2.2.2
dev: true
/yaml@1.10.2:
resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==}
engines: {node: '>= 6'}
dev: true
/yaml@2.2.2:
resolution: {integrity: sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==}
engines: {node: '>= 14'}

View File

@@ -115,7 +115,7 @@ async function subscribe() {
<div v-if="!analysis.next" space-y-12px>
<ab-setting
v-model:data="rss"
:label="$t('topbar.add.rsslink')"
:label="$t('topbar.add.rss_link')"
type="input"
:prop="{
placeholder: $t('topbar.add.placeholder'),

View File

@@ -9,7 +9,7 @@ const { user, update } = useAuth();
<template>
<ab-popup
v-model:show="show"
:title="$t('topbar.profile.poptitle')"
:title="$t('topbar.profile.pop_title')"
css="w-365px"
>
<div space-y-16px>
@@ -35,7 +35,7 @@ const { user, update } = useAuth();
<div flex="~ justify-end">
<ab-button size="small" @click="update">{{
$t('topbar.profile.updatebtn')
$t('topbar.profile.update_btn')
}}</ab-button>
</div>
</div>

View File

@@ -1,5 +1,4 @@
<script lang="ts" setup>
import { useI18n } from 'vue-i18n';
import type { BangumiRule } from '#/bangumi';
const emit = defineEmits<{
@@ -11,7 +10,9 @@ const emit = defineEmits<{
opts: { id: number; deleteFile: boolean }
): void;
}>();
const { t } = useI18n({ useScope: 'global' });
const { t } = useMyI18n();
const show = defineModel('show', { default: false });
const rule = defineModel<BangumiRule>('rule', {
required: true,
@@ -53,9 +54,9 @@ function emitEnable() {
const popupTitle = computed(() => {
if (rule.value.deleted) {
return t('homepage.rule.enablerule');
return t('homepage.rule.enable_rule');
} else {
return t('homepage.rule.editrule');
return t('homepage.rule.edit_rule');
}
});
@@ -71,16 +72,16 @@ const boxSize = computed(() => {
<template>
<ab-popup v-model:show="show" :title="popupTitle" :css="boxSize">
<div v-if="rule.deleted">
<div>{{ $t('homepage.rule.enablehit') }}</div>
<div>{{ $t('homepage.rule.enable_hit') }}</div>
<div line my-8px></div>
<div fx-cer justify-center space-x-10px>
<ab-button size="small" type="warn" @click="() => emitEnable()">{{
$t('homepage.rule.yesbtn')
$t('homepage.rule.yes_btn')
}}</ab-button>
<ab-button size="small" @click="() => close()">{{
$t('homepage.rule.nobtn')
$t('homepage.rule.no_btn')
}}</ab-button>
</div>
</div>
@@ -111,7 +112,7 @@ const boxSize = computed(() => {
v-model:show="deleteFileDialog.show"
:title="$t('homepage.rule.delete')"
>
<div>{{ $t('homepage.rule.deletehit') }}</div>
<div>{{ $t('homepage.rule.delete_hit') }}</div>
<div line my-8px></div>
<div fx-cer justify-center space-x-10px>
@@ -119,10 +120,10 @@ const boxSize = computed(() => {
size="small"
type="warn"
@click="() => emitdeleteFile(true)"
>{{ $t('homepage.rule.yesbtn') }}</ab-button
>{{ $t('homepage.rule.yes_btn') }}</ab-button
>
<ab-button size="small" @click="() => emitdeleteFile(false)">{{
$t('homepage.rule.nobtn')
$t('homepage.rule.no_btn')
}}</ab-button>
</div>
</ab-popup>

View File

@@ -1,17 +1,25 @@
<script lang="ts" setup>
withDefaults(
const props = withDefaults(
defineProps<{
label: string;
label: string | (() => string);
}>(),
{
label: '',
}
);
function abLabel() {
if (typeof props.label === 'function') {
return props.label();
} else {
return props.label;
}
}
</script>
<template>
<div flex="~ items-start" justify-between>
<div>{{ label }}</div>
<div>{{ abLabel() }}</div>
<slot> </slot>
</div>

View File

@@ -1,9 +1,9 @@
<script lang="ts" setup>
import { useI18n } from 'vue-i18n';
import type { BangumiRule } from '#/bangumi';
import type { SettingItem } from '#/components';
const { t } = useI18n({ useScope: 'global' });
const { t } = useMyI18n();
const rule = defineModel<BangumiRule>('rule', {
required: true,
});
@@ -11,7 +11,7 @@ const rule = defineModel<BangumiRule>('rule', {
const items: SettingItem<BangumiRule>[] = [
{
configKey: 'official_title',
label: t('homepage.rule.officaltitle'),
label: () => t('homepage.rule.offical_title'),
type: 'input',
prop: {
type: 'text',
@@ -19,7 +19,7 @@ const items: SettingItem<BangumiRule>[] = [
},
{
configKey: 'year',
label: t('homepage.rule.year'),
label: () => t('homepage.rule.year'),
type: 'input',
css: 'w-72px',
prop: {
@@ -28,7 +28,7 @@ const items: SettingItem<BangumiRule>[] = [
},
{
configKey: 'season',
label: t('homepage.rule.season'),
label: () => t('homepage.rule.season'),
type: 'input',
css: 'w-72px',
prop: {
@@ -38,7 +38,7 @@ const items: SettingItem<BangumiRule>[] = [
},
{
configKey: 'offset',
label: t('homepage.rule.offset'),
label: () => t('homepage.rule.offset'),
type: 'input',
css: 'w-72px',
prop: {
@@ -47,7 +47,7 @@ const items: SettingItem<BangumiRule>[] = [
},
{
configKey: 'filter',
label: t('homepage.rule.exclude'),
label: () => t('homepage.rule.exclude'),
type: 'dynamic-tags',
bottomLine: true,
},

View File

@@ -1,6 +1,6 @@
<script lang="ts" setup>
import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/vue';
import { AddOne, Earth, More } from '@icon-park/vue-next';
import { AddOne, International, More } from '@icon-park/vue-next';
withDefaults(
defineProps<{
@@ -8,7 +8,7 @@ withDefaults(
items: {
id: number;
icon: any;
label: string;
label: string | (() => string);
handle?: () => void | Promise<void>;
}[];
}>(),
@@ -17,14 +17,25 @@ withDefaults(
}
);
defineEmits(['clickAdds']);
defineEmits<{
(e: 'changeLang'): void;
(e: 'clickAdd'): void;
}>();
function abLabel(label: string | (() => string)) {
if (typeof label === 'function') {
return label();
} else {
return label;
}
}
</script>
<template>
<Menu>
<div rel>
<div fx-cer space-x-16px>
<Earth
<International
theme="outline"
size="24"
fill="#fff"
@@ -73,7 +84,7 @@ defineEmits(['clickAdds']);
:class="[active ? 'text-white bg-theme-row' : 'text-black']"
@click="() => i.handle && i.handle()"
>
<div text-main>{{ i.label }}</div>
<div text-main>{{ abLabel(i.label) }}</div>
<div
class="group-hover:text-white"

View File

@@ -9,7 +9,6 @@ import {
Play,
SettingTwo,
} from '@icon-park/vue-next';
import { useI18n } from 'vue-i18n';
const props = withDefaults(
defineProps<{
@@ -19,7 +18,9 @@ const props = withDefaults(
open: false,
}
);
const { t } = useI18n({ useScope: 'global' });
const { t } = useMyI18n();
const show = ref(props.open);
const toggle = () => (show.value = !show.value);
const route = useRoute();
@@ -29,39 +30,39 @@ const items = [
{
id: 1,
icon: Home,
label: t('sidebar.homepage'),
label: () => t('sidebar.homepage'),
path: '/bangumi',
},
{
id: 2,
icon: Calendar,
label: t('sidebar.calendar'),
label: () => t('sidebar.calendar'),
path: '/calendar',
hidden: true,
},
{
id: 3,
icon: Play,
label: t('sidebar.player'),
label: () => t('sidebar.player'),
path: '/player',
},
{
id: 4,
icon: Download,
label: t('sidebar.downloader'),
label: () => t('sidebar.downloader'),
path: '/downloader',
hidden: true,
},
{
id: 5,
icon: Log,
label: t('sidebar.log'),
label: () => t('sidebar.log'),
path: '/log',
},
{
id: 6,
icon: SettingTwo,
label: t('sidebar.config'),
label: () => t('sidebar.config'),
path: '/config',
},
];
@@ -107,7 +108,7 @@ const items = [
:key="i.id"
:to="i.path"
replace
:title="i.label"
:title="i.label()"
fx-cer
px-24px
space-x-42px
@@ -122,7 +123,7 @@ const items = [
]"
>
<Component :is="i.icon" :size="24" />
<div text-h2>{{ i.label }}</div>
<div text-h2>{{ i.label() }}</div>
</RouterLink>
<div

View File

@@ -7,9 +7,9 @@ import {
Power,
Refresh,
} from '@icon-park/vue-next';
import { useI18n } from 'vue-i18n';
const { t, locale } = useI18n({ useScope: 'global' });
const { t, changeLocale } = useMyI18n();
const search = ref('');
const show = ref(false);
const showAdd = ref(false);
@@ -21,37 +21,37 @@ const { running } = storeToRefs(useProgramStore());
const items = [
{
id: 1,
label: t('topbar.start'),
label: () => t('topbar.start'),
icon: PlayOne,
handle: start,
},
{
id: 2,
label: t('topbar.pause'),
label: () => t('topbar.pause'),
icon: Pause,
handle: pause,
},
{
id: 3,
label: t('topbar.restart'),
label: () => t('topbar.restart'),
icon: Refresh,
handle: restart,
},
{
id: 4,
label: t('topbar.shutdown'),
label: () => t('topbar.shutdown'),
icon: Power,
handle: shutdown,
},
{
id: 5,
label: t('topbar.resetrule'),
label: () => t('topbar.reset_rule'),
icon: Format,
handle: resetRule,
},
{
id: 6,
label: t('topbar.profile.title'),
label: () => t('topbar.profile.title'),
icon: Me,
handle: () => {
show.value = true;
@@ -66,20 +66,6 @@ onBeforeMount(() => {
onUnmounted(() => {
offUpdate();
});
function changeLocale() {
if (localStorage.getItem('lang') === 'zh-CN') {
const newLang = 'en-US';
locale.value = newLang;
localStorage.setItem('lang', newLang);
location.reload();
} else {
const newLang = 'zh-CN';
locale.value = newLang;
localStorage.setItem('lang', newLang);
location.reload();
}
}
</script>
<template>

View File

@@ -1,9 +1,8 @@
<script lang="ts" setup>
import { useI18n } from 'vue-i18n';
import type { Downloader, DownloaderType } from '#/config';
import type { SettingItem } from '#/components';
const { t } = useI18n({ useScope: 'global' });
const { t } = useMyI18n();
const { getSettingGroup } = useConfigStore();
const downloader = getSettingGroup('downloader');
@@ -12,7 +11,7 @@ const downloaderType: DownloaderType = ['qbittorrent'];
const items: SettingItem<Downloader>[] = [
{
configKey: 'type',
label: t('config.downloaderset.type'),
label: () => t('config.downloader_set.type'),
type: 'select',
css: 'w-115px',
prop: {
@@ -21,7 +20,7 @@ const items: SettingItem<Downloader>[] = [
},
{
configKey: 'host',
label: t('config.downloaderset.host'),
label: () => t('config.downloader_set.host'),
type: 'input',
prop: {
type: 'text',
@@ -30,7 +29,7 @@ const items: SettingItem<Downloader>[] = [
},
{
configKey: 'username',
label: t('config.downloaderset.username'),
label: () => t('config.downloader_set.username'),
type: 'input',
prop: {
type: 'text',
@@ -39,7 +38,7 @@ const items: SettingItem<Downloader>[] = [
},
{
configKey: 'password',
label: t('config.downloaderset.password'),
label: () => t('config.downloader_set.password'),
type: 'input',
prop: {
type: 'text',
@@ -49,7 +48,7 @@ const items: SettingItem<Downloader>[] = [
},
{
configKey: 'path',
label: t('config.downloaderset.path'),
label: () => t('config.downloader_set.path'),
type: 'input',
prop: {
type: 'text',
@@ -58,14 +57,14 @@ const items: SettingItem<Downloader>[] = [
},
{
configKey: 'ssl',
label: t('config.downloaderset.ssl'),
label: () => t('config.downloader_set.ssl'),
type: 'switch',
},
];
</script>
<template>
<ab-fold-panel :title="$t('config.downloaderset.title')">
<ab-fold-panel :title="$t('config.downloader_set.title')">
<div space-y-12px>
<ab-setting
v-for="i in items"

View File

@@ -1,9 +1,8 @@
<script lang="ts" setup>
import { useI18n } from 'vue-i18n';
import type { BangumiManage, RenameMethod } from '#/config';
import type { SettingItem } from '#/components';
const { t } = useI18n({ useScope: 'global' });
const { t } = useMyI18n();
const { getSettingGroup } = useConfigStore();
const manage = getSettingGroup('bangumi_manage');
@@ -12,12 +11,12 @@ const renameMethod: RenameMethod = ['normal', 'pn', 'advance', 'none'];
const items: SettingItem<BangumiManage>[] = [
{
configKey: 'enable',
label: t('config.manageset.enable'),
label: () => t('config.manage_set.enable'),
type: 'switch',
},
{
configKey: 'rename_method',
label: t('config.manageset.method'),
label: () => t('config.manage_set.method'),
type: 'select',
prop: {
items: renameMethod,
@@ -26,24 +25,24 @@ const items: SettingItem<BangumiManage>[] = [
},
{
configKey: 'eps_complete',
label: t('config.manageset.eps'),
label: () => t('config.manage_set.eps'),
type: 'switch',
},
{
configKey: 'group_tag',
label: t('config.manageset.grouptag'),
label: () => t('config.manage_set.group_tag'),
type: 'switch',
},
{
configKey: 'remove_bad_torrent',
label: t('config.manageset.deletebadtorr'),
label: () => t('config.manage_set.delete_bad_torrent'),
type: 'switch',
},
];
</script>
<template>
<ab-fold-panel :title="$t('config.manageset.title')">
<ab-fold-panel :title="$t('config.manage_set.title')">
<div space-y-12px>
<ab-setting
v-for="i in items"

View File

@@ -1,9 +1,8 @@
<script lang="ts" setup>
import { useI18n } from 'vue-i18n';
import type { Log, Program } from '#/config';
import type { SettingItem } from '#/components';
const { t } = useI18n({ useScope: 'global' });
const { t } = useMyI18n();
const { getSettingGroup } = useConfigStore();
const program = getSettingGroup('program');
@@ -12,7 +11,7 @@ const log = getSettingGroup('log');
const programItems: SettingItem<Program>[] = [
{
configKey: 'rss_time',
label: t('config.normalset.rssintvl'),
label: () => t('config.normal_set.rss_interval'),
type: 'input',
css: 'w-72px',
prop: {
@@ -22,7 +21,7 @@ const programItems: SettingItem<Program>[] = [
},
{
configKey: 'rename_time',
label: t('config.normalset.renameintvl'),
label: () => t('config.normal_set.rename_interval'),
type: 'input',
css: 'w-72px',
prop: {
@@ -32,7 +31,7 @@ const programItems: SettingItem<Program>[] = [
},
{
configKey: 'webui_port',
label: t('config.normalset.webport'),
label: () => t('config.normal_set.web_port'),
type: 'input',
css: 'w-72px',
prop: {
@@ -45,13 +44,13 @@ const programItems: SettingItem<Program>[] = [
const logItems: SettingItem<Log> = {
configKey: 'debug_enable',
label: t('config.normalset.debug'),
label: () => t('config.normal_set.debug'),
type: 'switch',
};
</script>
<template>
<ab-fold-panel :title="$t('config.normalset.title')">
<ab-fold-panel :title="$t('config.normal_set.title')">
<div space-y-12px>
<ab-setting
v-for="i in programItems"

View File

@@ -1,9 +1,8 @@
<script lang="ts" setup>
import { useI18n } from 'vue-i18n';
import type { Notification, NotificationType } from '#/config';
import type { SettingItem } from '#/components';
const { t } = useI18n({ useScope: 'global' });
const { t } = useMyI18n();
const { getSettingGroup } = useConfigStore();
const notification = getSettingGroup('notification');
@@ -17,13 +16,13 @@ const notificationType: NotificationType = [
const items: SettingItem<Notification>[] = [
{
configKey: 'enable',
label: t('config.notificationset.enable'),
label: () => t('config.notification_set.enable'),
type: 'switch',
bottomLine: true,
},
{
configKey: 'type',
label: t('config.notificationset.type'),
label: () => t('config.notification_set.type'),
type: 'select',
css: 'w-140px',
prop: {
@@ -32,7 +31,7 @@ const items: SettingItem<Notification>[] = [
},
{
configKey: 'token',
label: t('config.notificationset.token'),
label: () => t('config.notification_set.token'),
type: 'input',
prop: {
type: 'text',
@@ -41,7 +40,7 @@ const items: SettingItem<Notification>[] = [
},
{
configKey: 'chat_id',
label: t('config.notificationset.chatid'),
label: () => t('config.notification_set.chat_id'),
type: 'input',
prop: {
type: 'text',
@@ -52,7 +51,7 @@ const items: SettingItem<Notification>[] = [
</script>
<template>
<ab-fold-panel :title="$t('config.notificationset.title')">
<ab-fold-panel :title="$t('config.notification_set.title')">
<div space-y-12px>
<ab-setting
v-for="i in items"

View File

@@ -1,5 +1,4 @@
<script lang="ts" setup>
import { useI18n } from 'vue-i18n';
import type {
RssParser,
RssParserLang,
@@ -8,12 +7,13 @@ import type {
} from '#/config';
import type { SettingItem } from '#/components';
const { t } = useI18n({ useScope: 'global' });
const { t } = useMyI18n();
const { getSettingGroup } = useConfigStore();
const parser = getSettingGroup('rss_parser');
const sourceItems: RssParserType = ['mikan'];
/** @ts-expect-error Incorrect order */
const langs: RssParserLang = ['zh', 'en', 'jp'];
/** @ts-expect-error Incorrect order */
const parserMethods: RssParserMethodType = ['tmdb', 'mikan', 'parser'];
@@ -21,12 +21,12 @@ const parserMethods: RssParserMethodType = ['tmdb', 'mikan', 'parser'];
const items: SettingItem<RssParser>[] = [
{
configKey: 'enable',
label: t('config.parserset.enable'),
label: () => t('config.parser_set.enable'),
type: 'switch',
},
{
configKey: 'type',
label: t('config.parserset.source'),
label: () => t('config.parser_set.source'),
type: 'select',
css: 'w-115px',
prop: {
@@ -35,7 +35,7 @@ const items: SettingItem<RssParser>[] = [
},
{
configKey: 'token',
label: t('config.parserset.token'),
label: () => t('config.parser_set.token'),
type: 'input',
prop: {
type: 'text',
@@ -44,7 +44,7 @@ const items: SettingItem<RssParser>[] = [
},
{
configKey: 'custom_url',
label: t('config.parserset.url'),
label: () => t('config.parser_set.url'),
type: 'input',
prop: {
type: 'text',
@@ -54,7 +54,7 @@ const items: SettingItem<RssParser>[] = [
},
{
configKey: 'language',
label: t('config.parserset.language'),
label: () => t('config.parser_set.language'),
type: 'select',
prop: {
items: langs,
@@ -62,7 +62,7 @@ const items: SettingItem<RssParser>[] = [
},
{
configKey: 'parser_type',
label: t('config.parserset.type'),
label: () => t('config.parser_set.type'),
type: 'select',
prop: {
items: parserMethods,
@@ -70,14 +70,14 @@ const items: SettingItem<RssParser>[] = [
},
{
configKey: 'filter',
label: t('config.parserset.exclude'),
label: () => t('config.parser_set.exclude'),
type: 'dynamic-tags',
},
];
</script>
<template>
<ab-fold-panel :title="$t('config.parserset.title')">
<ab-fold-panel :title="$t('config.parser_set.title')">
<div space-y-12px>
<ab-setting
v-for="i in items"

View File

@@ -3,19 +3,19 @@ const { types, type, url } = storeToRefs(usePlayerStore());
</script>
<template>
<ab-fold-panel :title="$t('config.mediaplayerset.title')">
<ab-fold-panel :title="$t('config.media_player_set.title')">
<div space-y-12px>
<ab-setting
v-model:data="type"
type="select"
:label="$t('config.mediaplayerset.type')"
:label="$t('config.media_player_set.type')"
:prop="{ items: types }"
></ab-setting>
<ab-setting
v-model:data="url"
type="input"
:label="$t('config.mediaplayerset.url')"
:label="$t('config.media_player_set.url')"
:prop="{ placeholder: 'media player url' }"
></ab-setting>
</div>

View File

@@ -1,9 +1,8 @@
<script lang="ts" setup>
import { useI18n } from 'vue-i18n';
import type { Proxy, ProxyType } from '#/config';
import type { SettingItem } from '#/components';
const { t } = useI18n({ useScope: 'global' });
const { t } = useMyI18n();
const { getSettingGroup } = useConfigStore();
const proxy = getSettingGroup('proxy');
@@ -12,12 +11,12 @@ const proxyType: ProxyType = ['http', 'https', 'socks5'];
const items: SettingItem<Proxy>[] = [
{
configKey: 'enable',
label: t('config.proxyset.enable'),
label: () => t('config.proxy_set.enable'),
type: 'switch',
},
{
configKey: 'type',
label: t('config.proxyset.type'),
label: () => t('config.proxy_set.type'),
type: 'select',
prop: {
items: proxyType,
@@ -26,7 +25,7 @@ const items: SettingItem<Proxy>[] = [
},
{
configKey: 'host',
label: t('config.proxyset.host'),
label: () => t('config.proxy_set.host'),
type: 'input',
prop: {
type: 'text',
@@ -35,7 +34,7 @@ const items: SettingItem<Proxy>[] = [
},
{
configKey: 'port',
label: t('config.proxyset.port'),
label: () => t('config.proxy_set.port'),
type: 'input',
prop: {
type: 'text',
@@ -44,7 +43,7 @@ const items: SettingItem<Proxy>[] = [
},
{
configKey: 'username',
label: t('config.proxyset.username'),
label: () => t('config.proxy_set.username'),
type: 'input',
prop: {
type: 'text',
@@ -53,7 +52,7 @@ const items: SettingItem<Proxy>[] = [
},
{
configKey: 'password',
label: t('config.proxyset.password'),
label: () => t('config.proxy_set.password'),
type: 'input',
prop: {
type: 'text',
@@ -64,7 +63,7 @@ const items: SettingItem<Proxy>[] = [
</script>
<template>
<ab-fold-panel :title="$t('config.proxyset.title')">
<ab-fold-panel :title="$t('config.proxy_set.title')">
<div space-y-12px>
<ab-setting
v-for="i in items"

View File

@@ -0,0 +1,37 @@
import { createI18n } from 'vue-i18n';
import enUS from '@/i18n/en.json';
import zhCN from '@/i18n/zh-CN.json';
const messages = {
en: enUS,
'zh-CN': zhCN,
};
export const useMyI18n = createSharedComposable(() => {
const lang = useLocalStorage('lang', navigator.language);
const i18n = createI18n({
legacy: false,
locale: lang.value,
fallbackLocale: 'en',
messages,
});
function changeLocale() {
if (lang.value === 'zh-CN') {
i18n.global.locale.value = 'en';
lang.value = 'en';
} else {
i18n.global.locale.value = 'zh-CN';
lang.value = 'zh-CN';
}
}
return {
lang,
i18n,
t: i18n.global.t,
locale: i18n.global.locale,
changeLocale,
};
});

View File

@@ -3,7 +3,7 @@
"title": "Login",
"username": "Username",
"password": "Password",
"loginbtn": "Login",
"login_btn": "Login",
"default": "Default"
},
"sidebar": {
@@ -21,25 +21,24 @@
"pause": "Pause",
"restart": "Restart",
"shutdown": "Shutdown",
"resetrule": "Reset Rule",
"reset_rule": "Reset Rule",
"profile": {
"title": "Profile",
"poptitle": "Change Account",
"pop_title": "Change Account",
"username": "Username",
"password": "Password",
"updatebtn": "Update"
"update_btn": "Update"
},
"add": {
"title": "Add Bangumi",
"rsslink": "RSS Link",
"rss_link": "RSS Link",
"placeholder": "Please enter the RSS link",
"analyse": "Analyse"
}
},
"homepage": {
"title": "Bangumi List",
"rule": {
"officaltitle": "Official Title",
"offical_title": "Official Title",
"year": "Year",
"season": "Season",
"offset": "Offset",
@@ -48,37 +47,35 @@
"disable": "Disable",
"delete": "Delete",
"apply": "Apply",
"yesbtn": "Yes",
"nobtn": "No",
"enablehit": "Do you want to enable this rule?",
"deletehit": "Delete Local File?",
"enablerule": "Enable Rule",
"editrule": "Edit Rule"
"yes_btn": "Yes",
"no_btn": "No",
"enable_hit": "Do you want to enable this rule?",
"delete_hit": "Delete Local File?",
"enable_rule": "Enable Rule",
"edit_rule": "Edit Rule"
}
},
"player": {
"title": "Player",
"hit": "Please set up the media player"
},
"log": {
"title": "Log",
"reset": "Reset",
"copy": "Copy",
"contactinfo": "Contact Infomation",
"contact_info": "Contact Infomation",
"go": "Go",
"join": "Join",
"bugrepo": "Bug Report"
"bug_repo": "Bug Report"
},
"config": {
"title": "Config",
"normalset": {
"normal_set": {
"title": "Normal Setting",
"rssintvl": "Interval Time of Rss",
"renameintvl": "Interval Time of Rename",
"webport": "WebUI Port",
"rss_interval": "Interval Time of Rss",
"rename_interval": "Interval Time of Rename",
"web_port": "WebUI Port",
"debug": "Debug"
},
"parserset": {
"parser_set": {
"title": "Parser Setting",
"enable": "Enable",
"source": "Source",
@@ -88,7 +85,7 @@
"type": "Parser Type",
"exclude": "Exclude"
},
"downloaderset": {
"downloader_set": {
"title": "Downloader Setting",
"type": "Downloader Type",
"host": "Host",
@@ -97,22 +94,22 @@
"path": "Download Path",
"ssl": "SSL"
},
"manageset": {
"manage_set": {
"title": "Manage Setting",
"enable": "Enable",
"method": "Rename Method",
"eps": "EPS complete",
"grouptag": "Add Group Tag",
"deletebadtorr": "Delete Bad Torrent"
"group_tag": "Add Group Tag",
"delete_bad_torrent": "Delete Bad Torrent"
},
"notificationset": {
"notification_set": {
"title": "Notification Setting",
"enable": "Enable",
"type": "Type",
"token": "Token",
"chatid": "Chat ID"
"chat_id": "Chat ID"
},
"proxyset": {
"proxy_set": {
"title": "Proxy Setting",
"enable": "Enable",
"type": "Proxy Type",
@@ -121,7 +118,7 @@
"username": "Username",
"password": "Password"
},
"mediaplayerset": {
"media_player_set": {
"title": "Media Player Setting",
"type": "type",
"url": "url"

View File

@@ -3,8 +3,8 @@
"title": "登录",
"username": "用户名",
"password": "密码",
"loginbtn": "登录",
"default": "默认账号密码 "
"login_btn": "登录",
"default": "默认账号密码"
},
"sidebar": {
"title": "菜单",
@@ -13,7 +13,7 @@
"log": "日志",
"config": "设置",
"logout": "退出",
"calendar": "日历",
"calendar": "番剧日历",
"downloader": "下载器"
},
"topbar": {
@@ -21,64 +21,61 @@
"pause": "暂停",
"restart": "重启",
"shutdown": "关闭",
"resetrule": "重置规则",
"reset_rule": "重置规则",
"profile": {
"title": "账户资料",
"poptitle": "修改账户信息",
"title": "账户设置",
"pop_title": "修改账户",
"username": "用户名",
"password": "密码",
"updatebtn": "更新"
"update_btn": "更新"
},
"add": {
"title": "添加番剧",
"rsslink": "RSS链接",
"rss_link": "RSS链接",
"placeholder": "请输入RSS链接",
"analyse": "分析"
}
},
"homepage": {
"title": "番剧列表",
"rule": {
"officaltitle": "官方名称",
"offical_title": "官方名称",
"year": "年份",
"season": "季度",
"offset": "偏移",
"offset": "剧集偏移",
"exclude": "排除",
"enable": "启用",
"disable": "禁用",
"delete": "删除",
"apply": "应用",
"yesbtn": "是",
"nobtn": "否",
"enablehit": "确定要让规则生效吗?",
"deletehit": "删除本地文件",
"enablerule": "启用规则",
"editrule": "编辑规则"
"yes_btn": "是",
"no_btn": "否",
"enable_hit": "确定启用该规则?",
"delete_hit": "是否删除本地文件?",
"enable_rule": "启用规则",
"edit_rule": "编辑规则"
}
},
"player": {
"title": "播放器",
"hit": "请设置播放器地址"
"hit": "请设置媒体播放器地址"
},
"log": {
"title": "日志",
"reset": "重置",
"copy": "复制",
"contactinfo": "联系方式",
"contact_info": "联系方式",
"go": "访问",
"join": "加入",
"bugrepo": "Bug反馈"
"bug_repo": "Bug 反馈"
},
"config": {
"title": "系统设置",
"normalset": {
"title": "一般设置",
"rssintvl": "RSS间隔",
"renameintvl": "重命名间隔",
"webport": "网页端口",
"normal_set": {
"title": "常规设置",
"rss_interval": "RSS 间隔",
"rename_interval": "重命名间隔",
"web_port": "网页端口",
"debug": "调试"
},
"parserset": {
"parser_set": {
"title": "解析设置",
"enable": "启用",
"source": "数据源",
@@ -88,7 +85,7 @@
"type": "解析类型",
"exclude": "排除"
},
"downloaderset": {
"downloader_set": {
"title": "下载设置",
"type": "下载器类型",
"host": "下载器地址",
@@ -97,22 +94,22 @@
"path": "下载地址",
"ssl": "SSL"
},
"manageset": {
"manage_set": {
"title": "番剧管理设置",
"enable": "启用",
"method": "重命名方式",
"eps": "EPS完成",
"grouptag": "添加组标签",
"deletebadtorr": "删除坏种"
"eps": "番剧补全",
"group_tag": "添加组标签",
"delete_bad_torrent": "删除坏种"
},
"notificationset": {
"title": "推送设置",
"notification_set": {
"title": "通知设置",
"enable": "启用",
"type": "类型",
"token": "Token",
"chatid": "Chat ID"
"chat_id": "Chat ID"
},
"proxyset": {
"proxy_set": {
"title": "代理设置",
"enable": "启用",
"type": "类型",
@@ -121,7 +118,7 @@
"username": "用户名",
"password": "密码"
},
"mediaplayerset": {
"media_player_set": {
"title": "播放器设置",
"type": "类型",
"url": "播放器地址"

View File

@@ -1,28 +0,0 @@
import { createI18n } from 'vue-i18n';
import enUS from './lang/en-US.json';
import zhCN from './lang/zh-CN.json';
const messages = {
'en-US': enUS,
'zh-CN': zhCN,
};
// Default language is the same as last setting (undefined is browser language)
let lang = localStorage.getItem('lang');
if (lang === null) {
const navLang = navigator.language;
const localLang = navLang || false;
lang = localLang || 'en-US';
}
localStorage.setItem('lang', lang);
const i18n = createI18n({
legacy: false,
locale: lang,
globalInjection: true,
silentTranslationWarn: true,
globalInstall: true,
messages,
});
export default i18n;

View File

@@ -1,13 +1,13 @@
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import { router } from './router';
import i18n from './locales';
import App from './App.vue';
import '@unocss/reset/tailwind-compat.css';
import 'virtual:uno.css';
const pinia = createPinia();
const { i18n } = useMyI18n();
const app = createApp(App);
app.use(router);

View File

@@ -13,6 +13,8 @@ definePage({
<ab-sidebar />
<div layout-content>
<ab-page-title :title="$route.name"></ab-page-title>
<RouterView v-slot="{ Component }">
<KeepAlive>
<component :is="Component" />

View File

@@ -93,7 +93,6 @@ definePage({
</script>
<template>
<ab-page-title :title="$t('homepage.title')"></ab-page-title>
<div overflow-auto mt-12px flex-grow>
<div flex="~ wrap" gap-y-12px gap-x-50px>
<ab-bangumi-card

View File

@@ -9,7 +9,6 @@ definePage({
</script>
<template>
<ab-page-title :title="$t('config.title')"></ab-page-title>
<div overflow-auto mt-12px flex-grow>
<div h-full flex="~ col">
<div grid="~ cols-2" gap-20px mb-auto>

View File

@@ -16,7 +16,6 @@ definePage({
</script>
<template>
<ab-page-title :title="$t('log.title')"></ab-page-title>
<div overflow-auto mt-12px flex-grow>
<div flex="~ wrap" gap-12px>
<ab-container :title="$t('log.title')" w-660px grow>
@@ -39,7 +38,7 @@ definePage({
</ab-container>
<div grow w-500px space-y-20px>
<ab-container :title="$t('log.contactinfo')">
<ab-container :title="$t('log.contact_info')">
<div space-y-12px>
<ab-label label="Github">
<ab-button
@@ -85,7 +84,7 @@ definePage({
</div>
</ab-container>
<ab-container :title="$t('log.bugrepo')">
<ab-container :title="$t('log.bug_repo')">
<div space-y-12px>
<ab-button
mx-auto

View File

@@ -7,7 +7,6 @@ const { url } = storeToRefs(usePlayerStore());
</script>
<template>
<ab-page-title :title="$t('player.title')"></ab-page-title>
<div overflow-auto mt-12px flex-grow>
<template v-if="url === ''">
<div wh-full f-cer text-h1 text-primary>

View File

@@ -33,7 +33,7 @@ definePage({
<div flex="~ justify-end">
<ab-button size="small" @click="login">{{
$t('login.loginbtn')
$t('login.login_btn')
}}</ab-button>
</div>
</div>

View File

@@ -35,7 +35,14 @@ export const useConfigStore = defineStore('config', () => {
};
function getSettingGroup<Tkey extends keyof Config>(key: Tkey) {
return computed<Config[Tkey]>(() => config.value[key]);
return computed<Config[Tkey]>({
get() {
return config.value[key];
},
set(newVal) {
config.value[key] = newVal;
},
});
}
return {

View File

@@ -6,7 +6,7 @@ export interface SelectItem {
}
export interface AbSettingProps {
label: string;
label: string | (() => string);
type: 'input' | 'switch' | 'select' | 'dynamic-tags';
css?: string;
prop?: any;

View File

@@ -190,6 +190,7 @@ declare global {
const useFullscreen: typeof import('@vueuse/core')['useFullscreen']
const useGamepad: typeof import('@vueuse/core')['useGamepad']
const useGeolocation: typeof import('@vueuse/core')['useGeolocation']
const useI18n: typeof import('vue-i18n')['useI18n']
const useIdle: typeof import('@vueuse/core')['useIdle']
const useImage: typeof import('@vueuse/core')['useImage']
const useInfiniteScroll: typeof import('@vueuse/core')['useInfiniteScroll']
@@ -212,6 +213,7 @@ declare global {
const useMouseInElement: typeof import('@vueuse/core')['useMouseInElement']
const useMousePressed: typeof import('@vueuse/core')['useMousePressed']
const useMutationObserver: typeof import('@vueuse/core')['useMutationObserver']
const useMyI18n: typeof import('../../src/hooks/useMyI18n')['useMyI18n']
const useNavigatorLanguage: typeof import('@vueuse/core')['useNavigatorLanguage']
const useNetwork: typeof import('@vueuse/core')['useNetwork']
const useNow: typeof import('@vueuse/core')['useNow']

View File

@@ -6,6 +6,7 @@ import AutoImport from 'unplugin-auto-import/vite';
import Components from 'unplugin-vue-components/vite';
import VueRouter from 'unplugin-vue-router/vite';
import { VueRouterAutoImports } from 'unplugin-vue-router';
import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite';
// https://vitejs.dev/config/
export default defineConfig({
@@ -21,7 +22,14 @@ export default defineConfig({
}),
UnoCSS(),
AutoImport({
imports: ['vue', 'vitest', 'pinia', '@vueuse/core', VueRouterAutoImports],
imports: [
'vue',
'vitest',
'pinia',
'@vueuse/core',
VueRouterAutoImports,
'vue-i18n',
],
dts: 'types/dts/auto-imports.d.ts',
dirs: ['src/api', 'src/store', 'src/hooks', 'src/utils'],
}),
@@ -34,6 +42,9 @@ export default defineConfig({
'src/components/setting',
],
}),
VueI18nPlugin({
include: resolve(__dirname, './src/i18n/**'),
}),
],
css: {
preprocessorOptions: {