mirror of
https://github.com/setube/ogame-vue-ts.git
synced 2026-05-12 16:05:12 +08:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1ad051cd6d | ||
|
|
fda15646eb | ||
|
|
6a9846c6df | ||
|
|
49753566c3 | ||
|
|
66783f896c | ||
|
|
7cc885c62a | ||
|
|
5c6404d86a | ||
|
|
010ea137ac | ||
|
|
6dbca76252 | ||
|
|
c047ffb88e | ||
|
|
6f8adfa586 | ||
|
|
94fa2ad57a | ||
|
|
7ed508945a | ||
|
|
fe2e5bfad9 | ||
|
|
7f36b6693f | ||
|
|
27d60ae71a | ||
|
|
ca1aed1e9b | ||
|
|
04ee72a33d | ||
|
|
d95dffcfcd | ||
|
|
b6bcae3294 | ||
|
|
ebed10b82f | ||
|
|
f4f5a719f5 | ||
|
|
1686622013 |
23
.github/workflows/build.yml
vendored
23
.github/workflows/build.yml
vendored
@@ -72,16 +72,11 @@ jobs:
|
|||||||
- name: Setup Android SDK
|
- name: Setup Android SDK
|
||||||
uses: android-actions/setup-android@v3
|
uses: android-actions/setup-android@v3
|
||||||
|
|
||||||
# 缓存 Gradle 依赖 (可节省 3-5 分钟)
|
# 使用官方 Gradle Action(内置智能缓存)
|
||||||
- name: Cache Gradle
|
- name: Setup Gradle
|
||||||
uses: actions/cache@v4
|
uses: gradle/actions/setup-gradle@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
cache-read-only: false
|
||||||
~/.gradle/caches
|
|
||||||
~/.gradle/wrapper
|
|
||||||
key: gradle-${{ runner.os }}-${{ hashFiles('android/**/*.gradle*', 'android/**/gradle-wrapper.properties') }}
|
|
||||||
restore-keys: |
|
|
||||||
gradle-${{ runner.os }}-
|
|
||||||
|
|
||||||
- name: Build Frontend
|
- name: Build Frontend
|
||||||
run: |
|
run: |
|
||||||
@@ -118,13 +113,13 @@ jobs:
|
|||||||
working-directory: android
|
working-directory: android
|
||||||
run: |
|
run: |
|
||||||
chmod +x ./gradlew
|
chmod +x ./gradlew
|
||||||
./gradlew assembleRelease --no-daemon
|
./gradlew assembleRelease --build-cache
|
||||||
|
|
||||||
- name: Upload APK Artifacts
|
- name: Upload APK Artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: android-apk
|
name: android-apk
|
||||||
path: android/app/build/outputs/apk/release/*.APK
|
path: android/app/build/outputs/apk/release/*.apk
|
||||||
|
|
||||||
# 3. 构建 Electron 客户端
|
# 3. 构建 Electron 客户端
|
||||||
build-electron:
|
build-electron:
|
||||||
@@ -180,7 +175,7 @@ jobs:
|
|||||||
|
|
||||||
# 4. 发布 Release
|
# 4. 发布 Release
|
||||||
release:
|
release:
|
||||||
needs: [ build-server, build-android, build-electron ]
|
needs: [build-server, build-android, build-electron]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
@@ -211,7 +206,7 @@ jobs:
|
|||||||
find ./raw-assets/electron-* -type f \( -name "*.exe" -o -name "*.dmg" -o -name "*.AppImage" -o -name "*.zip" \) -exec cp {} ./final-release/ \;
|
find ./raw-assets/electron-* -type f \( -name "*.exe" -o -name "*.dmg" -o -name "*.AppImage" -o -name "*.zip" \) -exec cp {} ./final-release/ \;
|
||||||
|
|
||||||
# 移动 Android APK
|
# 移动 Android APK
|
||||||
find ./raw-assets/android-apk -type f -name "*.APK" -exec cp {} ./final-release/ \; || true
|
find ./raw-assets/android-apk -type f -name "*.apk" -exec cp {} ./final-release/ \; || true
|
||||||
|
|
||||||
# 检查结果
|
# 检查结果
|
||||||
echo "Final assets to upload:"
|
echo "Final assets to upload:"
|
||||||
@@ -222,7 +217,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
VERSION=${{ steps.get_version.outputs.VERSION }}
|
VERSION=${{ steps.get_version.outputs.VERSION }}
|
||||||
# 获取 release 中的现有 assets 并删除 APK 文件
|
# 获取 release 中的现有 assets 并删除 APK 文件
|
||||||
gh release view "$VERSION" --json assets -q '.assets[].name' 2>/dev/null | grep -i '\.APK$' | while read asset; do
|
gh release view "$VERSION" --json assets -q '.assets[].name' 2>/dev/null | grep -i '\.apk$' | while read asset; do
|
||||||
echo "Deleting existing asset: $asset"
|
echo "Deleting existing asset: $asset"
|
||||||
gh release delete-asset "$VERSION" "$asset" -y || true
|
gh release delete-asset "$VERSION" "$asset" -y || true
|
||||||
done
|
done
|
||||||
|
|||||||
2
.github/workflows/github-pages.yml
vendored
2
.github/workflows/github-pages.yml
vendored
@@ -2,7 +2,7 @@ name: 构建 Github Pages
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ main ] # 如果你的主分支叫 master,请改为 master
|
branches: [main] # 如果你的主分支叫 master,请改为 master
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|||||||
6
.github/workflows/ogame-vue-ts.yml
vendored
6
.github/workflows/ogame-vue-ts.yml
vendored
@@ -2,8 +2,8 @@ name: Docker 多架构构建并发布
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ main ]
|
branches: [main]
|
||||||
tags: [ 'v*.*.*' ] # 打 tag 时也触发
|
tags: ['v*.*.*'] # 打 tag 时也触发
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
@@ -37,7 +37,7 @@ jobs:
|
|||||||
|
|
||||||
# 登录 Docker Hub(只在用户名存在时执行)
|
# 登录 Docker Hub(只在用户名存在时执行)
|
||||||
- name: 登录 Docker Hub
|
- name: 登录 Docker Hub
|
||||||
if: vars.DOCKERHUB_USERNAME != '' # 只检查 vars,忽略 secrets
|
if: vars.DOCKERHUB_USERNAME != '' # 只检查 vars,忽略 secrets
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
username: ${{ vars.DOCKERHUB_USERNAME }}
|
username: ${{ vars.DOCKERHUB_USERNAME }}
|
||||||
|
|||||||
@@ -1,5 +1,15 @@
|
|||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
|
// 从 package.json 读取版本号
|
||||||
|
def packageJsonFile = file('../../package.json')
|
||||||
|
def packageJsonText = packageJsonFile.text
|
||||||
|
// 使用正则提取版本号
|
||||||
|
def versionMatcher = packageJsonText =~ /"version"\s*:\s*"([^"]+)"/
|
||||||
|
def appVersionName = versionMatcher ? versionMatcher[0][1] : "1.0.0"
|
||||||
|
// 将版本号转换为 versionCode,例如 "1.5.5" -> 1*10000 + 5*100 + 5 = 10505
|
||||||
|
def versionParts = appVersionName.split('\\.')
|
||||||
|
def appVersionCode = versionParts[0].toInteger() * 10000 + versionParts[1].toInteger() * 100 + versionParts[2].toInteger()
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "games.wenzi.ogame"
|
namespace = "games.wenzi.ogame"
|
||||||
compileSdk = rootProject.ext.compileSdkVersion
|
compileSdk = rootProject.ext.compileSdkVersion
|
||||||
@@ -7,8 +17,8 @@ android {
|
|||||||
applicationId "games.wenzi.ogame"
|
applicationId "games.wenzi.ogame"
|
||||||
minSdkVersion rootProject.ext.minSdkVersion
|
minSdkVersion rootProject.ext.minSdkVersion
|
||||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
versionCode 15
|
versionCode appVersionCode
|
||||||
versionName "1.5.0"
|
versionName appVersionName
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
aaptOptions {
|
aaptOptions {
|
||||||
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
||||||
@@ -56,7 +66,7 @@ android {
|
|||||||
def abi = output.getFilter(com.android.build.OutputFile.ABI)
|
def abi = output.getFilter(com.android.build.OutputFile.ABI)
|
||||||
if (abi != null) {
|
if (abi != null) {
|
||||||
output.versionCodeOverride = abiVersionCode[abi] * 1000 + defaultConfig.versionCode
|
output.versionCodeOverride = abiVersionCode[abi] * 1000 + defaultConfig.versionCode
|
||||||
output.outputFileName = "OGame-Vue-Ts-${abi}.APK"
|
output.outputFileName = "OGame-Vue-Ts-${abi}.apk"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ android {
|
|||||||
|
|
||||||
apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
|
apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation project(':capacitor-app')
|
||||||
|
implementation project(':capacitor-filesystem')
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,45 +1,46 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:usesCleartextTraffic="true"
|
android:usesCleartextTraffic="true"
|
||||||
android:networkSecurityConfig="@xml/network_security_config"
|
android:hardwareAccelerated="true"
|
||||||
android:theme="@style/AppTheme">
|
android:networkSecurityConfig="@xml/network_security_config"
|
||||||
|
android:theme="@style/AppTheme">
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode|navigation|density"
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode|navigation|density"
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:label="@string/title_activity_main"
|
android:label="@string/title_activity_main"
|
||||||
android:theme="@style/AppTheme.NoActionBarLaunch"
|
android:theme="@style/AppTheme.NoActionBarLaunch"
|
||||||
android:launchMode="singleTask"
|
android:launchMode="singleTask"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name="androidx.core.content.FileProvider"
|
android:name="androidx.core.content.FileProvider"
|
||||||
android:authorities="${applicationId}.fileprovider"
|
android:authorities="${applicationId}.fileprovider"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:grantUriPermissions="true">
|
android:grantUriPermissions="true">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
android:resource="@xml/file_paths"></meta-data>
|
android:resource="@xml/file_paths"></meta-data>
|
||||||
</provider>
|
</provider>
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
<!-- Permissions -->
|
<!-- Permissions -->
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
</manifest>
|
</manifest>
|
||||||
@@ -1,18 +1,100 @@
|
|||||||
package games.wenzi.ogame;
|
package games.wenzi.ogame;
|
||||||
|
|
||||||
|
import android.animation.Animator;
|
||||||
|
import android.animation.AnimatorListenerAdapter;
|
||||||
|
import android.animation.ObjectAnimator;
|
||||||
|
import android.graphics.Color;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
import androidx.core.content.ContextCompat;
|
import android.view.animation.AccelerateDecelerateInterpolator;
|
||||||
|
import android.webkit.WebChromeClient;
|
||||||
|
import android.webkit.WebSettings;
|
||||||
|
import android.webkit.WebView;
|
||||||
|
import androidx.core.splashscreen.SplashScreen;
|
||||||
|
import androidx.core.view.WindowCompat;
|
||||||
|
import androidx.core.view.WindowInsetsControllerCompat;
|
||||||
import com.getcapacitor.BridgeActivity;
|
import com.getcapacitor.BridgeActivity;
|
||||||
|
|
||||||
public class MainActivity extends BridgeActivity {
|
public class MainActivity extends BridgeActivity {
|
||||||
|
private boolean isWebViewReady = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
// 安装 SplashScreen,必须在 super.onCreate 之前调用
|
||||||
|
SplashScreen splashScreen = SplashScreen.installSplashScreen(this);
|
||||||
|
|
||||||
|
// 保持 SplashScreen 直到 WebView 加载完成
|
||||||
|
splashScreen.setKeepOnScreenCondition(() -> !isWebViewReady);
|
||||||
|
|
||||||
|
// 设置淡出退出动画
|
||||||
|
splashScreen.setOnExitAnimationListener(splashScreenView -> {
|
||||||
|
// 创建淡出动画
|
||||||
|
ObjectAnimator fadeOut = ObjectAnimator.ofFloat(
|
||||||
|
splashScreenView.getView(),
|
||||||
|
View.ALPHA,
|
||||||
|
1f,
|
||||||
|
0f
|
||||||
|
);
|
||||||
|
fadeOut.setInterpolator(new AccelerateDecelerateInterpolator());
|
||||||
|
fadeOut.setDuration(300);
|
||||||
|
|
||||||
|
// 动画结束后移除 SplashScreen
|
||||||
|
fadeOut.addListener(new AnimatorListenerAdapter() {
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
splashScreenView.remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
fadeOut.start();
|
||||||
|
});
|
||||||
|
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
// 设置状态栏颜色,防止 Capacitor 强制透明
|
|
||||||
Window window = getWindow();
|
Window window = getWindow();
|
||||||
window.setStatusBarColor(ContextCompat.getColor(this, R.color.status_bar_color));
|
|
||||||
window.setNavigationBarColor(ContextCompat.getColor(this, R.color.status_bar_color));
|
// 启用边到边显示(Edge-to-Edge)
|
||||||
|
WindowCompat.setDecorFitsSystemWindows(window, false);
|
||||||
|
|
||||||
|
// 设置透明状态栏和导航栏
|
||||||
|
window.setStatusBarColor(Color.TRANSPARENT);
|
||||||
|
window.setNavigationBarColor(Color.TRANSPARENT);
|
||||||
|
|
||||||
|
// 设置状态栏图标为浅色(因为背景是深色)
|
||||||
|
WindowInsetsControllerCompat controller = WindowCompat.getInsetsController(window, window.getDecorView());
|
||||||
|
if (controller != null) {
|
||||||
|
controller.setAppearanceLightStatusBars(false);
|
||||||
|
controller.setAppearanceLightNavigationBars(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
WebView webView = getBridge().getWebView();
|
||||||
|
if (webView != null) {
|
||||||
|
WebSettings settings = webView.getSettings();
|
||||||
|
// 禁用 WebView 文本缩放,防止系统字体大小设置影响布局
|
||||||
|
settings.setTextZoom(100);
|
||||||
|
// 优化 WebView 性能
|
||||||
|
settings.setCacheMode(WebSettings.LOAD_DEFAULT);
|
||||||
|
settings.setDomStorageEnabled(true);
|
||||||
|
settings.setDatabaseEnabled(true);
|
||||||
|
// 启用硬件加速渲染
|
||||||
|
webView.setLayerType(android.view.View.LAYER_TYPE_HARDWARE, null);
|
||||||
|
|
||||||
|
// 监听页面加载进度,加载完成后隐藏 SplashScreen
|
||||||
|
webView.setWebChromeClient(new WebChromeClient() {
|
||||||
|
@Override
|
||||||
|
public void onProgressChanged(WebView view, int newProgress) {
|
||||||
|
super.onProgressChanged(view, newProgress);
|
||||||
|
// 当页面加载达到 80% 时认为可以显示
|
||||||
|
if (newProgress >= 80) {
|
||||||
|
isWebViewReady = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
9
android/app/src/main/res/drawable/splash_icon.xml
Normal file
9
android/app/src/main/res/drawable/splash_icon.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- 启动画面专用图标,使用较小的尺寸避免模糊 -->
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item
|
||||||
|
android:width="96dp"
|
||||||
|
android:height="96dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:drawable="@mipmap/ic_launcher_foreground" />
|
||||||
|
</layer-list>
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<color name="status_bar_color">#1a1a2e</color>
|
<color name="status_bar_color">#1a1a2e</color>
|
||||||
|
<color name="splash_background">#0f0f1a</color>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<color name="ic_launcher_background">#FFFFFF</color>
|
<color name="ic_launcher_background">#FFFFFF</color>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">OGame Vue Ts</string>
|
<string name="app_name">OGame Vue Ts</string>
|
||||||
<string name="title_activity_main">OGame Vue Ts</string>
|
<string name="title_activity_main">OGame Vue Ts</string>
|
||||||
<string name="package_name">games.wenzi.ogame</string>
|
<string name="package_name">games.wenzi.ogame</string>
|
||||||
<string name="custom_url_scheme">games.wenzi.ogame</string>
|
<string name="custom_url_scheme">games.wenzi.ogame</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -1,24 +1,33 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
|
|
||||||
<!-- Base application theme. -->
|
<!-- Base application theme. -->
|
||||||
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||||
<!-- Customize your theme here. -->
|
<!-- Customize your theme here. -->
|
||||||
<item name="colorPrimary">#1a1a2e</item>
|
<item name="colorPrimary">#1a1a2e</item>
|
||||||
<item name="colorPrimaryDark">#0f0f1a</item>
|
<item name="colorPrimaryDark">#0f0f1a</item>
|
||||||
<item name="colorAccent">#6366f1</item>
|
<item name="colorAccent">#6366f1</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="AppTheme.NoActionBar" parent="Theme.AppCompat.DayNight.NoActionBar">
|
<style name="AppTheme.NoActionBar" parent="Theme.AppCompat.DayNight.NoActionBar">
|
||||||
<item name="windowActionBar">false</item>
|
<item name="windowActionBar">false</item>
|
||||||
<item name="windowNoTitle">true</item>
|
<item name="windowNoTitle">true</item>
|
||||||
<item name="android:background">@null</item>
|
<item name="android:background">@null</item>
|
||||||
<item name="android:statusBarColor">#1a1a2e</item>
|
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||||
<item name="android:navigationBarColor">#1a1a2e</item>
|
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||||
</style>
|
<item name="android:windowTranslucentStatus">false</item>
|
||||||
|
<item name="android:windowTranslucentNavigation">false</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<!-- 启动画面主题 - 延长显示直到 WebView 加载完成 -->
|
||||||
<style name="AppTheme.NoActionBarLaunch" parent="Theme.SplashScreen">
|
<style name="AppTheme.NoActionBarLaunch" parent="Theme.SplashScreen">
|
||||||
<item name="android:background">@drawable/splash</item>
|
<item name="windowSplashScreenBackground">@color/splash_background</item>
|
||||||
</style>
|
<!-- 使用较小的自定义图标避免模糊 -->
|
||||||
|
<item name="windowSplashScreenAnimatedIcon">@drawable/splash_icon</item>
|
||||||
|
<!-- 图标大小限制 -->
|
||||||
|
<item name="windowSplashScreenIconBackgroundColor">@color/splash_background</item>
|
||||||
|
<!-- 退出动画时长 -->
|
||||||
|
<item name="android:windowSplashScreenAnimationDuration">500</item>
|
||||||
|
<item name="postSplashScreenTheme">@style/AppTheme.NoActionBar</item>
|
||||||
|
</style>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<external-path name="my_images" path="." />
|
<external-path name="my_images" path="." />
|
||||||
<cache-path name="my_cache_images" path="." />
|
<cache-path name="my_cache_images" path="." />
|
||||||
</paths>
|
</paths>
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<network-security-config>
|
<network-security-config>
|
||||||
<base-config cleartextTrafficPermitted="true">
|
<base-config cleartextTrafficPermitted="true">
|
||||||
<trust-anchors>
|
<trust-anchors>
|
||||||
<certificates src="system" />
|
<certificates src="system" />
|
||||||
</trust-anchors>
|
</trust-anchors>
|
||||||
</base-config>
|
</base-config>
|
||||||
</network-security-config>
|
</network-security-config>
|
||||||
@@ -1,3 +1,9 @@
|
|||||||
// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN
|
// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN
|
||||||
include ':capacitor-android'
|
include ':capacitor-android'
|
||||||
project(':capacitor-android').projectDir = new File('../node_modules/.pnpm/@capacitor+android@8.0.0_@capacitor+core@8.0.0/node_modules/@capacitor/android/capacitor')
|
project(':capacitor-android').projectDir = new File('../node_modules/.pnpm/@capacitor+android@8.0.0_@capacitor+core@8.0.0/node_modules/@capacitor/android/capacitor')
|
||||||
|
|
||||||
|
include ':capacitor-app'
|
||||||
|
project(':capacitor-app').projectDir = new File('../node_modules/.pnpm/@capacitor+app@8.0.0_@capacitor+core@8.0.0/node_modules/@capacitor/app/android')
|
||||||
|
|
||||||
|
include ':capacitor-filesystem'
|
||||||
|
project(':capacitor-filesystem').projectDir = new File('../node_modules/.pnpm/@capacitor+filesystem@8.0.0_@capacitor+core@8.0.0/node_modules/@capacitor/filesystem/android')
|
||||||
|
|||||||
@@ -5,12 +5,22 @@ const config: CapacitorConfig = {
|
|||||||
appName: 'OGame Vue Ts',
|
appName: 'OGame Vue Ts',
|
||||||
webDir: 'docs',
|
webDir: 'docs',
|
||||||
server: {
|
server: {
|
||||||
androidScheme: 'https'
|
androidScheme: 'https',
|
||||||
|
cacheControl: 'max-age=31536000'
|
||||||
},
|
},
|
||||||
android: {
|
android: {
|
||||||
buildOptions: {
|
buildOptions: {
|
||||||
keystorePath: undefined,
|
keystorePath: undefined,
|
||||||
keystoreAlias: undefined
|
keystoreAlias: undefined
|
||||||
|
},
|
||||||
|
webContentsDebuggingEnabled: false,
|
||||||
|
allowMixedContent: false,
|
||||||
|
hardwareAcceleration: true
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
// 禁用键盘自动调整视口
|
||||||
|
Keyboard: {
|
||||||
|
resize: 'none'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1" />
|
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
|
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
|
||||||
|
<link rel="apple-touch-icon" href="/logo.svg" />
|
||||||
<title>OGame-Vue-Ts</title>
|
<title>OGame-Vue-Ts</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
@@ -13,8 +14,9 @@
|
|||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<script type="module" src="/src/main.ts"></script>
|
<script type="module" src="/src/main.ts"></script>
|
||||||
<!-- 统计勿删 -->
|
<!-- 统计勿删 -->
|
||||||
<script charset="UTF-8" id="LA_COLLECT" src="https://sdk.51.la/js-sdk-pro.min.js"></script>
|
<script>
|
||||||
<script>LA.init({ id: "L298GYqn6JhAO0VU", ck: "L298GYqn6JhAO0VU", autoTrack: true, hashMode: true })</script>
|
!function (p) { "use strict"; !function (t) { var s = window, e = document, i = p, c = "".concat("https:" === e.location.protocol ? "https://" : "http://", "sdk.51.la/js-sdk-pro.min.js"), n = e.createElement("script"), r = e.getElementsByTagName("script")[0]; n.type = "text/javascript", n.setAttribute("charset", "UTF-8"), n.async = !0, n.src = c, n.id = "LA_COLLECT", i.d = n; var o = function () { s.LA.ids.push(i) }; s.LA ? s.LA.ids && o() : (s.LA = p, s.LA.ids = [], o()), r.parentNode.insertBefore(n, r) }() }({ id: "L298GYqn6JhAO0VU", ck: "L298GYqn6JhAO0VU", autoTrack: true, hashMode: true });
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@@ -8,8 +8,8 @@
|
|||||||
"email": "1962257451@qq.com"
|
"email": "1962257451@qq.com"
|
||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.5.5",
|
"version": "1.5.6",
|
||||||
"buildDate": "2025/12/25 18:23:43",
|
"buildDate": "2025/12/27 03:58:44",
|
||||||
"main": "dist-electron/main.js",
|
"main": "dist-electron/main.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -44,7 +44,8 @@
|
|||||||
"tailwindcss": "^4.1.17",
|
"tailwindcss": "^4.1.17",
|
||||||
"vue": "^3.5.24",
|
"vue": "^3.5.24",
|
||||||
"vue-router": "4",
|
"vue-router": "4",
|
||||||
"vue-sonner": "^2.0.9"
|
"vue-sonner": "^2.0.9",
|
||||||
|
"lightningcss": "^1.30.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/crypto-js": "^4.2.2",
|
"@types/crypto-js": "^4.2.2",
|
||||||
@@ -52,7 +53,6 @@
|
|||||||
"@types/node": "^24.10.2",
|
"@types/node": "^24.10.2",
|
||||||
"@vitejs/plugin-vue": "^6.0.1",
|
"@vitejs/plugin-vue": "^6.0.1",
|
||||||
"@vue/tsconfig": "^0.8.1",
|
"@vue/tsconfig": "^0.8.1",
|
||||||
"autoprefixer": "^10.4.23",
|
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
|
|||||||
32
pnpm-lock.yaml
generated
32
pnpm-lock.yaml
generated
@@ -44,6 +44,9 @@ importers:
|
|||||||
finalhandler:
|
finalhandler:
|
||||||
specifier: ^2.1.1
|
specifier: ^2.1.1
|
||||||
version: 2.1.1
|
version: 2.1.1
|
||||||
|
lightningcss:
|
||||||
|
specifier: ^1.30.2
|
||||||
|
version: 1.30.2
|
||||||
lucide-vue-next:
|
lucide-vue-next:
|
||||||
specifier: ^0.556.0
|
specifier: ^0.556.0
|
||||||
version: 0.556.0(vue@3.5.25(typescript@5.9.3))
|
version: 0.556.0(vue@3.5.25(typescript@5.9.3))
|
||||||
@@ -93,9 +96,6 @@ importers:
|
|||||||
'@vue/tsconfig':
|
'@vue/tsconfig':
|
||||||
specifier: ^0.8.1
|
specifier: ^0.8.1
|
||||||
version: 0.8.1(typescript@5.9.3)(vue@3.5.25(typescript@5.9.3))
|
version: 0.8.1(typescript@5.9.3)(vue@3.5.25(typescript@5.9.3))
|
||||||
autoprefixer:
|
|
||||||
specifier: ^10.4.23
|
|
||||||
version: 10.4.23(postcss@8.5.6)
|
|
||||||
class-variance-authority:
|
class-variance-authority:
|
||||||
specifier: ^0.7.1
|
specifier: ^0.7.1
|
||||||
version: 0.7.1
|
version: 0.7.1
|
||||||
@@ -1672,13 +1672,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==}
|
resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==}
|
||||||
engines: {node: '>= 4.0.0'}
|
engines: {node: '>= 4.0.0'}
|
||||||
|
|
||||||
autoprefixer@10.4.23:
|
|
||||||
resolution: {integrity: sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==}
|
|
||||||
engines: {node: ^10 || ^12 || >=14}
|
|
||||||
hasBin: true
|
|
||||||
peerDependencies:
|
|
||||||
postcss: ^8.1.0
|
|
||||||
|
|
||||||
available-typed-arrays@1.0.7:
|
available-typed-arrays@1.0.7:
|
||||||
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
|
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -2215,9 +2208,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==}
|
resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==}
|
||||||
engines: {node: '>= 6'}
|
engines: {node: '>= 6'}
|
||||||
|
|
||||||
fraction.js@5.3.4:
|
|
||||||
resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==}
|
|
||||||
|
|
||||||
framer-motion@12.23.12:
|
framer-motion@12.23.12:
|
||||||
resolution: {integrity: sha512-6e78rdVtnBvlEVgu6eFEAgG9v3wLnYEboM8I5O5EXvfKC8gxGQB8wXJdhkMy10iVcn05jl6CNw7/HTsTCfwcWg==}
|
resolution: {integrity: sha512-6e78rdVtnBvlEVgu6eFEAgG9v3wLnYEboM8I5O5EXvfKC8gxGQB8wXJdhkMy10iVcn05jl6CNw7/HTsTCfwcWg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -3114,9 +3104,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
|
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
postcss-value-parser@4.2.0:
|
|
||||||
resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
|
|
||||||
|
|
||||||
postcss@8.5.6:
|
postcss@8.5.6:
|
||||||
resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
|
resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
|
||||||
engines: {node: ^10 || ^12 || >=14}
|
engines: {node: ^10 || ^12 || >=14}
|
||||||
@@ -5680,15 +5667,6 @@ snapshots:
|
|||||||
|
|
||||||
at-least-node@1.0.0: {}
|
at-least-node@1.0.0: {}
|
||||||
|
|
||||||
autoprefixer@10.4.23(postcss@8.5.6):
|
|
||||||
dependencies:
|
|
||||||
browserslist: 4.28.1
|
|
||||||
caniuse-lite: 1.0.30001760
|
|
||||||
fraction.js: 5.3.4
|
|
||||||
picocolors: 1.1.1
|
|
||||||
postcss: 8.5.6
|
|
||||||
postcss-value-parser: 4.2.0
|
|
||||||
|
|
||||||
available-typed-arrays@1.0.7:
|
available-typed-arrays@1.0.7:
|
||||||
dependencies:
|
dependencies:
|
||||||
possible-typed-array-names: 1.1.0
|
possible-typed-array-names: 1.1.0
|
||||||
@@ -6395,8 +6373,6 @@ snapshots:
|
|||||||
hasown: 2.0.2
|
hasown: 2.0.2
|
||||||
mime-types: 2.1.35
|
mime-types: 2.1.35
|
||||||
|
|
||||||
fraction.js@5.3.4: {}
|
|
||||||
|
|
||||||
framer-motion@12.23.12:
|
framer-motion@12.23.12:
|
||||||
dependencies:
|
dependencies:
|
||||||
motion-dom: 12.23.12
|
motion-dom: 12.23.12
|
||||||
@@ -7267,8 +7243,6 @@ snapshots:
|
|||||||
|
|
||||||
possible-typed-array-names@1.1.0: {}
|
possible-typed-array-names@1.1.0: {}
|
||||||
|
|
||||||
postcss-value-parser@4.2.0: {}
|
|
||||||
|
|
||||||
postcss@8.5.6:
|
postcss@8.5.6:
|
||||||
dependencies:
|
dependencies:
|
||||||
nanoid: 3.3.11
|
nanoid: 3.3.11
|
||||||
|
|||||||
16
src/App.vue
16
src/App.vue
@@ -1,8 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<!-- 首页:无侧边栏/头部 -->
|
<!-- 首页:无侧边栏/头部 -->
|
||||||
<template v-if="isHomePage">
|
<RouterView v-if="isHomePage" />
|
||||||
<RouterView />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- 其他页面:完整布局(含侧边栏) -->
|
<!-- 其他页面:完整布局(含侧边栏) -->
|
||||||
<SidebarProvider v-else :open="sidebarOpen" @update:open="handleSidebarOpenChange">
|
<SidebarProvider v-else :open="sidebarOpen" @update:open="handleSidebarOpenChange">
|
||||||
@@ -133,13 +131,6 @@
|
|||||||
<SidebarMenuBadge v-if="item.path === '/fleet' && activeFleetMissionsCount > 0" class="bg-primary text-primary-foreground">
|
<SidebarMenuBadge v-if="item.path === '/fleet' && activeFleetMissionsCount > 0" class="bg-primary text-primary-foreground">
|
||||||
{{ activeFleetMissionsCount }}
|
{{ activeFleetMissionsCount }}
|
||||||
</SidebarMenuBadge>
|
</SidebarMenuBadge>
|
||||||
<!-- 未读外交报告数量 -->
|
|
||||||
<SidebarMenuBadge
|
|
||||||
v-if="item.path === '/diplomacy' && unreadDiplomaticReportsCount > 0"
|
|
||||||
class="bg-destructive text-destructive-foreground"
|
|
||||||
>
|
|
||||||
{{ unreadDiplomaticReportsCount }}
|
|
||||||
</SidebarMenuBadge>
|
|
||||||
</SidebarMenuButton>
|
</SidebarMenuButton>
|
||||||
</SidebarMenuItem>
|
</SidebarMenuItem>
|
||||||
</SidebarMenu>
|
</SidebarMenu>
|
||||||
@@ -738,11 +729,6 @@
|
|||||||
return fleetMissions + flyingMissiles
|
return fleetMissions + flyingMissiles
|
||||||
})
|
})
|
||||||
|
|
||||||
// 未读外交报告数量
|
|
||||||
const unreadDiplomaticReportsCount = computed(() => {
|
|
||||||
return (gameStore.player.diplomaticReports || []).filter(r => !r.read).length
|
|
||||||
})
|
|
||||||
|
|
||||||
// 月球相关
|
// 月球相关
|
||||||
const moon = computed(() => {
|
const moon = computed(() => {
|
||||||
if (!planet.value || planet.value.isMoon) return null
|
if (!planet.value || planet.value.isMoon) return null
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<Dialog :open="true" @update:open="handleClose">
|
<Dialog :open="true" @update:open="handleClose">
|
||||||
<DialogContent class="max-w-2xl p-0 overflow-hidden bg-gradient-to-b from-background to-background/95">
|
<DialogContent class="max-w-2xl p-0 overflow-hidden bg-gradient-to-b from-background to-background/95">
|
||||||
|
<!-- 可访问性标题(隐藏) -->
|
||||||
|
<VisuallyHidden>
|
||||||
|
<DialogTitle>{{ t('campaign.dialogue.title') }}</DialogTitle>
|
||||||
|
<DialogDescription>{{ t('campaign.dialogue.description') }}</DialogDescription>
|
||||||
|
</VisuallyHidden>
|
||||||
|
|
||||||
<!-- 对话框头部 - 星空效果 -->
|
<!-- 对话框头部 - 星空效果 -->
|
||||||
<div class="absolute inset-0 pointer-events-none overflow-hidden">
|
<div class="absolute inset-0 pointer-events-none overflow-hidden">
|
||||||
<div class="stars-bg" />
|
<div class="stars-bg" />
|
||||||
@@ -80,7 +86,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, watch, onMounted, onUnmounted } from 'vue'
|
import { ref, computed, watch, onMounted, onUnmounted } from 'vue'
|
||||||
import { useI18n } from '@/composables/useI18n'
|
import { useI18n } from '@/composables/useI18n'
|
||||||
import { Dialog, DialogContent } from '@/components/ui/dialog'
|
import { Dialog, DialogContent, DialogTitle, DialogDescription } from '@/components/ui/dialog'
|
||||||
|
import { VisuallyHidden } from 'reka-ui'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import type { StoryDialogue, DialogueChoice } from '@/types/game'
|
import type { StoryDialogue, DialogueChoice } from '@/types/game'
|
||||||
import { User, Bot, HelpCircle, MessageCircle, ChevronRight } from 'lucide-vue-next'
|
import { User, Bot, HelpCircle, MessageCircle, ChevronRight } from 'lucide-vue-next'
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="[colors[type], sizes[size], 'rounded shadow-sm']" />
|
<span :class="[colors[type], sizes[size], 'inline-block rounded shrink-0']" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@@ -8,21 +8,22 @@
|
|||||||
size?: 'sm' | 'md' | 'lg'
|
size?: 'sm' | 'md' | 'lg'
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
withDefaults(defineProps<Props>(), {
|
||||||
size: 'md'
|
size: 'md'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 使用纯色背景,在 Android WebView 中更可靠
|
||||||
const colors = {
|
const colors = {
|
||||||
metal: 'bg-gradient-to-br from-slate-400 to-slate-600',
|
metal: 'bg-slate-500',
|
||||||
crystal: 'bg-gradient-to-br from-cyan-400 to-blue-600',
|
crystal: 'bg-cyan-500',
|
||||||
deuterium: 'bg-gradient-to-br from-green-400 to-emerald-600',
|
deuterium: 'bg-green-500',
|
||||||
darkMatter: 'bg-gradient-to-br from-purple-600 to-indigo-900',
|
darkMatter: 'bg-purple-700',
|
||||||
energy: 'bg-gradient-to-br from-yellow-400 to-orange-500'
|
energy: 'bg-yellow-500'
|
||||||
}
|
}
|
||||||
|
|
||||||
const sizes = {
|
const sizes = {
|
||||||
sm: 'w-3 h-3',
|
sm: 'w-3 h-3 min-w-3 min-h-3',
|
||||||
md: 'w-4 h-4',
|
md: 'w-4 h-4 min-w-4 min-h-4',
|
||||||
lg: 'w-5 h-5'
|
lg: 'w-5 h-5 min-w-5 min-h-5'
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
{{ t('diplomacy.markAllRead') }}
|
{{ t('diplomacy.markAllRead') }}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<ScrollArea class="h-96">
|
<ScrollArea class="h-auto max-h-96 overflow-y-auto">
|
||||||
<Empty v-if="allNotifications.length === 0" class="border-0">
|
<Empty v-if="allNotifications.length === 0" class="border-0">
|
||||||
<EmptyContent>
|
<EmptyContent>
|
||||||
<ScrollText class="h-10 w-10 text-muted-foreground" />
|
<ScrollText class="h-10 w-10 text-muted-foreground" />
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
{{ t('enemyAlert.markAllRead') }}
|
{{ t('enemyAlert.markAllRead') }}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<ScrollArea class="h-96">
|
<ScrollArea class="h-auto max-h-96 overflow-y-auto">
|
||||||
<Empty v-if="activeAlerts.length === 0" class="border-0">
|
<Empty v-if="activeAlerts.length === 0" class="border-0">
|
||||||
<EmptyContent>
|
<EmptyContent>
|
||||||
<Shield class="h-10 w-10 text-muted-foreground" />
|
<Shield class="h-10 w-10 text-muted-foreground" />
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
|
|
||||||
<ScrollArea class="h-[420px]">
|
<ScrollArea class="h-auto max-h-96 overflow-y-auto">
|
||||||
<TabsContent v-for="tab in tabConfig" :key="tab.value" :value="tab.value" class="mt-0">
|
<TabsContent v-for="tab in tabConfig" :key="tab.value" :value="tab.value" class="mt-0">
|
||||||
<Empty v-if="tab.items.length === 0" class="border-0">
|
<Empty v-if="tab.items.length === 0" class="border-0">
|
||||||
<EmptyContent>
|
<EmptyContent>
|
||||||
@@ -63,7 +63,9 @@
|
|||||||
class="text-[10px] sm:text-xs whitespace-nowrap"
|
class="text-[10px] sm:text-xs whitespace-nowrap"
|
||||||
:class="isWaitingItemResourcesReady(item as WaitingQueueItem) ? 'text-green-500' : 'text-yellow-500'"
|
:class="isWaitingItemResourcesReady(item as WaitingQueueItem) ? 'text-green-500' : 'text-yellow-500'"
|
||||||
>
|
>
|
||||||
{{ isWaitingItemResourcesReady(item as WaitingQueueItem) ? t('queue.resourcesReady') : t('queue.waitingResources') }}
|
{{
|
||||||
|
isWaitingItemResourcesReady(item as WaitingQueueItem) ? t('queue.resourcesReady') : t('queue.waitingResources')
|
||||||
|
}}
|
||||||
</span>
|
</span>
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
@@ -146,8 +148,8 @@
|
|||||||
import { useI18n } from '@/composables/useI18n'
|
import { useI18n } from '@/composables/useI18n'
|
||||||
import { formatTime, formatNumber } from '@/utils/format'
|
import { formatTime, formatNumber } from '@/utils/format'
|
||||||
import type { BuildQueueItem, WaitingQueueItem, BuildingType, ShipType, DefenseType, TechnologyType, Resources } from '@/types/game'
|
import type { BuildQueueItem, WaitingQueueItem, BuildingType, ShipType, DefenseType, TechnologyType, Resources } from '@/types/game'
|
||||||
import * as waitingQueueLogic from '@/logic/waitingQueueLogic'
|
import * as waitingQueueLogic from '@/logic/waitingQueueLogic'
|
||||||
import * as resourceLogic from '@/logic/resourceLogic'
|
import * as resourceLogic from '@/logic/resourceLogic'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const gameStore = useGameStore()
|
const gameStore = useGameStore()
|
||||||
|
|||||||
@@ -81,6 +81,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, watch } from 'vue'
|
import { ref, computed, watch } from 'vue'
|
||||||
import { useI18n } from '@/composables/useI18n'
|
import { useI18n } from '@/composables/useI18n'
|
||||||
|
import { useGameStore } from '@/stores/gameStore'
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@@ -95,13 +96,11 @@ import { Label } from '@/components/ui/label'
|
|||||||
import { Loader2 } from 'lucide-vue-next'
|
import { Loader2 } from 'lucide-vue-next'
|
||||||
import {
|
import {
|
||||||
type WebDAVConfig,
|
type WebDAVConfig,
|
||||||
getWebDAVConfig,
|
|
||||||
saveWebDAVConfig,
|
|
||||||
clearWebDAVConfig,
|
|
||||||
testWebDAVConnection
|
testWebDAVConnection
|
||||||
} from '@/services/webdavService'
|
} from '@/services/webdavService'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
const gameStore = useGameStore()
|
||||||
|
|
||||||
const isOpen = defineModel<boolean>('open', { default: false })
|
const isOpen = defineModel<boolean>('open', { default: false })
|
||||||
|
|
||||||
@@ -130,7 +129,7 @@ const isConfigValid = computed(() => {
|
|||||||
// 加载已保存的配置
|
// 加载已保存的配置
|
||||||
watch(isOpen, (open) => {
|
watch(isOpen, (open) => {
|
||||||
if (open) {
|
if (open) {
|
||||||
const saved = getWebDAVConfig()
|
const saved = gameStore.webdavConfig
|
||||||
if (saved) {
|
if (saved) {
|
||||||
config.value = { ...saved }
|
config.value = { ...saved }
|
||||||
hasExistingConfig.value = true
|
hasExistingConfig.value = true
|
||||||
@@ -152,21 +151,25 @@ const handleTest = async () => {
|
|||||||
testResult.value = null
|
testResult.value = null
|
||||||
|
|
||||||
try {
|
try {
|
||||||
testResult.value = await testWebDAVConnection(config.value)
|
const result = await testWebDAVConnection(config.value)
|
||||||
|
testResult.value = {
|
||||||
|
success: result.success,
|
||||||
|
message: t(result.messageKey)
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
isTesting.value = false
|
isTesting.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
saveWebDAVConfig(config.value)
|
gameStore.webdavConfig = { ...config.value }
|
||||||
hasExistingConfig.value = true
|
hasExistingConfig.value = true
|
||||||
emit('saved', config.value)
|
emit('saved', config.value)
|
||||||
isOpen.value = false
|
isOpen.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleClear = () => {
|
const handleClear = () => {
|
||||||
clearWebDAVConfig()
|
gameStore.webdavConfig = null
|
||||||
hasExistingConfig.value = false
|
hasExistingConfig.value = false
|
||||||
config.value = {
|
config.value = {
|
||||||
serverUrl: '',
|
serverUrl: '',
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ const loadFiles = async () => {
|
|||||||
if (result.success && result.files) {
|
if (result.success && result.files) {
|
||||||
files.value = result.files
|
files.value = result.files
|
||||||
} else {
|
} else {
|
||||||
error.value = result.message || t('settings.webdav.loadFailed')
|
error.value = t(result.messageKey) || t('settings.webdav.loadFailed')
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error.value = e instanceof Error ? e.message : String(e)
|
error.value = e instanceof Error ? e.message : String(e)
|
||||||
@@ -174,7 +174,7 @@ const handleDelete = async (fileName: string) => {
|
|||||||
selectedFile.value = null
|
selectedFile.value = null
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
toast.error(result.message || t('settings.webdav.deleteFailed'))
|
toast.error(t(result.messageKey) || t('settings.webdav.deleteFailed'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -2,20 +2,21 @@
|
|||||||
<AlertDialogPortal>
|
<AlertDialogPortal>
|
||||||
<AlertDialogOverlay
|
<AlertDialogOverlay
|
||||||
data-slot="alert-dialog-overlay"
|
data-slot="alert-dialog-overlay"
|
||||||
class="data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80"
|
class="data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80 flex items-center justify-center p-4"
|
||||||
/>
|
|
||||||
<AlertDialogContent
|
|
||||||
data-slot="alert-dialog-content"
|
|
||||||
v-bind="{ ...$attrs, ...forwarded }"
|
|
||||||
:class="
|
|
||||||
cn(
|
|
||||||
'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg',
|
|
||||||
props.class
|
|
||||||
)
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
<slot />
|
<AlertDialogContent
|
||||||
</AlertDialogContent>
|
data-slot="alert-dialog-content"
|
||||||
|
v-bind="{ ...$attrs, ...forwarded }"
|
||||||
|
:class="
|
||||||
|
cn(
|
||||||
|
'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 z-50 grid w-full max-w-lg gap-4 rounded-lg border p-6 shadow-lg duration-200',
|
||||||
|
props.class
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</AlertDialogContent>
|
||||||
|
</AlertDialogOverlay>
|
||||||
</AlertDialogPortal>
|
</AlertDialogPortal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +1,31 @@
|
|||||||
<template>
|
<template>
|
||||||
<DialogPortal>
|
<DialogPortal>
|
||||||
<DialogOverlay />
|
<DialogOverlay
|
||||||
<DialogContent
|
data-slot="dialog-overlay"
|
||||||
data-slot="dialog-content"
|
class="data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-60 bg-black/80 flex items-center justify-center p-4"
|
||||||
v-bind="{ ...$attrs, ...forwarded }"
|
|
||||||
:class="
|
|
||||||
cn(
|
|
||||||
'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-1/2 left-1/2 z-60 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-2xl',
|
|
||||||
props.class
|
|
||||||
)
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
<slot />
|
<DialogContent
|
||||||
|
data-slot="dialog-content"
|
||||||
<DialogClose
|
v-bind="{ ...$attrs, ...forwarded }"
|
||||||
v-if="showCloseButton"
|
:class="
|
||||||
data-slot="dialog-close"
|
cn(
|
||||||
class="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
|
'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 z-60 grid w-full max-w-2xl gap-4 rounded-lg border p-6 shadow-lg duration-200 relative',
|
||||||
|
props.class
|
||||||
|
)
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<X />
|
<slot />
|
||||||
<span class="sr-only">Close</span>
|
|
||||||
</DialogClose>
|
<DialogClose
|
||||||
</DialogContent>
|
v-if="showCloseButton"
|
||||||
|
data-slot="dialog-close"
|
||||||
|
class="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
|
||||||
|
>
|
||||||
|
<X />
|
||||||
|
<span class="sr-only">Close</span>
|
||||||
|
</DialogClose>
|
||||||
|
</DialogContent>
|
||||||
|
</DialogOverlay>
|
||||||
</DialogPortal>
|
</DialogPortal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -30,9 +34,8 @@
|
|||||||
import type { HTMLAttributes } from 'vue'
|
import type { HTMLAttributes } from 'vue'
|
||||||
import { reactiveOmit } from '@vueuse/core'
|
import { reactiveOmit } from '@vueuse/core'
|
||||||
import { X } from 'lucide-vue-next'
|
import { X } from 'lucide-vue-next'
|
||||||
import { DialogClose, DialogContent, DialogPortal, useForwardPropsEmits } from 'reka-ui'
|
import { DialogClose, DialogContent, DialogOverlay, DialogPortal, useForwardPropsEmits } from 'reka-ui'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
import DialogOverlay from './DialogOverlay.vue'
|
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
inheritAttrs: false
|
inheritAttrs: false
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
v-bind="delegatedProps"
|
v-bind="delegatedProps"
|
||||||
:class="
|
:class="
|
||||||
cn(
|
cn(
|
||||||
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-60 bg-black/80',
|
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-60 bg-black/80 flex items-center justify-center p-4',
|
||||||
props.class
|
props.class
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
|
|||||||
@@ -1,16 +1,19 @@
|
|||||||
<template>
|
<template>
|
||||||
<DialogPortal>
|
<DialogPortal>
|
||||||
<DialogOverlay />
|
<DialogOverlay
|
||||||
<DialogContent
|
data-slot="dialog-overlay"
|
||||||
data-slot="scrollable-dialog-content"
|
class="data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-60 bg-black/80 flex items-center justify-center p-4"
|
||||||
v-bind="{ ...$attrs, ...forwarded }"
|
|
||||||
:class="
|
|
||||||
cn(
|
|
||||||
'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-1/2 left-1/2 z-60 w-[calc(100vw-3rem)] -translate-x-1/2 -translate-y-1/2 rounded-lg border shadow-lg duration-200 sm:w-auto sm:min-w-[764px] flex flex-col p-0',
|
|
||||||
containerClass
|
|
||||||
)
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
|
<DialogContent
|
||||||
|
data-slot="scrollable-dialog-content"
|
||||||
|
v-bind="{ ...$attrs, ...forwarded }"
|
||||||
|
:class="
|
||||||
|
cn(
|
||||||
|
'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 z-60 w-full max-w-3xl rounded-lg border shadow-lg duration-200 flex flex-col p-0 relative',
|
||||||
|
containerClass
|
||||||
|
)
|
||||||
|
"
|
||||||
|
>
|
||||||
<!-- 固定的头部 -->
|
<!-- 固定的头部 -->
|
||||||
<div class="shrink-0 px-4 pt-4 pb-3 sm:px-6 sm:pt-6 sm:pb-4 border-b">
|
<div class="shrink-0 px-4 pt-4 pb-3 sm:px-6 sm:pt-6 sm:pb-4 border-b">
|
||||||
<slot name="header" />
|
<slot name="header" />
|
||||||
@@ -35,7 +38,8 @@
|
|||||||
<X />
|
<X />
|
||||||
<span class="sr-only">Close</span>
|
<span class="sr-only">Close</span>
|
||||||
</DialogClose>
|
</DialogClose>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
</DialogOverlay>
|
||||||
</DialogPortal>
|
</DialogPortal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -44,9 +48,8 @@
|
|||||||
import type { HTMLAttributes } from 'vue'
|
import type { HTMLAttributes } from 'vue'
|
||||||
import { reactiveOmit } from '@vueuse/core'
|
import { reactiveOmit } from '@vueuse/core'
|
||||||
import { X } from 'lucide-vue-next'
|
import { X } from 'lucide-vue-next'
|
||||||
import { DialogClose, DialogContent, DialogPortal, useForwardPropsEmits } from 'reka-ui'
|
import { DialogClose, DialogContent, DialogOverlay, DialogPortal, useForwardPropsEmits } from 'reka-ui'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
import DialogOverlay from './DialogOverlay.vue'
|
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
inheritAttrs: false
|
inheritAttrs: false
|
||||||
|
|||||||
@@ -157,6 +157,10 @@ export default {
|
|||||||
build: 'Bauen',
|
build: 'Bauen',
|
||||||
production: 'Produktion',
|
production: 'Produktion',
|
||||||
consumption: 'Verbrauch',
|
consumption: 'Verbrauch',
|
||||||
|
levelRange: 'Stufenbereich',
|
||||||
|
totalCost: 'Gesamtkosten',
|
||||||
|
totalPoints: 'Gesamtpunkte',
|
||||||
|
researchSpeedBonus: 'Forschungsgeschwindigkeitsbonus',
|
||||||
capacity: 'Capacity/Effect',
|
capacity: 'Capacity/Effect',
|
||||||
storageCapacity: 'Capacity',
|
storageCapacity: 'Capacity',
|
||||||
energyProduction: 'Energy Production',
|
energyProduction: 'Energy Production',
|
||||||
@@ -265,6 +269,9 @@ export default {
|
|||||||
},
|
},
|
||||||
research: {
|
research: {
|
||||||
researchTime: 'Forschungszeit',
|
researchTime: 'Forschungszeit',
|
||||||
|
levelRange: 'Stufenbereich',
|
||||||
|
totalCost: 'Gesamtkosten',
|
||||||
|
totalPoints: 'Gesamtpunkte',
|
||||||
capacity: 'Capacity/Effect',
|
capacity: 'Capacity/Effect',
|
||||||
storageCapacity: 'Capacity',
|
storageCapacity: 'Capacity',
|
||||||
energyProduction: 'Energy Production',
|
energyProduction: 'Energy Production',
|
||||||
@@ -392,8 +399,10 @@ export default {
|
|||||||
tabConsumption: 'Verbrauchsdetails'
|
tabConsumption: 'Verbrauchsdetails'
|
||||||
},
|
},
|
||||||
buildingsView: {
|
buildingsView: {
|
||||||
|
title: 'Gebäude',
|
||||||
spaceUsage: 'Platzbedarf',
|
spaceUsage: 'Platzbedarf',
|
||||||
upgradeCost: 'Ausbaukosten',
|
upgradeCost: 'Ausbaukosten',
|
||||||
|
build: 'Bauen',
|
||||||
upgrade: 'Ausbauen',
|
upgrade: 'Ausbauen',
|
||||||
maxLevelReached: 'Maximale Stufe erreicht',
|
maxLevelReached: 'Maximale Stufe erreicht',
|
||||||
requirementsNotMet: 'Anforderungen nicht erfüllt',
|
requirementsNotMet: 'Anforderungen nicht erfüllt',
|
||||||
@@ -415,10 +424,20 @@ export default {
|
|||||||
'Bitte überprüfen Sie, ob Sie genügend Ressourcen haben, die Voraussetzungen erfüllt sind oder keine anderen Forschungsaufträge vorhanden sind.'
|
'Bitte überprüfen Sie, ob Sie genügend Ressourcen haben, die Voraussetzungen erfüllt sind oder keine anderen Forschungsaufträge vorhanden sind.'
|
||||||
},
|
},
|
||||||
shipyard: {
|
shipyard: {
|
||||||
|
attack: 'Angriff',
|
||||||
|
shield: 'Schild',
|
||||||
|
armor: 'Panzerung',
|
||||||
missileAttack: 'Raketenangriff',
|
missileAttack: 'Raketenangriff',
|
||||||
speed: 'Geschwindigkeit',
|
speed: 'Geschwindigkeit',
|
||||||
cargoCapacity: 'Ladekapazität',
|
cargoCapacity: 'Ladekapazität',
|
||||||
fuelConsumption: 'Treibstoffverbrauch'
|
fuelConsumption: 'Treibstoffverbrauch',
|
||||||
|
buildCost: 'Baukosten',
|
||||||
|
buildTime: 'Bauzeit',
|
||||||
|
perUnit: 'Pro Einheit',
|
||||||
|
batchCalculator: 'Stapelrechner',
|
||||||
|
quantity: 'Menge',
|
||||||
|
totalCost: 'Gesamtkosten',
|
||||||
|
totalTime: 'Gesamtzeit'
|
||||||
},
|
},
|
||||||
shipyardView: {
|
shipyardView: {
|
||||||
title: 'Raumschiffwerft',
|
title: 'Raumschiffwerft',
|
||||||
@@ -530,6 +549,8 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
recycle: 'Recyceln',
|
recycle: 'Recyceln',
|
||||||
|
harvestDarkMatter: 'Dunkle Materie ernten',
|
||||||
|
station: 'Stationieren',
|
||||||
transportResources: 'Ressourcen transportieren',
|
transportResources: 'Ressourcen transportieren',
|
||||||
totalCargoCapacity: 'Gesamtladekapazität',
|
totalCargoCapacity: 'Gesamtladekapazität',
|
||||||
used: 'Verwendet',
|
used: 'Verwendet',
|
||||||
@@ -1804,6 +1825,27 @@ export default {
|
|||||||
reputationBonusDesc: 'Dein Verbündeter {npcName} spricht gut von dir zu {targetNpc}'
|
reputationBonusDesc: 'Dein Verbündeter {npcName} spricht gut von dir zu {targetNpc}'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
webdav: {
|
||||||
|
connectionSuccess: 'WebDAV-Verbindung erfolgreich',
|
||||||
|
connectionSuccessDirectoryCreated: 'WebDAV-Verbindung erfolgreich, Speicherverzeichnis erstellt',
|
||||||
|
authFailed: 'Authentifizierung fehlgeschlagen, bitte Benutzername und Passwort überprüfen',
|
||||||
|
directoryNotExist: 'Verzeichnis existiert nicht und konnte nicht erstellt werden',
|
||||||
|
networkError: 'Netzwerkfehler, bitte Serveradresse und Netzwerk überprüfen',
|
||||||
|
unknownError: 'Unbekannter Fehler',
|
||||||
|
uploadSuccess: 'Spielstand erfolgreich hochgeladen',
|
||||||
|
uploadFailed: 'Hochladen fehlgeschlagen',
|
||||||
|
downloadSuccess: 'Spielstand erfolgreich heruntergeladen',
|
||||||
|
downloadFailed: 'Herunterladen fehlgeschlagen',
|
||||||
|
noSaveFiles: 'Keine Spielstände auf dem Server',
|
||||||
|
fileListSuccess: 'Spielstandliste erfolgreich abgerufen',
|
||||||
|
fileListFailed: 'Abrufen der Spielstandliste fehlgeschlagen',
|
||||||
|
deleteSuccess: 'Spielstand erfolgreich gelöscht',
|
||||||
|
deleteFailed: 'Löschen fehlgeschlagen',
|
||||||
|
serverError: 'Serverfehler',
|
||||||
|
notConfigured: 'WebDAV nicht konfiguriert',
|
||||||
|
invalidUrl: 'Ungültige WebDAV-URL',
|
||||||
|
timeout: 'Verbindungszeitüberschreitung'
|
||||||
|
},
|
||||||
campaign: {
|
campaign: {
|
||||||
name: 'Kampagne',
|
name: 'Kampagne',
|
||||||
description: 'Erkunde die mysteriöse Galaxie und entdecke antike Geheimnisse',
|
description: 'Erkunde die mysteriöse Galaxie und entdecke antike Geheimnisse',
|
||||||
@@ -1830,6 +1872,8 @@ export default {
|
|||||||
branchUnlocked: 'Neuer Storyzweig freigeschaltet!'
|
branchUnlocked: 'Neuer Storyzweig freigeschaltet!'
|
||||||
},
|
},
|
||||||
dialogue: {
|
dialogue: {
|
||||||
|
title: 'Story-Dialog',
|
||||||
|
description: 'Kampagnen-Story-Dialoginhalt',
|
||||||
skip: 'Überspringen',
|
skip: 'Überspringen',
|
||||||
continue: 'Weiter',
|
continue: 'Weiter',
|
||||||
finish: 'Beenden',
|
finish: 'Beenden',
|
||||||
@@ -1942,25 +1986,42 @@ export default {
|
|||||||
spyAnyNPC: 'Spioniere einen NPC-Planeten aus',
|
spyAnyNPC: 'Spioniere einen NPC-Planeten aus',
|
||||||
sendGiftToNPC: 'Sende ein Geschenk an einen NPC',
|
sendGiftToNPC: 'Sende ein Geschenk an einen NPC',
|
||||||
researchAstrophysics: 'Erforsche Astrophysik auf Stufe 1',
|
researchAstrophysics: 'Erforsche Astrophysik auf Stufe 1',
|
||||||
|
researchAstrophysicsHigher: 'Erforsche Astrophysik auf Stufe 3',
|
||||||
buildColonyShip: 'Baue ein Kolonieschiff',
|
buildColonyShip: 'Baue ein Kolonieschiff',
|
||||||
colonizeNewPlanet: 'Kolonisiere einen neuen Planeten',
|
colonizeNewPlanet: 'Kolonisiere einen neuen Planeten',
|
||||||
completeExpedition: 'Schließe eine Expeditionsmission ab',
|
colonizeMultiple: 'Kolonisiere 5 Planeten',
|
||||||
|
completeExpedition: 'Schließe 3 Expeditionsmissionen ab',
|
||||||
|
expeditionDeepSpace: 'Schließe 2 Tiefenraum-Expeditionen ab',
|
||||||
|
expeditionUncharted: 'Erkunde 1 unbekannte Region',
|
||||||
|
expeditionDangerous: 'Schließe 3 gefährliche Nebel-Expeditionen ab',
|
||||||
discoverRuins: 'Entdecke antike Ruinen',
|
discoverRuins: 'Entdecke antike Ruinen',
|
||||||
researchComputer: 'Erforsche Computertechnik auf Stufe 4',
|
researchComputer: 'Erforsche Computertechnik auf Stufe 4',
|
||||||
|
researchImpulse: 'Erforsche Impulsantrieb auf Stufe 3',
|
||||||
|
researchLaser: 'Erforsche Lasertechnik auf Stufe 5',
|
||||||
|
researchIntergalactic: 'Erforsche Computertechnik auf Stufe 10',
|
||||||
|
researchGraviton: 'Erforsche Gravitontechnik auf Stufe 1',
|
||||||
improveRelation: 'Verbessere Beziehungen zu einem NPC',
|
improveRelation: 'Verbessere Beziehungen zu einem NPC',
|
||||||
reachFriendly: 'Erreiche freundlichen Status mit einem NPC',
|
reachFriendly: 'Erreiche freundlichen Status mit einem NPC',
|
||||||
spyHostileNPC: 'Spioniere einen feindlichen NPC aus',
|
reachFriendlyRelation: 'Erreiche freundlichen Status mit einem beliebigen NPC',
|
||||||
|
sendMultipleGifts: 'Sende 3 Geschenke an NPCs',
|
||||||
|
spyHostileNPC: 'Spioniere 2 feindliche NPCs aus',
|
||||||
formAlliance: 'Schließe Allianz mit einem freundlichen NPC',
|
formAlliance: 'Schließe Allianz mit einem freundlichen NPC',
|
||||||
buildDefenses: 'Baue Verteidigungsanlagen',
|
buildDefenses: 'Baue Verteidigungsanlagen',
|
||||||
|
buildMissileSilo: 'Baue Raketensilo auf Stufe 2',
|
||||||
|
buildCruisers: 'Baue 10 Kreuzer',
|
||||||
winDefenseBattle: 'Gewinne eine Verteidigungsschlacht',
|
winDefenseBattle: 'Gewinne eine Verteidigungsschlacht',
|
||||||
|
defendAgainstAttack: 'Verteidige erfolgreich gegen 1 Angriff',
|
||||||
spyEnemyPlanet: 'Spioniere Feindplaneten aus',
|
spyEnemyPlanet: 'Spioniere Feindplaneten aus',
|
||||||
|
spyEnemyPlanets: 'Spioniere 5 Feindplaneten aus',
|
||||||
|
winAttackBattles: 'Gewinne 3 Angriffsschlachten',
|
||||||
attackEnemy: 'Greife den Feind an',
|
attackEnemy: 'Greife den Feind an',
|
||||||
recycleDebris: 'Recycel Trümmer',
|
recycleDebris: 'Recycel 5 Mal Trümmer',
|
||||||
buildBattleships: 'Baue 10 Schlachtschiffe',
|
buildBattleships: 'Baue 20 Schlachtschiffe',
|
||||||
exploreDeepRuins: 'Erkunde tiefe Ruinen',
|
exploreDeepRuins: 'Erkunde tiefe Ruinen',
|
||||||
researchHyperspace: 'Erforsche Hyperraumtechnologie',
|
researchHyperspace: 'Erforsche Hyperraumantrieb auf Stufe 3',
|
||||||
defeatBoss: 'Besiege den mysteriösen Feind',
|
defeatBoss: 'Besiege den Antiken Wächter',
|
||||||
colonizeSpecial: 'Kolonisiere besonderen Standort',
|
colonizeSpecial: 'Kolonisiere besonderen Standort',
|
||||||
|
accumulateWealth: 'Sammle 1 Million Gesamtressourcen',
|
||||||
continueDevelopment: 'Setze Entwicklung fort'
|
continueDevelopment: 'Setze Entwicklung fort'
|
||||||
},
|
},
|
||||||
dialogues: {
|
dialogues: {
|
||||||
|
|||||||
@@ -155,6 +155,10 @@ export default {
|
|||||||
buildTime: 'Build Time',
|
buildTime: 'Build Time',
|
||||||
production: 'Production',
|
production: 'Production',
|
||||||
consumption: 'Consumption',
|
consumption: 'Consumption',
|
||||||
|
levelRange: 'Level Range',
|
||||||
|
totalCost: 'Total Cost',
|
||||||
|
totalPoints: 'Total Points',
|
||||||
|
researchSpeedBonus: 'Research Speed Bonus',
|
||||||
|
|
||||||
storageCapacity: 'Capacity',
|
storageCapacity: 'Capacity',
|
||||||
energyProduction: 'Energy Production',
|
energyProduction: 'Energy Production',
|
||||||
@@ -260,6 +264,9 @@ export default {
|
|||||||
},
|
},
|
||||||
research: {
|
research: {
|
||||||
researchTime: 'Research Time',
|
researchTime: 'Research Time',
|
||||||
|
levelRange: 'Level Range',
|
||||||
|
totalCost: 'Total Cost',
|
||||||
|
totalPoints: 'Total Points',
|
||||||
|
|
||||||
attackBonus: 'Attack Bonus',
|
attackBonus: 'Attack Bonus',
|
||||||
shieldBonus: 'Shield Bonus',
|
shieldBonus: 'Shield Bonus',
|
||||||
@@ -382,6 +389,7 @@ export default {
|
|||||||
title: 'Buildings',
|
title: 'Buildings',
|
||||||
spaceUsage: 'Space Usage',
|
spaceUsage: 'Space Usage',
|
||||||
upgradeCost: 'Upgrade Cost',
|
upgradeCost: 'Upgrade Cost',
|
||||||
|
build: 'Build',
|
||||||
upgrade: 'Upgrade',
|
upgrade: 'Upgrade',
|
||||||
maxLevelReached: 'Max Level Reached',
|
maxLevelReached: 'Max Level Reached',
|
||||||
requirementsNotMet: 'Requirements Not Met',
|
requirementsNotMet: 'Requirements Not Met',
|
||||||
@@ -402,10 +410,20 @@ export default {
|
|||||||
researchFailedMessage: 'Please check if you have enough resources, prerequisites are met, or if there are other research tasks.'
|
researchFailedMessage: 'Please check if you have enough resources, prerequisites are met, or if there are other research tasks.'
|
||||||
},
|
},
|
||||||
shipyard: {
|
shipyard: {
|
||||||
|
attack: 'Attack',
|
||||||
|
shield: 'Shield',
|
||||||
|
armor: 'Armor',
|
||||||
missileAttack: 'Missile Attack',
|
missileAttack: 'Missile Attack',
|
||||||
speed: 'Speed',
|
speed: 'Speed',
|
||||||
cargoCapacity: 'Cargo Capacity',
|
cargoCapacity: 'Cargo Capacity',
|
||||||
fuelConsumption: 'Fuel Consumption'
|
fuelConsumption: 'Fuel Consumption',
|
||||||
|
buildCost: 'Build Cost',
|
||||||
|
buildTime: 'Build Time',
|
||||||
|
perUnit: 'Per Unit',
|
||||||
|
batchCalculator: 'Batch Calculator',
|
||||||
|
quantity: 'Quantity',
|
||||||
|
totalCost: 'Total Cost',
|
||||||
|
totalTime: 'Total Time'
|
||||||
},
|
},
|
||||||
shipyardView: {
|
shipyardView: {
|
||||||
title: 'Shipyard',
|
title: 'Shipyard',
|
||||||
@@ -425,7 +443,17 @@ export default {
|
|||||||
buildFailedMessage: 'Please check if you have enough resources or if prerequisites are met.'
|
buildFailedMessage: 'Please check if you have enough resources or if prerequisites are met.'
|
||||||
},
|
},
|
||||||
defense: {
|
defense: {
|
||||||
missileAttack: 'Missile Attack'
|
attack: 'Attack',
|
||||||
|
shield: 'Shield',
|
||||||
|
armor: 'Armor',
|
||||||
|
missileAttack: 'Missile Attack',
|
||||||
|
buildCost: 'Build Cost',
|
||||||
|
buildTime: 'Build Time',
|
||||||
|
perUnit: 'Per Unit',
|
||||||
|
batchCalculator: 'Batch Calculator',
|
||||||
|
quantity: 'Quantity',
|
||||||
|
totalCost: 'Total Cost',
|
||||||
|
totalTime: 'Total Time'
|
||||||
},
|
},
|
||||||
defenseView: {
|
defenseView: {
|
||||||
title: 'Defense',
|
title: 'Defense',
|
||||||
@@ -504,6 +532,8 @@ export default {
|
|||||||
},
|
},
|
||||||
recycle: 'Recycle',
|
recycle: 'Recycle',
|
||||||
destroy: 'Planet Destruction',
|
destroy: 'Planet Destruction',
|
||||||
|
harvestDarkMatter: 'Harvest Dark Matter',
|
||||||
|
station: 'Station',
|
||||||
transportResources: 'Transport Resources',
|
transportResources: 'Transport Resources',
|
||||||
totalCargoCapacity: 'Total Cargo Capacity',
|
totalCargoCapacity: 'Total Cargo Capacity',
|
||||||
used: 'Used',
|
used: 'Used',
|
||||||
@@ -1753,6 +1783,8 @@ export default {
|
|||||||
branchUnlocked: 'New story branch unlocked!'
|
branchUnlocked: 'New story branch unlocked!'
|
||||||
},
|
},
|
||||||
dialogue: {
|
dialogue: {
|
||||||
|
title: 'Story Dialogue',
|
||||||
|
description: 'Campaign story dialogue content',
|
||||||
skip: 'Skip',
|
skip: 'Skip',
|
||||||
continue: 'Continue',
|
continue: 'Continue',
|
||||||
finish: 'Finish',
|
finish: 'Finish',
|
||||||
@@ -1940,25 +1972,42 @@ export default {
|
|||||||
spyAnyNPC: 'Spy on any NPC planet',
|
spyAnyNPC: 'Spy on any NPC planet',
|
||||||
sendGiftToNPC: 'Send a gift to any NPC',
|
sendGiftToNPC: 'Send a gift to any NPC',
|
||||||
researchAstrophysics: 'Research Astrophysics to level 1',
|
researchAstrophysics: 'Research Astrophysics to level 1',
|
||||||
|
researchAstrophysicsHigher: 'Research Astrophysics to level 3',
|
||||||
buildColonyShip: 'Build a Colony Ship',
|
buildColonyShip: 'Build a Colony Ship',
|
||||||
colonizeNewPlanet: 'Colonize a new planet',
|
colonizeNewPlanet: 'Colonize a new planet',
|
||||||
completeExpedition: 'Complete an expedition mission',
|
colonizeMultiple: 'Colonize 5 planets',
|
||||||
|
completeExpedition: 'Complete 3 expedition missions',
|
||||||
|
expeditionDeepSpace: 'Complete 2 deep space expeditions',
|
||||||
|
expeditionUncharted: 'Explore 1 uncharted region',
|
||||||
|
expeditionDangerous: 'Complete 3 dangerous nebula expeditions',
|
||||||
discoverRuins: 'Discover ancient ruins',
|
discoverRuins: 'Discover ancient ruins',
|
||||||
researchComputer: 'Research Computer Technology to level 4',
|
researchComputer: 'Research Computer Technology to level 4',
|
||||||
|
researchImpulse: 'Research Impulse Drive to level 3',
|
||||||
|
researchLaser: 'Research Laser Technology to level 5',
|
||||||
|
researchIntergalactic: 'Research Computer Technology to level 10',
|
||||||
|
researchGraviton: 'Research Graviton Technology to level 1',
|
||||||
improveRelation: 'Improve relations with an NPC',
|
improveRelation: 'Improve relations with an NPC',
|
||||||
reachFriendly: 'Reach friendly status with an NPC',
|
reachFriendly: 'Reach friendly status with an NPC',
|
||||||
spyHostileNPC: 'Spy on a hostile NPC',
|
reachFriendlyRelation: 'Reach friendly status with any NPC',
|
||||||
|
sendMultipleGifts: 'Send 3 gifts to NPCs',
|
||||||
|
spyHostileNPC: 'Spy on 2 hostile NPCs',
|
||||||
formAlliance: 'Form alliance with a friendly NPC',
|
formAlliance: 'Form alliance with a friendly NPC',
|
||||||
buildDefenses: 'Build defense facilities',
|
buildDefenses: 'Build defense facilities',
|
||||||
|
buildMissileSilo: 'Build Missile Silo to level 2',
|
||||||
|
buildCruisers: 'Build 10 Cruisers',
|
||||||
winDefenseBattle: 'Win a defensive battle',
|
winDefenseBattle: 'Win a defensive battle',
|
||||||
|
defendAgainstAttack: 'Successfully defend against 1 attack',
|
||||||
spyEnemyPlanet: 'Spy on enemy planet',
|
spyEnemyPlanet: 'Spy on enemy planet',
|
||||||
|
spyEnemyPlanets: 'Spy on 5 enemy planets',
|
||||||
|
winAttackBattles: 'Win 3 attack battles',
|
||||||
attackEnemy: 'Attack the enemy',
|
attackEnemy: 'Attack the enemy',
|
||||||
recycleDebris: 'Recycle debris',
|
recycleDebris: 'Recycle debris 5 times',
|
||||||
buildBattleships: 'Build 10 Battleships',
|
buildBattleships: 'Build 20 Battleships',
|
||||||
exploreDeepRuins: 'Explore deep ruins',
|
exploreDeepRuins: 'Explore deep ruins',
|
||||||
researchHyperspace: 'Research Hyperspace Technology',
|
researchHyperspace: 'Research Hyperspace Drive to level 3',
|
||||||
defeatBoss: 'Defeat the mysterious enemy',
|
defeatBoss: 'Defeat the Ancient Guardian',
|
||||||
colonizeSpecial: 'Colonize special location',
|
colonizeSpecial: 'Colonize special location',
|
||||||
|
accumulateWealth: 'Accumulate 1 million total resources',
|
||||||
continueDevelopment: 'Continue development'
|
continueDevelopment: 'Continue development'
|
||||||
},
|
},
|
||||||
dialogues: {
|
dialogues: {
|
||||||
@@ -2162,5 +2211,31 @@ export default {
|
|||||||
reputationBonus: 'Reputation Bonus',
|
reputationBonus: 'Reputation Bonus',
|
||||||
reputationBonusDesc: 'Your ally {npcName} speaks well of you to {targetNpc}'
|
reputationBonusDesc: 'Your ally {npcName} speaks well of you to {targetNpc}'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
webdav: {
|
||||||
|
// Connection
|
||||||
|
connectionSuccess: 'WebDAV connection successful',
|
||||||
|
connectionSuccessDirectoryCreated: 'WebDAV connection successful, save directory created',
|
||||||
|
authFailed: 'Authentication failed, please check username and password',
|
||||||
|
directoryNotExist: 'Directory does not exist and cannot be created',
|
||||||
|
connectionFailedHttp: 'Connection failed: HTTP {status}',
|
||||||
|
networkError: 'Network error, possibly CORS restriction. Try using a CORS-enabled WebDAV service or proxy.',
|
||||||
|
connectionError: 'Connection error: {error}',
|
||||||
|
// Upload
|
||||||
|
uploadSuccess: 'Upload successful',
|
||||||
|
noWritePermission: 'No write permission',
|
||||||
|
insufficientStorage: 'Insufficient storage space',
|
||||||
|
uploadFailedHttp: 'Upload failed: HTTP {status}',
|
||||||
|
uploadError: 'Upload error: {error}',
|
||||||
|
// Download
|
||||||
|
fileNotExist: 'File does not exist',
|
||||||
|
downloadFailedHttp: 'Download failed: HTTP {status}',
|
||||||
|
downloadError: 'Download error: {error}',
|
||||||
|
// List
|
||||||
|
listFailedHttp: 'Failed to get file list: HTTP {status}',
|
||||||
|
listError: 'Error getting file list: {error}',
|
||||||
|
// Delete
|
||||||
|
deleteFailedHttp: 'Delete failed: HTTP {status}',
|
||||||
|
deleteError: 'Delete error: {error}'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -155,6 +155,10 @@ export default {
|
|||||||
buildTime: 'Tiempo de Construcción',
|
buildTime: 'Tiempo de Construcción',
|
||||||
production: 'Producción',
|
production: 'Producción',
|
||||||
consumption: 'Consumo',
|
consumption: 'Consumo',
|
||||||
|
levelRange: 'Rango de Nivel',
|
||||||
|
totalCost: 'Costo Total',
|
||||||
|
totalPoints: 'Puntos Totales',
|
||||||
|
researchSpeedBonus: 'Bono de Velocidad de Investigación',
|
||||||
|
|
||||||
storageCapacity: 'Capacidad',
|
storageCapacity: 'Capacidad',
|
||||||
energyProduction: 'Producción de Energía',
|
energyProduction: 'Producción de Energía',
|
||||||
@@ -411,10 +415,20 @@ export default {
|
|||||||
'Por favor verifica si tienes suficientes recursos, si se cumplen los prerrequisitos, o si hay otras tareas de investigación.'
|
'Por favor verifica si tienes suficientes recursos, si se cumplen los prerrequisitos, o si hay otras tareas de investigación.'
|
||||||
},
|
},
|
||||||
shipyard: {
|
shipyard: {
|
||||||
|
attack: 'Ataque',
|
||||||
|
shield: 'Escudo',
|
||||||
|
armor: 'Blindaje',
|
||||||
missileAttack: 'Ataque de Misil',
|
missileAttack: 'Ataque de Misil',
|
||||||
speed: 'Velocidad',
|
speed: 'Velocidad',
|
||||||
cargoCapacity: 'Capacidad de Carga',
|
cargoCapacity: 'Capacidad de Carga',
|
||||||
fuelConsumption: 'Consumo de Combustible'
|
fuelConsumption: 'Consumo de Combustible',
|
||||||
|
buildCost: 'Costo de Construcción',
|
||||||
|
buildTime: 'Tiempo de Construcción',
|
||||||
|
perUnit: 'Por Unidad',
|
||||||
|
batchCalculator: 'Calculadora de Lotes',
|
||||||
|
quantity: 'Cantidad',
|
||||||
|
totalCost: 'Costo Total',
|
||||||
|
totalTime: 'Tiempo Total'
|
||||||
},
|
},
|
||||||
shipyardView: {
|
shipyardView: {
|
||||||
title: 'Astillero',
|
title: 'Astillero',
|
||||||
@@ -524,6 +538,8 @@ export default {
|
|||||||
},
|
},
|
||||||
recycle: 'Reciclar',
|
recycle: 'Reciclar',
|
||||||
destroy: 'Destrucción Planetaria',
|
destroy: 'Destrucción Planetaria',
|
||||||
|
harvestDarkMatter: 'Recolectar Materia Oscura',
|
||||||
|
station: 'Estacionar',
|
||||||
transportResources: 'Transportar Recursos',
|
transportResources: 'Transportar Recursos',
|
||||||
totalCargoCapacity: 'Capacidad de Carga Total',
|
totalCargoCapacity: 'Capacidad de Carga Total',
|
||||||
used: 'Usado',
|
used: 'Usado',
|
||||||
@@ -1771,6 +1787,8 @@ export default {
|
|||||||
branchUnlocked: '¡Nueva rama de historia desbloqueada!'
|
branchUnlocked: '¡Nueva rama de historia desbloqueada!'
|
||||||
},
|
},
|
||||||
dialogue: {
|
dialogue: {
|
||||||
|
title: 'Diálogo de historia',
|
||||||
|
description: 'Contenido del diálogo de la campaña',
|
||||||
skip: 'Saltar',
|
skip: 'Saltar',
|
||||||
continue: 'Continuar',
|
continue: 'Continuar',
|
||||||
finish: 'Finalizar',
|
finish: 'Finalizar',
|
||||||
@@ -1958,25 +1976,42 @@ export default {
|
|||||||
spyAnyNPC: 'Espiar cualquier planeta NPC',
|
spyAnyNPC: 'Espiar cualquier planeta NPC',
|
||||||
sendGiftToNPC: 'Enviar un regalo a cualquier NPC',
|
sendGiftToNPC: 'Enviar un regalo a cualquier NPC',
|
||||||
researchAstrophysics: 'Investigar Astrofísica al nivel 1',
|
researchAstrophysics: 'Investigar Astrofísica al nivel 1',
|
||||||
|
researchAstrophysicsHigher: 'Investigar Astrofísica al nivel 3',
|
||||||
buildColonyShip: 'Construir una Nave Colonial',
|
buildColonyShip: 'Construir una Nave Colonial',
|
||||||
colonizeNewPlanet: 'Colonizar un nuevo planeta',
|
colonizeNewPlanet: 'Colonizar un nuevo planeta',
|
||||||
completeExpedition: 'Completar una misión de expedición',
|
colonizeMultiple: 'Colonizar 5 planetas',
|
||||||
|
completeExpedition: 'Completar 3 misiones de expedición',
|
||||||
|
expeditionDeepSpace: 'Completar 2 expediciones al espacio profundo',
|
||||||
|
expeditionUncharted: 'Explorar 1 región desconocida',
|
||||||
|
expeditionDangerous: 'Completar 3 expediciones a nebulosas peligrosas',
|
||||||
discoverRuins: 'Descubrir ruinas antiguas',
|
discoverRuins: 'Descubrir ruinas antiguas',
|
||||||
researchComputer: 'Investigar Tecnología de Computación al nivel 4',
|
researchComputer: 'Investigar Tecnología de Computación al nivel 4',
|
||||||
|
researchImpulse: 'Investigar Motor de Impulso al nivel 3',
|
||||||
|
researchLaser: 'Investigar Tecnología Láser al nivel 5',
|
||||||
|
researchIntergalactic: 'Investigar Tecnología de Computación al nivel 10',
|
||||||
|
researchGraviton: 'Investigar Tecnología Gravitón al nivel 1',
|
||||||
improveRelation: 'Mejorar relaciones con un NPC',
|
improveRelation: 'Mejorar relaciones con un NPC',
|
||||||
reachFriendly: 'Alcanzar estatus amigable con un NPC',
|
reachFriendly: 'Alcanzar estatus amigable con un NPC',
|
||||||
spyHostileNPC: 'Espiar un NPC hostil',
|
reachFriendlyRelation: 'Alcanzar estatus amigable con cualquier NPC',
|
||||||
|
sendMultipleGifts: 'Enviar 3 regalos a NPCs',
|
||||||
|
spyHostileNPC: 'Espiar 2 NPCs hostiles',
|
||||||
formAlliance: 'Formar alianza con un NPC amigable',
|
formAlliance: 'Formar alianza con un NPC amigable',
|
||||||
buildDefenses: 'Construir instalaciones de defensa',
|
buildDefenses: 'Construir instalaciones de defensa',
|
||||||
|
buildMissileSilo: 'Construir Silo de Misiles al nivel 2',
|
||||||
|
buildCruisers: 'Construir 10 Cruceros',
|
||||||
winDefenseBattle: 'Ganar una batalla defensiva',
|
winDefenseBattle: 'Ganar una batalla defensiva',
|
||||||
|
defendAgainstAttack: 'Defender exitosamente contra 1 ataque',
|
||||||
spyEnemyPlanet: 'Espiar planeta enemigo',
|
spyEnemyPlanet: 'Espiar planeta enemigo',
|
||||||
|
spyEnemyPlanets: 'Espiar 5 planetas enemigos',
|
||||||
|
winAttackBattles: 'Ganar 3 batallas de ataque',
|
||||||
attackEnemy: 'Atacar al enemigo',
|
attackEnemy: 'Atacar al enemigo',
|
||||||
recycleDebris: 'Reciclar escombros',
|
recycleDebris: 'Reciclar escombros 5 veces',
|
||||||
buildBattleships: 'Construir 10 Naves de Batalla',
|
buildBattleships: 'Construir 20 Naves de Batalla',
|
||||||
exploreDeepRuins: 'Explorar ruinas profundas',
|
exploreDeepRuins: 'Explorar ruinas profundas',
|
||||||
researchHyperspace: 'Investigar Tecnología de Hiperespacio',
|
researchHyperspace: 'Investigar Motor de Hiperespacio al nivel 3',
|
||||||
defeatBoss: 'Derrotar al enemigo misterioso',
|
defeatBoss: 'Derrotar al Guardián Antiguo',
|
||||||
colonizeSpecial: 'Colonizar ubicación especial',
|
colonizeSpecial: 'Colonizar ubicación especial',
|
||||||
|
accumulateWealth: 'Acumular 1 millón de recursos totales',
|
||||||
continueDevelopment: 'Continuar desarrollo'
|
continueDevelopment: 'Continuar desarrollo'
|
||||||
},
|
},
|
||||||
dialogues: {
|
dialogues: {
|
||||||
@@ -2184,5 +2219,26 @@ export default {
|
|||||||
reputationBonus: 'Bonificación de Reputación',
|
reputationBonus: 'Bonificación de Reputación',
|
||||||
reputationBonusDesc: 'Tu aliado {npcName} habla bien de ti ante {targetNpc}'
|
reputationBonusDesc: 'Tu aliado {npcName} habla bien de ti ante {targetNpc}'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
webdav: {
|
||||||
|
connectionSuccess: 'Conexión WebDAV exitosa',
|
||||||
|
connectionSuccessDirectoryCreated: 'Conexión WebDAV exitosa, directorio de guardado creado',
|
||||||
|
authFailed: 'Autenticación fallida, verifica el nombre de usuario y contraseña',
|
||||||
|
directoryNotExist: 'El directorio no existe y no se puede crear',
|
||||||
|
networkError: 'Error de red, verifica la dirección del servidor y la red',
|
||||||
|
unknownError: 'Error desconocido',
|
||||||
|
uploadSuccess: 'Guardado subido exitosamente',
|
||||||
|
uploadFailed: 'Error al subir',
|
||||||
|
downloadSuccess: 'Guardado descargado exitosamente',
|
||||||
|
downloadFailed: 'Error al descargar',
|
||||||
|
noSaveFiles: 'No hay archivos de guardado en el servidor',
|
||||||
|
fileListSuccess: 'Lista de guardados obtenida exitosamente',
|
||||||
|
fileListFailed: 'Error al obtener la lista de guardados',
|
||||||
|
deleteSuccess: 'Guardado eliminado exitosamente',
|
||||||
|
deleteFailed: 'Error al eliminar',
|
||||||
|
serverError: 'Error del servidor',
|
||||||
|
notConfigured: 'WebDAV no configurado',
|
||||||
|
invalidUrl: 'URL de WebDAV inválida',
|
||||||
|
timeout: 'Tiempo de conexión agotado'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -641,7 +641,9 @@ export default {
|
|||||||
jumpGateSuccessMessage: '艦隊は{target}へ瞬時に転送されました',
|
jumpGateSuccessMessage: '艦隊は{target}へ瞬時に転送されました',
|
||||||
jumpGateFailed: 'ジャンプゲート転送失敗',
|
jumpGateFailed: 'ジャンプゲート転送失敗',
|
||||||
jumpGateFailedMessage: 'ジャンプゲートの状態と艦隊構成を確認してください',
|
jumpGateFailedMessage: 'ジャンプゲートの状態と艦隊構成を確認してください',
|
||||||
destroy: '破壊'
|
destroy: '破壊',
|
||||||
|
harvestDarkMatter: 'ダークマター採取',
|
||||||
|
station: '駐留'
|
||||||
},
|
},
|
||||||
officersView: {
|
officersView: {
|
||||||
title: '士官',
|
title: '士官',
|
||||||
@@ -1854,6 +1856,27 @@ export default {
|
|||||||
reputationBonusDesc: '同盟の{npcName}が{targetNpc}にあなたのことを良く言っています'
|
reputationBonusDesc: '同盟の{npcName}が{targetNpc}にあなたのことを良く言っています'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
webdav: {
|
||||||
|
connectionSuccess: 'WebDAV接続成功',
|
||||||
|
connectionSuccessDirectoryCreated: 'WebDAV接続成功、保存ディレクトリを作成しました',
|
||||||
|
authFailed: '認証失敗、ユーザー名とパスワードを確認してください',
|
||||||
|
directoryNotExist: 'ディレクトリが存在せず、作成できませんでした',
|
||||||
|
networkError: 'ネットワークエラー、サーバーアドレスとネットワークを確認してください',
|
||||||
|
unknownError: '不明なエラー',
|
||||||
|
uploadSuccess: 'セーブデータのアップロード成功',
|
||||||
|
uploadFailed: 'アップロード失敗',
|
||||||
|
downloadSuccess: 'セーブデータのダウンロード成功',
|
||||||
|
downloadFailed: 'ダウンロード失敗',
|
||||||
|
noSaveFiles: 'サーバーにセーブデータがありません',
|
||||||
|
fileListSuccess: 'セーブデータリストの取得成功',
|
||||||
|
fileListFailed: 'セーブデータリストの取得失敗',
|
||||||
|
deleteSuccess: 'セーブデータの削除成功',
|
||||||
|
deleteFailed: '削除失敗',
|
||||||
|
serverError: 'サーバーエラー',
|
||||||
|
notConfigured: 'WebDAVが設定されていません',
|
||||||
|
invalidUrl: '無効なWebDAV URL',
|
||||||
|
timeout: '接続タイムアウト'
|
||||||
|
},
|
||||||
campaign: {
|
campaign: {
|
||||||
name: 'キャンペーン',
|
name: 'キャンペーン',
|
||||||
description: '神秘的な銀河を探索し、古代の秘密を解き明かす',
|
description: '神秘的な銀河を探索し、古代の秘密を解き明かす',
|
||||||
@@ -1881,6 +1904,8 @@ export default {
|
|||||||
branchUnlocked: '新しいストーリー分岐が解放されました!'
|
branchUnlocked: '新しいストーリー分岐が解放されました!'
|
||||||
},
|
},
|
||||||
dialogue: {
|
dialogue: {
|
||||||
|
title: 'ストーリー対話',
|
||||||
|
description: 'キャンペーンストーリーの対話内容',
|
||||||
skip: 'スキップ',
|
skip: 'スキップ',
|
||||||
continue: '続ける',
|
continue: '続ける',
|
||||||
finish: '完了',
|
finish: '完了',
|
||||||
@@ -1993,25 +2018,42 @@ export default {
|
|||||||
spyAnyNPC: 'NPC惑星をスパイ',
|
spyAnyNPC: 'NPC惑星をスパイ',
|
||||||
sendGiftToNPC: 'NPCに贈り物を送る',
|
sendGiftToNPC: 'NPCに贈り物を送る',
|
||||||
researchAstrophysics: '宇宙物理学をレベル1まで研究',
|
researchAstrophysics: '宇宙物理学をレベル1まで研究',
|
||||||
|
researchAstrophysicsHigher: '宇宙物理学をレベル3まで研究',
|
||||||
buildColonyShip: 'コロニーシップを建造',
|
buildColonyShip: 'コロニーシップを建造',
|
||||||
colonizeNewPlanet: '新惑星を植民地化',
|
colonizeNewPlanet: '新惑星を植民地化',
|
||||||
completeExpedition: '遠征ミッションを完了',
|
colonizeMultiple: '5つの惑星を植民地化',
|
||||||
|
completeExpedition: '3回の遠征ミッションを完了',
|
||||||
|
expeditionDeepSpace: '2回の深宇宙遠征を完了',
|
||||||
|
expeditionUncharted: '1回の未知領域探索',
|
||||||
|
expeditionDangerous: '3回の危険な星雲遠征を完了',
|
||||||
discoverRuins: '古代遺跡を発見',
|
discoverRuins: '古代遺跡を発見',
|
||||||
researchComputer: 'コンピュータ技術をレベル4まで研究',
|
researchComputer: 'コンピュータ技術をレベル4まで研究',
|
||||||
|
researchImpulse: 'インパルスドライブをレベル3まで研究',
|
||||||
|
researchLaser: 'レーザー技術をレベル5まで研究',
|
||||||
|
researchIntergalactic: 'コンピュータ技術をレベル10まで研究',
|
||||||
|
researchGraviton: 'グラビトン技術をレベル1まで研究',
|
||||||
improveRelation: 'NPCとの関係を改善',
|
improveRelation: 'NPCとの関係を改善',
|
||||||
reachFriendly: 'NPCと友好ステータスに到達',
|
reachFriendly: 'NPCと友好ステータスに到達',
|
||||||
spyHostileNPC: '敵対NPCをスパイ',
|
reachFriendlyRelation: '任意のNPCと友好ステータスに到達',
|
||||||
|
sendMultipleGifts: 'NPCに3回贈り物を送る',
|
||||||
|
spyHostileNPC: '2つの敵対NPCをスパイ',
|
||||||
formAlliance: '友好NPCと同盟を結ぶ',
|
formAlliance: '友好NPCと同盟を結ぶ',
|
||||||
buildDefenses: '防衛施設を建設',
|
buildDefenses: '防衛施設を建設',
|
||||||
|
buildMissileSilo: 'ミサイルサイロをレベル2に建設',
|
||||||
|
buildCruisers: 'クルーザー10隻を建造',
|
||||||
winDefenseBattle: '防衛戦に勝利',
|
winDefenseBattle: '防衛戦に勝利',
|
||||||
|
defendAgainstAttack: '1回の攻撃を防御成功',
|
||||||
spyEnemyPlanet: '敵惑星をスパイ',
|
spyEnemyPlanet: '敵惑星をスパイ',
|
||||||
|
spyEnemyPlanets: '5つの敵惑星をスパイ',
|
||||||
|
winAttackBattles: '3回の攻撃戦に勝利',
|
||||||
attackEnemy: '敵を攻撃',
|
attackEnemy: '敵を攻撃',
|
||||||
recycleDebris: 'デブリをリサイクル',
|
recycleDebris: '5回デブリをリサイクル',
|
||||||
buildBattleships: 'バトルシップ10隻を建造',
|
buildBattleships: 'バトルシップ20隻を建造',
|
||||||
exploreDeepRuins: '深部遺跡を探索',
|
exploreDeepRuins: '深部遺跡を探索',
|
||||||
researchHyperspace: 'ハイパースペース技術を研究',
|
researchHyperspace: 'ハイパースペースドライブをレベル3まで研究',
|
||||||
defeatBoss: '謎の敵を撃破',
|
defeatBoss: '古代の守護者を撃破',
|
||||||
colonizeSpecial: '特別な場所を植民地化',
|
colonizeSpecial: '特別な場所を植民地化',
|
||||||
|
accumulateWealth: '総資源100万を蓄積',
|
||||||
continueDevelopment: '発展を継続'
|
continueDevelopment: '発展を継続'
|
||||||
},
|
},
|
||||||
dialogues: {
|
dialogues: {
|
||||||
|
|||||||
@@ -388,6 +388,7 @@ export default {
|
|||||||
title: '건물',
|
title: '건물',
|
||||||
spaceUsage: '공간 사용',
|
spaceUsage: '공간 사용',
|
||||||
upgradeCost: '업그레이드 비용',
|
upgradeCost: '업그레이드 비용',
|
||||||
|
build: '건설',
|
||||||
upgrade: '업그레이드',
|
upgrade: '업그레이드',
|
||||||
maxLevelReached: '최대 레벨 도달',
|
maxLevelReached: '최대 레벨 도달',
|
||||||
requirementsNotMet: '요구 사항 미충족',
|
requirementsNotMet: '요구 사항 미충족',
|
||||||
@@ -408,9 +409,19 @@ export default {
|
|||||||
researchFailedMessage: '자원이 충분한지, 전제 조건이 충족되었는지, 또는 다른 연구 작업이 있는지 확인하세요.'
|
researchFailedMessage: '자원이 충분한지, 전제 조건이 충족되었는지, 또는 다른 연구 작업이 있는지 확인하세요.'
|
||||||
},
|
},
|
||||||
shipyard: {
|
shipyard: {
|
||||||
|
attack: '공격력',
|
||||||
|
shield: '실드',
|
||||||
|
armor: '장갑',
|
||||||
speed: '속도',
|
speed: '속도',
|
||||||
cargoCapacity: '화물 용량',
|
cargoCapacity: '화물 용량',
|
||||||
fuelConsumption: '연료 소비'
|
fuelConsumption: '연료 소비',
|
||||||
|
buildCost: '건조 비용',
|
||||||
|
buildTime: '건조 시간',
|
||||||
|
perUnit: '단위당',
|
||||||
|
batchCalculator: '일괄 계산기',
|
||||||
|
quantity: '수량',
|
||||||
|
totalCost: '총 비용',
|
||||||
|
totalTime: '총 시간'
|
||||||
},
|
},
|
||||||
shipyardView: {
|
shipyardView: {
|
||||||
title: '조선소',
|
title: '조선소',
|
||||||
@@ -521,6 +532,8 @@ export default {
|
|||||||
},
|
},
|
||||||
recycle: '회수',
|
recycle: '회수',
|
||||||
destroy: '행성 파괴',
|
destroy: '행성 파괴',
|
||||||
|
harvestDarkMatter: '암흑 물질 수확',
|
||||||
|
station: '주둔',
|
||||||
transportResources: '자원 수송',
|
transportResources: '자원 수송',
|
||||||
totalCargoCapacity: '총 적재량',
|
totalCargoCapacity: '총 적재량',
|
||||||
used: '사용됨',
|
used: '사용됨',
|
||||||
@@ -1784,6 +1797,27 @@ export default {
|
|||||||
reputationBonusDesc: '동맹 {npcName}이(가) {targetNpc}에게 당신을 좋게 말하고 있습니다'
|
reputationBonusDesc: '동맹 {npcName}이(가) {targetNpc}에게 당신을 좋게 말하고 있습니다'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
webdav: {
|
||||||
|
connectionSuccess: 'WebDAV 연결 성공',
|
||||||
|
connectionSuccessDirectoryCreated: 'WebDAV 연결 성공, 저장 디렉토리 생성됨',
|
||||||
|
authFailed: '인증 실패, 사용자 이름과 비밀번호를 확인하세요',
|
||||||
|
directoryNotExist: '디렉토리가 존재하지 않으며 생성할 수 없습니다',
|
||||||
|
networkError: '네트워크 오류, 서버 주소와 네트워크를 확인하세요',
|
||||||
|
unknownError: '알 수 없는 오류',
|
||||||
|
uploadSuccess: '저장 파일 업로드 성공',
|
||||||
|
uploadFailed: '업로드 실패',
|
||||||
|
downloadSuccess: '저장 파일 다운로드 성공',
|
||||||
|
downloadFailed: '다운로드 실패',
|
||||||
|
noSaveFiles: '서버에 저장 파일이 없습니다',
|
||||||
|
fileListSuccess: '저장 파일 목록 가져오기 성공',
|
||||||
|
fileListFailed: '저장 파일 목록 가져오기 실패',
|
||||||
|
deleteSuccess: '저장 파일 삭제 성공',
|
||||||
|
deleteFailed: '삭제 실패',
|
||||||
|
serverError: '서버 오류',
|
||||||
|
notConfigured: 'WebDAV가 구성되지 않았습니다',
|
||||||
|
invalidUrl: '잘못된 WebDAV URL',
|
||||||
|
timeout: '연결 시간 초과'
|
||||||
|
},
|
||||||
campaign: {
|
campaign: {
|
||||||
name: '캠페인',
|
name: '캠페인',
|
||||||
description: '신비로운 은하를 탐험하고 고대의 비밀을 밝혀내세요',
|
description: '신비로운 은하를 탐험하고 고대의 비밀을 밝혀내세요',
|
||||||
@@ -1810,6 +1844,8 @@ export default {
|
|||||||
branchUnlocked: '새로운 스토리 분기가 해금되었습니다!'
|
branchUnlocked: '새로운 스토리 분기가 해금되었습니다!'
|
||||||
},
|
},
|
||||||
dialogue: {
|
dialogue: {
|
||||||
|
title: '스토리 대화',
|
||||||
|
description: '캠페인 스토리 대화 내용',
|
||||||
skip: '건너뛰기',
|
skip: '건너뛰기',
|
||||||
continue: '계속',
|
continue: '계속',
|
||||||
finish: '완료',
|
finish: '완료',
|
||||||
@@ -1922,25 +1958,42 @@ export default {
|
|||||||
spyAnyNPC: 'NPC 행성 정찰',
|
spyAnyNPC: 'NPC 행성 정찰',
|
||||||
sendGiftToNPC: 'NPC에게 선물 보내기',
|
sendGiftToNPC: 'NPC에게 선물 보내기',
|
||||||
researchAstrophysics: '천체물리학을 레벨 1까지 연구',
|
researchAstrophysics: '천체물리학을 레벨 1까지 연구',
|
||||||
|
researchAstrophysicsHigher: '천체물리학을 레벨 3까지 연구',
|
||||||
buildColonyShip: '식민선 건조',
|
buildColonyShip: '식민선 건조',
|
||||||
colonizeNewPlanet: '새 행성 식민지화',
|
colonizeNewPlanet: '새 행성 식민지화',
|
||||||
completeExpedition: '탐험 임무 완료',
|
colonizeMultiple: '5개 행성 식민지화',
|
||||||
|
completeExpedition: '3회 탐험 임무 완료',
|
||||||
|
expeditionDeepSpace: '2회 심우주 탐험 완료',
|
||||||
|
expeditionUncharted: '1회 미지 영역 탐험',
|
||||||
|
expeditionDangerous: '3회 위험 성운 탐험 완료',
|
||||||
discoverRuins: '고대 유적 발견',
|
discoverRuins: '고대 유적 발견',
|
||||||
researchComputer: '컴퓨터 기술을 레벨 4까지 연구',
|
researchComputer: '컴퓨터 기술을 레벨 4까지 연구',
|
||||||
|
researchImpulse: '임펄스 드라이브를 레벨 3까지 연구',
|
||||||
|
researchLaser: '레이저 기술을 레벨 5까지 연구',
|
||||||
|
researchIntergalactic: '컴퓨터 기술을 레벨 10까지 연구',
|
||||||
|
researchGraviton: '그래비톤 기술을 레벨 1까지 연구',
|
||||||
improveRelation: 'NPC와의 관계 개선',
|
improveRelation: 'NPC와의 관계 개선',
|
||||||
reachFriendly: 'NPC와 우호 상태 도달',
|
reachFriendly: 'NPC와 우호 상태 도달',
|
||||||
spyHostileNPC: '적대 NPC 정찰',
|
reachFriendlyRelation: '임의의 NPC와 우호 상태 도달',
|
||||||
|
sendMultipleGifts: 'NPC에게 3회 선물 보내기',
|
||||||
|
spyHostileNPC: '적대 NPC 2곳 정찰',
|
||||||
formAlliance: '우호 NPC와 동맹 체결',
|
formAlliance: '우호 NPC와 동맹 체결',
|
||||||
buildDefenses: '방어 시설 건설',
|
buildDefenses: '방어 시설 건설',
|
||||||
|
buildMissileSilo: '미사일 사일로를 레벨 2로 건설',
|
||||||
|
buildCruisers: '순양함 10대 건조',
|
||||||
winDefenseBattle: '방어전 승리',
|
winDefenseBattle: '방어전 승리',
|
||||||
|
defendAgainstAttack: '1회 공격 방어 성공',
|
||||||
spyEnemyPlanet: '적 행성 정찰',
|
spyEnemyPlanet: '적 행성 정찰',
|
||||||
|
spyEnemyPlanets: '적 행성 5곳 정찰',
|
||||||
|
winAttackBattles: '3회 공격전 승리',
|
||||||
attackEnemy: '적 공격',
|
attackEnemy: '적 공격',
|
||||||
recycleDebris: '잔해 재활용',
|
recycleDebris: '5회 잔해 재활용',
|
||||||
buildBattleships: '전함 10대 건조',
|
buildBattleships: '전함 20대 건조',
|
||||||
exploreDeepRuins: '깊은 유적 탐험',
|
exploreDeepRuins: '깊은 유적 탐험',
|
||||||
researchHyperspace: '하이퍼스페이스 기술 연구',
|
researchHyperspace: '하이퍼스페이스 드라이브를 레벨 3까지 연구',
|
||||||
defeatBoss: '신비한 적 격파',
|
defeatBoss: '고대 수호자 격파',
|
||||||
colonizeSpecial: '특별한 장소 식민지화',
|
colonizeSpecial: '특별한 장소 식민지화',
|
||||||
|
accumulateWealth: '총 자원 100만 축적',
|
||||||
continueDevelopment: '개발 계속'
|
continueDevelopment: '개발 계속'
|
||||||
},
|
},
|
||||||
dialogues: {
|
dialogues: {
|
||||||
|
|||||||
@@ -161,6 +161,7 @@ export default {
|
|||||||
totalCost: 'Общая стоимость',
|
totalCost: 'Общая стоимость',
|
||||||
totalPoints: 'Общие очки',
|
totalPoints: 'Общие очки',
|
||||||
levelRange: 'Диапазон уровней',
|
levelRange: 'Диапазон уровней',
|
||||||
|
researchSpeedBonus: 'Бонус скорости исследований',
|
||||||
capacity: 'Capacity/Effect',
|
capacity: 'Capacity/Effect',
|
||||||
storageCapacity: 'Capacity',
|
storageCapacity: 'Capacity',
|
||||||
energyProduction: 'Energy Production',
|
energyProduction: 'Energy Production',
|
||||||
@@ -269,6 +270,9 @@ export default {
|
|||||||
},
|
},
|
||||||
research: {
|
research: {
|
||||||
researchTime: 'Время исследования',
|
researchTime: 'Время исследования',
|
||||||
|
levelRange: 'Диапазон уровней',
|
||||||
|
totalCost: 'Общая стоимость',
|
||||||
|
totalPoints: 'Всего очков',
|
||||||
capacity: 'Capacity/Effect',
|
capacity: 'Capacity/Effect',
|
||||||
storageCapacity: 'Capacity',
|
storageCapacity: 'Capacity',
|
||||||
energyProduction: 'Energy Production',
|
energyProduction: 'Energy Production',
|
||||||
@@ -402,6 +406,7 @@ export default {
|
|||||||
spaceUsage: 'Использование полей',
|
spaceUsage: 'Использование полей',
|
||||||
level: 'Уровень',
|
level: 'Уровень',
|
||||||
upgradeCost: 'Стоимость улучшения',
|
upgradeCost: 'Стоимость улучшения',
|
||||||
|
build: 'Построить',
|
||||||
upgrade: 'Улучшить',
|
upgrade: 'Улучшить',
|
||||||
maxLevelReached: 'Достигнут максимальный уровень',
|
maxLevelReached: 'Достигнут максимальный уровень',
|
||||||
requirementsNotMet: 'Требования не выполнены',
|
requirementsNotMet: 'Требования не выполнены',
|
||||||
@@ -423,9 +428,19 @@ export default {
|
|||||||
'Пожалуйста, проверьте, достаточно ли у вас ресурсов, выполнены ли предварительные условия или нет других исследовательских задач.'
|
'Пожалуйста, проверьте, достаточно ли у вас ресурсов, выполнены ли предварительные условия или нет других исследовательских задач.'
|
||||||
},
|
},
|
||||||
shipyard: {
|
shipyard: {
|
||||||
|
attack: 'Атака',
|
||||||
|
shield: 'Щит',
|
||||||
|
armor: 'Броня',
|
||||||
speed: 'Скорость',
|
speed: 'Скорость',
|
||||||
cargoCapacity: 'Грузоподъёмность',
|
cargoCapacity: 'Грузоподъёмность',
|
||||||
fuelConsumption: 'Расход топлива'
|
fuelConsumption: 'Расход топлива',
|
||||||
|
buildCost: 'Стоимость постройки',
|
||||||
|
buildTime: 'Время постройки',
|
||||||
|
perUnit: 'За единицу',
|
||||||
|
batchCalculator: 'Калькулятор партии',
|
||||||
|
quantity: 'Количество',
|
||||||
|
totalCost: 'Общая стоимость',
|
||||||
|
totalTime: 'Общее время'
|
||||||
},
|
},
|
||||||
shipyardView: {
|
shipyardView: {
|
||||||
title: 'Верфь',
|
title: 'Верфь',
|
||||||
@@ -607,7 +622,9 @@ export default {
|
|||||||
jumpGateSuccessMessage: 'Флот мгновенно переброшен к {target}',
|
jumpGateSuccessMessage: 'Флот мгновенно переброшен к {target}',
|
||||||
jumpGateFailed: 'Переброска через ворота не удалась',
|
jumpGateFailed: 'Переброска через ворота не удалась',
|
||||||
jumpGateFailedMessage: 'Проверьте состояние ворот и конфигурацию флота',
|
jumpGateFailedMessage: 'Проверьте состояние ворот и конфигурацию флота',
|
||||||
destroy: 'Уничтожение'
|
destroy: 'Уничтожение',
|
||||||
|
harvestDarkMatter: 'Сбор тёмной материи',
|
||||||
|
station: 'Расположение'
|
||||||
},
|
},
|
||||||
officersView: {
|
officersView: {
|
||||||
title: 'Офицеры',
|
title: 'Офицеры',
|
||||||
@@ -1836,6 +1853,27 @@ export default {
|
|||||||
reputationBonusDesc: 'Ваш союзник {npcName} хорошо отзывается о вас {targetNpc}'
|
reputationBonusDesc: 'Ваш союзник {npcName} хорошо отзывается о вас {targetNpc}'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
webdav: {
|
||||||
|
connectionSuccess: 'Подключение WebDAV успешно',
|
||||||
|
connectionSuccessDirectoryCreated: 'Подключение WebDAV успешно, каталог сохранений создан',
|
||||||
|
authFailed: 'Ошибка аутентификации, проверьте имя пользователя и пароль',
|
||||||
|
directoryNotExist: 'Каталог не существует и не может быть создан',
|
||||||
|
networkError: 'Ошибка сети, проверьте адрес сервера и сеть',
|
||||||
|
unknownError: 'Неизвестная ошибка',
|
||||||
|
uploadSuccess: 'Сохранение успешно загружено',
|
||||||
|
uploadFailed: 'Ошибка загрузки',
|
||||||
|
downloadSuccess: 'Сохранение успешно скачано',
|
||||||
|
downloadFailed: 'Ошибка скачивания',
|
||||||
|
noSaveFiles: 'На сервере нет сохранений',
|
||||||
|
fileListSuccess: 'Список сохранений получен',
|
||||||
|
fileListFailed: 'Ошибка получения списка сохранений',
|
||||||
|
deleteSuccess: 'Сохранение успешно удалено',
|
||||||
|
deleteFailed: 'Ошибка удаления',
|
||||||
|
serverError: 'Ошибка сервера',
|
||||||
|
notConfigured: 'WebDAV не настроен',
|
||||||
|
invalidUrl: 'Недействительный URL WebDAV',
|
||||||
|
timeout: 'Превышено время ожидания'
|
||||||
|
},
|
||||||
campaign: {
|
campaign: {
|
||||||
name: 'Кампания',
|
name: 'Кампания',
|
||||||
description: 'Исследуйте загадочную галактику и раскройте древние тайны',
|
description: 'Исследуйте загадочную галактику и раскройте древние тайны',
|
||||||
@@ -1863,6 +1901,8 @@ export default {
|
|||||||
branchUnlocked: 'Новая сюжетная ветка разблокирована!'
|
branchUnlocked: 'Новая сюжетная ветка разблокирована!'
|
||||||
},
|
},
|
||||||
dialogue: {
|
dialogue: {
|
||||||
|
title: 'Сюжетный диалог',
|
||||||
|
description: 'Содержание сюжетного диалога кампании',
|
||||||
skip: 'Пропустить',
|
skip: 'Пропустить',
|
||||||
continue: 'Продолжить',
|
continue: 'Продолжить',
|
||||||
finish: 'Завершить',
|
finish: 'Завершить',
|
||||||
@@ -1975,25 +2015,42 @@ export default {
|
|||||||
spyAnyNPC: 'Разведать планету NPC',
|
spyAnyNPC: 'Разведать планету NPC',
|
||||||
sendGiftToNPC: 'Отправить подарок NPC',
|
sendGiftToNPC: 'Отправить подарок NPC',
|
||||||
researchAstrophysics: 'Исследовать Астрофизику до уровня 1',
|
researchAstrophysics: 'Исследовать Астрофизику до уровня 1',
|
||||||
|
researchAstrophysicsHigher: 'Исследовать Астрофизику до уровня 3',
|
||||||
buildColonyShip: 'Построить Колониальный корабль',
|
buildColonyShip: 'Построить Колониальный корабль',
|
||||||
colonizeNewPlanet: 'Колонизировать новую планету',
|
colonizeNewPlanet: 'Колонизировать новую планету',
|
||||||
completeExpedition: 'Завершить экспедиционную миссию',
|
colonizeMultiple: 'Колонизировать 5 планет',
|
||||||
|
completeExpedition: 'Завершить 3 экспедиционные миссии',
|
||||||
|
expeditionDeepSpace: 'Завершить 2 экспедиции в глубокий космос',
|
||||||
|
expeditionUncharted: 'Исследовать 1 неизвестный регион',
|
||||||
|
expeditionDangerous: 'Завершить 3 экспедиции в опасные туманности',
|
||||||
discoverRuins: 'Обнаружить древние руины',
|
discoverRuins: 'Обнаружить древние руины',
|
||||||
researchComputer: 'Исследовать Компьютерную технологию до уровня 4',
|
researchComputer: 'Исследовать Компьютерную технологию до уровня 4',
|
||||||
|
researchImpulse: 'Исследовать Импульсный двигатель до уровня 3',
|
||||||
|
researchLaser: 'Исследовать Лазерную технологию до уровня 5',
|
||||||
|
researchIntergalactic: 'Исследовать Компьютерную технологию до уровня 10',
|
||||||
|
researchGraviton: 'Исследовать Гравитонную технологию до уровня 1',
|
||||||
improveRelation: 'Улучшить отношения с NPC',
|
improveRelation: 'Улучшить отношения с NPC',
|
||||||
reachFriendly: 'Достичь дружеского статуса с NPC',
|
reachFriendly: 'Достичь дружеского статуса с NPC',
|
||||||
spyHostileNPC: 'Разведать враждебного NPC',
|
reachFriendlyRelation: 'Достичь дружеского статуса с любым NPC',
|
||||||
|
sendMultipleGifts: 'Отправить 3 подарка NPC',
|
||||||
|
spyHostileNPC: 'Разведать 2 враждебных NPC',
|
||||||
formAlliance: 'Заключить альянс с дружественным NPC',
|
formAlliance: 'Заключить альянс с дружественным NPC',
|
||||||
buildDefenses: 'Построить оборонительные сооружения',
|
buildDefenses: 'Построить оборонительные сооружения',
|
||||||
|
buildMissileSilo: 'Построить Ракетную шахту до уровня 2',
|
||||||
|
buildCruisers: 'Построить 10 Крейсеров',
|
||||||
winDefenseBattle: 'Выиграть оборонительное сражение',
|
winDefenseBattle: 'Выиграть оборонительное сражение',
|
||||||
|
defendAgainstAttack: 'Успешно отразить 1 атаку',
|
||||||
spyEnemyPlanet: 'Разведать вражескую планету',
|
spyEnemyPlanet: 'Разведать вражескую планету',
|
||||||
|
spyEnemyPlanets: 'Разведать 5 вражеских планет',
|
||||||
|
winAttackBattles: 'Выиграть 3 атакующих сражения',
|
||||||
attackEnemy: 'Атаковать врага',
|
attackEnemy: 'Атаковать врага',
|
||||||
recycleDebris: 'Переработать обломки',
|
recycleDebris: 'Переработать обломки 5 раз',
|
||||||
buildBattleships: 'Построить 10 Линкоров',
|
buildBattleships: 'Построить 20 Линкоров',
|
||||||
exploreDeepRuins: 'Исследовать глубокие руины',
|
exploreDeepRuins: 'Исследовать глубокие руины',
|
||||||
researchHyperspace: 'Исследовать Гиперпространственную технологию',
|
researchHyperspace: 'Исследовать Гиперпространственный двигатель до уровня 3',
|
||||||
defeatBoss: 'Победить загадочного врага',
|
defeatBoss: 'Победить Древнего Стража',
|
||||||
colonizeSpecial: 'Колонизировать особое место',
|
colonizeSpecial: 'Колонизировать особое место',
|
||||||
|
accumulateWealth: 'Накопить 1 миллион ресурсов',
|
||||||
continueDevelopment: 'Продолжить развитие'
|
continueDevelopment: 'Продолжить развитие'
|
||||||
},
|
},
|
||||||
dialogues: {
|
dialogues: {
|
||||||
|
|||||||
@@ -32,7 +32,8 @@ export default {
|
|||||||
exitConfirmTitle: '退出游戏',
|
exitConfirmTitle: '退出游戏',
|
||||||
exitConfirmMessage: '确定要退出游戏吗?游戏进度会自动保存。',
|
exitConfirmMessage: '确定要退出游戏吗?游戏进度会自动保存。',
|
||||||
points: '积分',
|
points: '积分',
|
||||||
retry: '重试'
|
retry: '重试',
|
||||||
|
playerName: '玩家名称'
|
||||||
},
|
},
|
||||||
errors: {
|
errors: {
|
||||||
requirementsNotMet: '不满足前置条件',
|
requirementsNotMet: '不满足前置条件',
|
||||||
@@ -264,6 +265,9 @@ export default {
|
|||||||
},
|
},
|
||||||
research: {
|
research: {
|
||||||
researchTime: '研究时间',
|
researchTime: '研究时间',
|
||||||
|
levelRange: '等级范围',
|
||||||
|
totalCost: '总成本',
|
||||||
|
totalPoints: '总积分',
|
||||||
|
|
||||||
attackBonus: '攻击加成',
|
attackBonus: '攻击加成',
|
||||||
shieldBonus: '护盾加成',
|
shieldBonus: '护盾加成',
|
||||||
@@ -384,6 +388,7 @@ export default {
|
|||||||
title: '建筑',
|
title: '建筑',
|
||||||
spaceUsage: '占用空间',
|
spaceUsage: '占用空间',
|
||||||
upgradeCost: '升级消耗',
|
upgradeCost: '升级消耗',
|
||||||
|
build: '建造',
|
||||||
upgrade: '升级',
|
upgrade: '升级',
|
||||||
maxLevelReached: '等级已满',
|
maxLevelReached: '等级已满',
|
||||||
requirementsNotMet: '条件不足',
|
requirementsNotMet: '条件不足',
|
||||||
@@ -404,9 +409,19 @@ export default {
|
|||||||
researchFailedMessage: '请检查资源是否足够、前置条件是否满足,或是否有其他研究任务。'
|
researchFailedMessage: '请检查资源是否足够、前置条件是否满足,或是否有其他研究任务。'
|
||||||
},
|
},
|
||||||
shipyard: {
|
shipyard: {
|
||||||
|
attack: '攻击力',
|
||||||
|
shield: '护盾',
|
||||||
|
armor: '装甲',
|
||||||
speed: '速度',
|
speed: '速度',
|
||||||
cargoCapacity: '载货量',
|
cargoCapacity: '载货量',
|
||||||
fuelConsumption: '燃料消耗'
|
fuelConsumption: '燃料消耗',
|
||||||
|
buildCost: '建造成本',
|
||||||
|
buildTime: '建造时间',
|
||||||
|
perUnit: '单位',
|
||||||
|
batchCalculator: '批量计算器',
|
||||||
|
quantity: '数量',
|
||||||
|
totalCost: '总成本',
|
||||||
|
totalTime: '总时间'
|
||||||
},
|
},
|
||||||
shipyardView: {
|
shipyardView: {
|
||||||
title: '船坞',
|
title: '船坞',
|
||||||
@@ -511,6 +526,8 @@ export default {
|
|||||||
},
|
},
|
||||||
recycle: '回收',
|
recycle: '回收',
|
||||||
destroy: '行星毁灭',
|
destroy: '行星毁灭',
|
||||||
|
harvestDarkMatter: '暗物质采集',
|
||||||
|
station: '驻守协防',
|
||||||
transportResources: '运输资源',
|
transportResources: '运输资源',
|
||||||
totalCargoCapacity: '总载货量',
|
totalCargoCapacity: '总载货量',
|
||||||
used: '已用',
|
used: '已用',
|
||||||
@@ -1782,6 +1799,8 @@ export default {
|
|||||||
branchUnlocked: '新的故事分支已解锁!'
|
branchUnlocked: '新的故事分支已解锁!'
|
||||||
},
|
},
|
||||||
dialogue: {
|
dialogue: {
|
||||||
|
title: '剧情对话',
|
||||||
|
description: '战役剧情对话内容',
|
||||||
skip: '跳过',
|
skip: '跳过',
|
||||||
continue: '继续',
|
continue: '继续',
|
||||||
finish: '完成',
|
finish: '完成',
|
||||||
@@ -1966,25 +1985,42 @@ export default {
|
|||||||
spyAnyNPC: '侦查任意NPC星球',
|
spyAnyNPC: '侦查任意NPC星球',
|
||||||
sendGiftToNPC: '向任意NPC送礼',
|
sendGiftToNPC: '向任意NPC送礼',
|
||||||
researchAstrophysics: '研究天体物理学到1级',
|
researchAstrophysics: '研究天体物理学到1级',
|
||||||
|
researchAstrophysicsHigher: '研究天体物理学到3级',
|
||||||
buildColonyShip: '建造殖民船',
|
buildColonyShip: '建造殖民船',
|
||||||
colonizeNewPlanet: '殖民新星球',
|
colonizeNewPlanet: '殖民新星球',
|
||||||
completeExpedition: '完成远征任务',
|
colonizeMultiple: '殖民5个星球',
|
||||||
|
completeExpedition: '完成3次远征任务',
|
||||||
|
expeditionDeepSpace: '完成2次深空远征',
|
||||||
|
expeditionUncharted: '探索1次未知区域',
|
||||||
|
expeditionDangerous: '完成3次危险星云远征',
|
||||||
discoverRuins: '发现古代遗迹',
|
discoverRuins: '发现古代遗迹',
|
||||||
researchComputer: '研究电脑技术到4级',
|
researchComputer: '研究电脑技术到4级',
|
||||||
|
researchImpulse: '研究脉冲驱动到3级',
|
||||||
|
researchLaser: '研究激光技术到5级',
|
||||||
|
researchIntergalactic: '研究电脑技术到10级',
|
||||||
|
researchGraviton: '研究引力子技术到1级',
|
||||||
improveRelation: '提升与NPC的关系',
|
improveRelation: '提升与NPC的关系',
|
||||||
reachFriendly: '与NPC达到友好关系',
|
reachFriendly: '与NPC达到友好关系',
|
||||||
spyHostileNPC: '侦查敌对NPC',
|
reachFriendlyRelation: '与任意NPC达到友好关系',
|
||||||
|
sendMultipleGifts: '向NPC发送3次礼物',
|
||||||
|
spyHostileNPC: '侦查2个敌对NPC',
|
||||||
formAlliance: '与友好NPC结盟',
|
formAlliance: '与友好NPC结盟',
|
||||||
buildDefenses: '建造防御设施',
|
buildDefenses: '建造防御设施',
|
||||||
|
buildMissileSilo: '建造导弹发射井到2级',
|
||||||
|
buildCruisers: '建造10艘巡洋舰',
|
||||||
winDefenseBattle: '赢得防御战斗',
|
winDefenseBattle: '赢得防御战斗',
|
||||||
|
defendAgainstAttack: '成功防御1次攻击',
|
||||||
spyEnemyPlanet: '侦查敌方星球',
|
spyEnemyPlanet: '侦查敌方星球',
|
||||||
|
spyEnemyPlanets: '侦查5个敌方星球',
|
||||||
|
winAttackBattles: '赢得3次进攻战斗',
|
||||||
attackEnemy: '攻击敌方',
|
attackEnemy: '攻击敌方',
|
||||||
recycleDebris: '回收残骸',
|
recycleDebris: '回收5次残骸场',
|
||||||
buildBattleships: '建造10艘战列舰',
|
buildBattleships: '建造20艘战列舰',
|
||||||
exploreDeepRuins: '探索遗迹深处',
|
exploreDeepRuins: '探索遗迹深处',
|
||||||
researchHyperspace: '研究超空间技术',
|
researchHyperspace: '研究超空间驱动到3级',
|
||||||
defeatBoss: '击败神秘敌人',
|
defeatBoss: '击败古代守护者',
|
||||||
colonizeSpecial: '殖民特殊位置',
|
colonizeSpecial: '殖民特殊位置',
|
||||||
|
accumulateWealth: '积累总资源达到100万',
|
||||||
continueDevelopment: '继续发展'
|
continueDevelopment: '继续发展'
|
||||||
},
|
},
|
||||||
dialogues: {
|
dialogues: {
|
||||||
@@ -2075,5 +2111,31 @@ export default {
|
|||||||
epilogue_1: '银河系广阔无垠,还有无数秘密等待你去发现...'
|
epilogue_1: '银河系广阔无垠,还有无数秘密等待你去发现...'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
webdav: {
|
||||||
|
// 连接相关
|
||||||
|
connectionSuccess: 'WebDAV 连接成功',
|
||||||
|
connectionSuccessDirectoryCreated: 'WebDAV 连接成功,已创建存档目录',
|
||||||
|
authFailed: '认证失败,请检查用户名和密码',
|
||||||
|
directoryNotExist: '目录不存在且无法创建',
|
||||||
|
connectionFailedHttp: '连接失败: HTTP {status}',
|
||||||
|
networkError: '网络错误,可能是 CORS 限制。建议使用支持 CORS 的 WebDAV 服务或通过代理访问。',
|
||||||
|
connectionError: '连接错误: {error}',
|
||||||
|
// 上传相关
|
||||||
|
uploadSuccess: '上传成功',
|
||||||
|
noWritePermission: '没有写入权限',
|
||||||
|
insufficientStorage: '存储空间不足',
|
||||||
|
uploadFailedHttp: '上传失败: HTTP {status}',
|
||||||
|
uploadError: '上传错误: {error}',
|
||||||
|
// 下载相关
|
||||||
|
fileNotExist: '文件不存在',
|
||||||
|
downloadFailedHttp: '下载失败: HTTP {status}',
|
||||||
|
downloadError: '下载错误: {error}',
|
||||||
|
// 列表相关
|
||||||
|
listFailedHttp: '获取文件列表失败: HTTP {status}',
|
||||||
|
listError: '获取文件列表错误: {error}',
|
||||||
|
// 删除相关
|
||||||
|
deleteFailedHttp: '删除失败: HTTP {status}',
|
||||||
|
deleteError: '删除错误: {error}'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -270,6 +270,9 @@ export default {
|
|||||||
},
|
},
|
||||||
research: {
|
research: {
|
||||||
researchTime: '研究時間',
|
researchTime: '研究時間',
|
||||||
|
levelRange: '等級範圍',
|
||||||
|
totalCost: '總成本',
|
||||||
|
totalPoints: '總積分',
|
||||||
capacity: '容量/效果',
|
capacity: '容量/效果',
|
||||||
attackBonus: '攻擊加成',
|
attackBonus: '攻擊加成',
|
||||||
shieldBonus: '護盾加成',
|
shieldBonus: '護盾加成',
|
||||||
@@ -414,11 +417,20 @@ export default {
|
|||||||
researchFailedMessage: '請檢查資源是否足夠、前置條件是否滿足,或是否有其他研究任務。'
|
researchFailedMessage: '請檢查資源是否足夠、前置條件是否滿足,或是否有其他研究任務。'
|
||||||
},
|
},
|
||||||
shipyard: {
|
shipyard: {
|
||||||
missileAttack: '導彈攻擊',
|
attack: '攻擊力',
|
||||||
|
shield: '護盾',
|
||||||
armor: '裝甲',
|
armor: '裝甲',
|
||||||
|
missileAttack: '導彈攻擊',
|
||||||
speed: '速度',
|
speed: '速度',
|
||||||
cargoCapacity: '載貨量',
|
cargoCapacity: '載貨量',
|
||||||
fuelConsumption: '燃料消耗',
|
fuelConsumption: '燃料消耗',
|
||||||
|
buildCost: '建造成本',
|
||||||
|
buildTime: '建造時間',
|
||||||
|
perUnit: '單位',
|
||||||
|
batchCalculator: '批量計算器',
|
||||||
|
quantity: '數量',
|
||||||
|
totalCost: '總成本',
|
||||||
|
totalTime: '總時間',
|
||||||
build: '建造'
|
build: '建造'
|
||||||
},
|
},
|
||||||
shipyardView: {
|
shipyardView: {
|
||||||
@@ -530,6 +542,8 @@ export default {
|
|||||||
},
|
},
|
||||||
recycle: '回收',
|
recycle: '回收',
|
||||||
destroy: '行星毀滅',
|
destroy: '行星毀滅',
|
||||||
|
harvestDarkMatter: '暗物質採集',
|
||||||
|
station: '駐守協防',
|
||||||
transportResources: '運輸資源',
|
transportResources: '運輸資源',
|
||||||
totalCargoCapacity: '總載貨量',
|
totalCargoCapacity: '總載貨量',
|
||||||
used: '已用',
|
used: '已用',
|
||||||
@@ -1933,6 +1947,27 @@ export default {
|
|||||||
reputationBonusDesc: '你的盟友{npcName}向{targetNpc}說了你的好話'
|
reputationBonusDesc: '你的盟友{npcName}向{targetNpc}說了你的好話'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
webdav: {
|
||||||
|
connectionSuccess: 'WebDAV 連線成功',
|
||||||
|
connectionSuccessDirectoryCreated: 'WebDAV 連線成功,已建立存檔目錄',
|
||||||
|
authFailed: '認證失敗,請檢查使用者名稱和密碼',
|
||||||
|
directoryNotExist: '目錄不存在且無法建立',
|
||||||
|
networkError: '網路錯誤,請檢查伺服器位址和網路連線',
|
||||||
|
unknownError: '未知錯誤',
|
||||||
|
uploadSuccess: '存檔上傳成功',
|
||||||
|
uploadFailed: '上傳失敗',
|
||||||
|
downloadSuccess: '存檔下載成功',
|
||||||
|
downloadFailed: '下載失敗',
|
||||||
|
noSaveFiles: '伺服器上沒有存檔',
|
||||||
|
fileListSuccess: '取得存檔列表成功',
|
||||||
|
fileListFailed: '取得存檔列表失敗',
|
||||||
|
deleteSuccess: '存檔刪除成功',
|
||||||
|
deleteFailed: '刪除失敗',
|
||||||
|
serverError: '伺服器錯誤',
|
||||||
|
notConfigured: 'WebDAV 未設定',
|
||||||
|
invalidUrl: '無效的 WebDAV URL',
|
||||||
|
timeout: '連線逾時'
|
||||||
|
},
|
||||||
campaign: {
|
campaign: {
|
||||||
name: '戰役',
|
name: '戰役',
|
||||||
description: '探索神秘的銀河系,揭開古代文明的秘密',
|
description: '探索神秘的銀河系,揭開古代文明的秘密',
|
||||||
@@ -1960,6 +1995,8 @@ export default {
|
|||||||
branchUnlocked: '新的故事分支已解鎖!'
|
branchUnlocked: '新的故事分支已解鎖!'
|
||||||
},
|
},
|
||||||
dialogue: {
|
dialogue: {
|
||||||
|
title: '劇情對話',
|
||||||
|
description: '戰役劇情對話內容',
|
||||||
skip: '跳過',
|
skip: '跳過',
|
||||||
continue: '繼續',
|
continue: '繼續',
|
||||||
finish: '完成',
|
finish: '完成',
|
||||||
@@ -2144,25 +2181,42 @@ export default {
|
|||||||
spyAnyNPC: '偵查任意NPC星球',
|
spyAnyNPC: '偵查任意NPC星球',
|
||||||
sendGiftToNPC: '向任意NPC送禮',
|
sendGiftToNPC: '向任意NPC送禮',
|
||||||
researchAstrophysics: '研究天體物理學到1級',
|
researchAstrophysics: '研究天體物理學到1級',
|
||||||
|
researchAstrophysicsHigher: '研究天體物理學到3級',
|
||||||
buildColonyShip: '建造殖民船',
|
buildColonyShip: '建造殖民船',
|
||||||
colonizeNewPlanet: '殖民新星球',
|
colonizeNewPlanet: '殖民新星球',
|
||||||
completeExpedition: '完成遠征任務',
|
colonizeMultiple: '殖民5個星球',
|
||||||
|
completeExpedition: '完成3次遠征任務',
|
||||||
|
expeditionDeepSpace: '完成2次深空遠征',
|
||||||
|
expeditionUncharted: '探索1次未知區域',
|
||||||
|
expeditionDangerous: '完成3次危險星雲遠征',
|
||||||
discoverRuins: '發現古代遺跡',
|
discoverRuins: '發現古代遺跡',
|
||||||
researchComputer: '研究電腦技術到4級',
|
researchComputer: '研究電腦技術到4級',
|
||||||
|
researchImpulse: '研究脈衝驅動到3級',
|
||||||
|
researchLaser: '研究雷射技術到5級',
|
||||||
|
researchIntergalactic: '研究電腦技術到10級',
|
||||||
|
researchGraviton: '研究引力子技術到1級',
|
||||||
improveRelation: '提升與NPC的關係',
|
improveRelation: '提升與NPC的關係',
|
||||||
reachFriendly: '與NPC達到友好關係',
|
reachFriendly: '與NPC達到友好關係',
|
||||||
spyHostileNPC: '偵查敵對NPC',
|
reachFriendlyRelation: '與任意NPC達到友好關係',
|
||||||
|
sendMultipleGifts: '向NPC發送3次禮物',
|
||||||
|
spyHostileNPC: '偵查2個敵對NPC',
|
||||||
formAlliance: '與友好NPC結盟',
|
formAlliance: '與友好NPC結盟',
|
||||||
buildDefenses: '建造防禦設施',
|
buildDefenses: '建造防禦設施',
|
||||||
|
buildMissileSilo: '建造飛彈發射井到2級',
|
||||||
|
buildCruisers: '建造10艘巡洋艦',
|
||||||
winDefenseBattle: '贏得防禦戰鬥',
|
winDefenseBattle: '贏得防禦戰鬥',
|
||||||
|
defendAgainstAttack: '成功防禦1次攻擊',
|
||||||
spyEnemyPlanet: '偵查敵方星球',
|
spyEnemyPlanet: '偵查敵方星球',
|
||||||
|
spyEnemyPlanets: '偵查5個敵方星球',
|
||||||
|
winAttackBattles: '贏得3次進攻戰鬥',
|
||||||
attackEnemy: '攻擊敵方',
|
attackEnemy: '攻擊敵方',
|
||||||
recycleDebris: '回收殘骸',
|
recycleDebris: '回收5次殘骸場',
|
||||||
buildBattleships: '建造10艘戰列艦',
|
buildBattleships: '建造20艘戰列艦',
|
||||||
exploreDeepRuins: '探索遺跡深處',
|
exploreDeepRuins: '探索遺跡深處',
|
||||||
researchHyperspace: '研究超空間技術',
|
researchHyperspace: '研究超空間驅動到3級',
|
||||||
defeatBoss: '擊敗神秘敵人',
|
defeatBoss: '擊敗古代守護者',
|
||||||
colonizeSpecial: '殖民特殊位置',
|
colonizeSpecial: '殖民特殊位置',
|
||||||
|
accumulateWealth: '積累總資源達到100萬',
|
||||||
continueDevelopment: '繼續發展'
|
continueDevelopment: '繼續發展'
|
||||||
},
|
},
|
||||||
dialogues: {
|
dialogues: {
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
/**
|
/**
|
||||||
* WebDAV 同步服务
|
* WebDAV 同步服务
|
||||||
* 支持将存档上传到 WebDAV 服务器(如坚果云、Nextcloud、NAS等)
|
* 支持将存档上传到 WebDAV 服务器(如坚果云、Nextcloud、NAS等)
|
||||||
|
* 注意:WebDAV 配置存储在 gameStore.webdavConfig 中,与用户数据一起持久化
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export interface WebDAVConfig {
|
import type { WebDAVConfig } from '@/types/game'
|
||||||
serverUrl: string // WebDAV 服务器地址,如 https://dav.jianguoyun.com/dav/
|
|
||||||
username: string // 用户名
|
// 重新导出类型以保持向后兼容
|
||||||
password: string // 密码或应用专用密码
|
export type { WebDAVConfig }
|
||||||
basePath: string // 存档存放路径,如 /ogame-saves/
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface WebDAVFile {
|
export interface WebDAVFile {
|
||||||
name: string
|
name: string
|
||||||
@@ -18,29 +17,47 @@ export interface WebDAVFile {
|
|||||||
isDirectory: boolean
|
isDirectory: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const STORAGE_KEY = 'ogame-webdav-config'
|
// WebDAV 消息 key(用于 i18n)
|
||||||
|
export const WebDAVMessageKey = {
|
||||||
|
// 连接相关
|
||||||
|
connectionSuccess: 'webdav.connectionSuccess',
|
||||||
|
connectionSuccessDirectoryCreated: 'webdav.connectionSuccessDirectoryCreated',
|
||||||
|
authFailed: 'webdav.authFailed',
|
||||||
|
directoryNotExist: 'webdav.directoryNotExist',
|
||||||
|
connectionFailedHttp: 'webdav.connectionFailedHttp',
|
||||||
|
networkError: 'webdav.networkError',
|
||||||
|
connectionError: 'webdav.connectionError',
|
||||||
|
|
||||||
// 获取保存的 WebDAV 配置
|
// 上传相关
|
||||||
export const getWebDAVConfig = (): WebDAVConfig | null => {
|
uploadSuccess: 'webdav.uploadSuccess',
|
||||||
try {
|
noWritePermission: 'webdav.noWritePermission',
|
||||||
const saved = localStorage.getItem(STORAGE_KEY)
|
insufficientStorage: 'webdav.insufficientStorage',
|
||||||
if (saved) {
|
uploadFailedHttp: 'webdav.uploadFailedHttp',
|
||||||
return JSON.parse(saved)
|
uploadError: 'webdav.uploadError',
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Failed to load WebDAV config:', e)
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
// 保存 WebDAV 配置
|
// 下载相关
|
||||||
export const saveWebDAVConfig = (config: WebDAVConfig): void => {
|
fileNotExist: 'webdav.fileNotExist',
|
||||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(config))
|
downloadFailedHttp: 'webdav.downloadFailedHttp',
|
||||||
}
|
downloadError: 'webdav.downloadError',
|
||||||
|
|
||||||
// 清除 WebDAV 配置
|
// 列表相关
|
||||||
export const clearWebDAVConfig = (): void => {
|
listFailedHttp: 'webdav.listFailedHttp',
|
||||||
localStorage.removeItem(STORAGE_KEY)
|
listError: 'webdav.listError',
|
||||||
|
|
||||||
|
// 删除相关
|
||||||
|
deleteFailedHttp: 'webdav.deleteFailedHttp',
|
||||||
|
deleteError: 'webdav.deleteError'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type WebDAVMessageKeyType = (typeof WebDAVMessageKey)[keyof typeof WebDAVMessageKey]
|
||||||
|
|
||||||
|
export interface WebDAVResult {
|
||||||
|
success: boolean
|
||||||
|
messageKey: WebDAVMessageKeyType
|
||||||
|
messageParams?: Record<string, string | number>
|
||||||
|
fileName?: string
|
||||||
|
data?: string
|
||||||
|
files?: WebDAVFile[]
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构建 Authorization header
|
// 构建 Authorization header
|
||||||
@@ -66,7 +83,7 @@ const normalizePath = (serverUrl: string, basePath: string, fileName?: string):
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 测试 WebDAV 连接
|
// 测试 WebDAV 连接
|
||||||
export const testWebDAVConnection = async (config: WebDAVConfig): Promise<{ success: boolean; message: string }> => {
|
export const testWebDAVConnection = async (config: WebDAVConfig): Promise<WebDAVResult> => {
|
||||||
try {
|
try {
|
||||||
const url = normalizePath(config.serverUrl, config.basePath)
|
const url = normalizePath(config.serverUrl, config.basePath)
|
||||||
|
|
||||||
@@ -80,33 +97,30 @@ export const testWebDAVConnection = async (config: WebDAVConfig): Promise<{ succ
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (response.ok || response.status === 207) {
|
if (response.ok || response.status === 207) {
|
||||||
return { success: true, message: 'WebDAV 连接成功' }
|
return { success: true, messageKey: WebDAVMessageKey.connectionSuccess }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.status === 401) {
|
if (response.status === 401) {
|
||||||
return { success: false, message: '认证失败,请检查用户名和密码' }
|
return { success: false, messageKey: WebDAVMessageKey.authFailed }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.status === 404) {
|
if (response.status === 404) {
|
||||||
// 尝试创建目录
|
// 尝试创建目录
|
||||||
const createResult = await createDirectory(config, config.basePath)
|
const createResult = await createDirectory(config, config.basePath)
|
||||||
if (createResult) {
|
if (createResult) {
|
||||||
return { success: true, message: 'WebDAV 连接成功,已创建存档目录' }
|
return { success: true, messageKey: WebDAVMessageKey.connectionSuccessDirectoryCreated }
|
||||||
}
|
}
|
||||||
return { success: false, message: '目录不存在且无法创建' }
|
return { success: false, messageKey: WebDAVMessageKey.directoryNotExist }
|
||||||
}
|
}
|
||||||
|
|
||||||
return { success: false, message: `连接失败: HTTP ${response.status}` }
|
return { success: false, messageKey: WebDAVMessageKey.connectionFailedHttp, messageParams: { status: response.status } }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = error instanceof Error ? error.message : String(error)
|
const errorMessage = error instanceof Error ? error.message : String(error)
|
||||||
// CORS 错误的处理
|
// CORS 错误的处理
|
||||||
if (message.includes('Failed to fetch') || message.includes('NetworkError')) {
|
if (errorMessage.includes('Failed to fetch') || errorMessage.includes('NetworkError')) {
|
||||||
return {
|
return { success: false, messageKey: WebDAVMessageKey.networkError }
|
||||||
success: false,
|
|
||||||
message: '网络错误,可能是 CORS 限制。建议使用支持 CORS 的 WebDAV 服务或通过代理访问。'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return { success: false, message: `连接错误: ${message}` }
|
return { success: false, messageKey: WebDAVMessageKey.connectionError, messageParams: { error: errorMessage } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,11 +144,7 @@ const createDirectory = async (config: WebDAVConfig, path: string): Promise<bool
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 上传存档到 WebDAV
|
// 上传存档到 WebDAV
|
||||||
export const uploadToWebDAV = async (
|
export const uploadToWebDAV = async (config: WebDAVConfig, data: string, fileName?: string): Promise<WebDAVResult> => {
|
||||||
config: WebDAVConfig,
|
|
||||||
data: string,
|
|
||||||
fileName?: string
|
|
||||||
): Promise<{ success: boolean; message: string; fileName?: string }> => {
|
|
||||||
try {
|
try {
|
||||||
// 生成带时间戳的文件名
|
// 生成带时间戳的文件名
|
||||||
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19)
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19)
|
||||||
@@ -151,25 +161,25 @@ export const uploadToWebDAV = async (
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (response.ok || response.status === 201 || response.status === 204) {
|
if (response.ok || response.status === 201 || response.status === 204) {
|
||||||
return { success: true, message: '上传成功', fileName: actualFileName }
|
return { success: true, messageKey: WebDAVMessageKey.uploadSuccess, fileName: actualFileName }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.status === 401) {
|
if (response.status === 401) {
|
||||||
return { success: false, message: '认证失败' }
|
return { success: false, messageKey: WebDAVMessageKey.authFailed }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.status === 403) {
|
if (response.status === 403) {
|
||||||
return { success: false, message: '没有写入权限' }
|
return { success: false, messageKey: WebDAVMessageKey.noWritePermission }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.status === 507) {
|
if (response.status === 507) {
|
||||||
return { success: false, message: '存储空间不足' }
|
return { success: false, messageKey: WebDAVMessageKey.insufficientStorage }
|
||||||
}
|
}
|
||||||
|
|
||||||
return { success: false, message: `上传失败: HTTP ${response.status}` }
|
return { success: false, messageKey: WebDAVMessageKey.uploadFailedHttp, messageParams: { status: response.status } }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = error instanceof Error ? error.message : String(error)
|
const errorMessage = error instanceof Error ? error.message : String(error)
|
||||||
return { success: false, message: `上传错误: ${message}` }
|
return { success: false, messageKey: WebDAVMessageKey.uploadError, messageParams: { error: errorMessage } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,7 +222,7 @@ const parsePropfindResponse = (xml: string, _basePath: string): WebDAVFile[] =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 列出 WebDAV 目录中的存档文件
|
// 列出 WebDAV 目录中的存档文件
|
||||||
export const listWebDAVFiles = async (config: WebDAVConfig): Promise<{ success: boolean; files?: WebDAVFile[]; message?: string }> => {
|
export const listWebDAVFiles = async (config: WebDAVConfig): Promise<WebDAVResult> => {
|
||||||
try {
|
try {
|
||||||
const url = normalizePath(config.serverUrl, config.basePath)
|
const url = normalizePath(config.serverUrl, config.basePath)
|
||||||
|
|
||||||
@@ -227,29 +237,26 @@ export const listWebDAVFiles = async (config: WebDAVConfig): Promise<{ success:
|
|||||||
|
|
||||||
if (!response.ok && response.status !== 207) {
|
if (!response.ok && response.status !== 207) {
|
||||||
if (response.status === 401) {
|
if (response.status === 401) {
|
||||||
return { success: false, message: '认证失败' }
|
return { success: false, messageKey: WebDAVMessageKey.authFailed }
|
||||||
}
|
}
|
||||||
if (response.status === 404) {
|
if (response.status === 404) {
|
||||||
return { success: false, message: '目录不存在' }
|
return { success: false, messageKey: WebDAVMessageKey.directoryNotExist }
|
||||||
}
|
}
|
||||||
return { success: false, message: `获取文件列表失败: HTTP ${response.status}` }
|
return { success: false, messageKey: WebDAVMessageKey.listFailedHttp, messageParams: { status: response.status } }
|
||||||
}
|
}
|
||||||
|
|
||||||
const xml = await response.text()
|
const xml = await response.text()
|
||||||
const files = parsePropfindResponse(xml, config.basePath)
|
const files = parsePropfindResponse(xml, config.basePath)
|
||||||
|
|
||||||
return { success: true, files }
|
return { success: true, messageKey: WebDAVMessageKey.connectionSuccess, files }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = error instanceof Error ? error.message : String(error)
|
const errorMessage = error instanceof Error ? error.message : String(error)
|
||||||
return { success: false, message: `获取文件列表错误: ${message}` }
|
return { success: false, messageKey: WebDAVMessageKey.listError, messageParams: { error: errorMessage } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 从 WebDAV 下载存档
|
// 从 WebDAV 下载存档
|
||||||
export const downloadFromWebDAV = async (
|
export const downloadFromWebDAV = async (config: WebDAVConfig, fileName: string): Promise<WebDAVResult> => {
|
||||||
config: WebDAVConfig,
|
|
||||||
fileName: string
|
|
||||||
): Promise<{ success: boolean; data?: string; message?: string }> => {
|
|
||||||
try {
|
try {
|
||||||
const url = normalizePath(config.serverUrl, config.basePath, fileName)
|
const url = normalizePath(config.serverUrl, config.basePath, fileName)
|
||||||
|
|
||||||
@@ -262,27 +269,24 @@ export const downloadFromWebDAV = async (
|
|||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
if (response.status === 401) {
|
if (response.status === 401) {
|
||||||
return { success: false, message: '认证失败' }
|
return { success: false, messageKey: WebDAVMessageKey.authFailed }
|
||||||
}
|
}
|
||||||
if (response.status === 404) {
|
if (response.status === 404) {
|
||||||
return { success: false, message: '文件不存在' }
|
return { success: false, messageKey: WebDAVMessageKey.fileNotExist }
|
||||||
}
|
}
|
||||||
return { success: false, message: `下载失败: HTTP ${response.status}` }
|
return { success: false, messageKey: WebDAVMessageKey.downloadFailedHttp, messageParams: { status: response.status } }
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await response.text()
|
const data = await response.text()
|
||||||
return { success: true, data }
|
return { success: true, messageKey: WebDAVMessageKey.connectionSuccess, data }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = error instanceof Error ? error.message : String(error)
|
const errorMessage = error instanceof Error ? error.message : String(error)
|
||||||
return { success: false, message: `下载错误: ${message}` }
|
return { success: false, messageKey: WebDAVMessageKey.downloadError, messageParams: { error: errorMessage } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除 WebDAV 文件
|
// 删除 WebDAV 文件
|
||||||
export const deleteFromWebDAV = async (
|
export const deleteFromWebDAV = async (config: WebDAVConfig, fileName: string): Promise<WebDAVResult> => {
|
||||||
config: WebDAVConfig,
|
|
||||||
fileName: string
|
|
||||||
): Promise<{ success: boolean; message?: string }> => {
|
|
||||||
try {
|
try {
|
||||||
const url = normalizePath(config.serverUrl, config.basePath, fileName)
|
const url = normalizePath(config.serverUrl, config.basePath, fileName)
|
||||||
|
|
||||||
@@ -294,20 +298,20 @@ export const deleteFromWebDAV = async (
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (response.ok || response.status === 204) {
|
if (response.ok || response.status === 204) {
|
||||||
return { success: true }
|
return { success: true, messageKey: WebDAVMessageKey.connectionSuccess }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.status === 401) {
|
if (response.status === 401) {
|
||||||
return { success: false, message: '认证失败' }
|
return { success: false, messageKey: WebDAVMessageKey.authFailed }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.status === 404) {
|
if (response.status === 404) {
|
||||||
return { success: true } // 文件不存在也视为删除成功
|
return { success: true, messageKey: WebDAVMessageKey.connectionSuccess } // 文件不存在也视为删除成功
|
||||||
}
|
}
|
||||||
|
|
||||||
return { success: false, message: `删除失败: HTTP ${response.status}` }
|
return { success: false, messageKey: WebDAVMessageKey.deleteFailedHttp, messageParams: { status: response.status } }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = error instanceof Error ? error.message : String(error)
|
const errorMessage = error instanceof Error ? error.message : String(error)
|
||||||
return { success: false, message: `删除错误: ${message}` }
|
return { success: false, messageKey: WebDAVMessageKey.deleteError, messageParams: { error: errorMessage } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ import type {
|
|||||||
IncomingFleetAlert,
|
IncomingFleetAlert,
|
||||||
MissileAttack,
|
MissileAttack,
|
||||||
AchievementStats,
|
AchievementStats,
|
||||||
AchievementProgress
|
AchievementProgress,
|
||||||
|
WebDAVConfig
|
||||||
} from '@/types/game'
|
} from '@/types/game'
|
||||||
import { TechnologyType, OfficerType } from '@/types/game'
|
import { TechnologyType, OfficerType } from '@/types/game'
|
||||||
import { initializeAchievementStats, initializeAchievements } from '@/logic/achievementLogic'
|
import { initializeAchievementStats, initializeAchievements } from '@/logic/achievementLogic'
|
||||||
@@ -61,7 +62,8 @@ export const useGameStore = defineStore('game', {
|
|||||||
research: true,
|
research: true,
|
||||||
unlock: true
|
unlock: true
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
webdavConfig: null as WebDAVConfig | null
|
||||||
}),
|
}),
|
||||||
actions: {
|
actions: {
|
||||||
async requestBrowserPermission(): Promise<boolean> {
|
async requestBrowserPermission(): Promise<boolean> {
|
||||||
|
|||||||
@@ -120,6 +120,15 @@
|
|||||||
@apply border-border outline-ring/50;
|
@apply border-border outline-ring/50;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
/* 平滑过渡 */
|
||||||
|
transition: background-color 0.3s ease, color 0.3s ease;
|
||||||
|
/* 禁用文本大小调整,防止移动端自动放大文本 */
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
-moz-text-size-adjust: 100%;
|
||||||
|
text-size-adjust: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
@apply bg-background text-foreground;
|
@apply bg-background text-foreground;
|
||||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||||
@@ -128,11 +137,6 @@
|
|||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 平滑过渡 */
|
|
||||||
html {
|
|
||||||
transition: background-color 0.3s ease, color 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 滚动条样式 */
|
/* 滚动条样式 */
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
width: 8px;
|
width: 8px;
|
||||||
@@ -218,3 +222,21 @@ aside nav a:hover button {
|
|||||||
background-color: oklch(0.3 0.02 85) !important;
|
background-color: oklch(0.3 0.02 85) !important;
|
||||||
color: oklch(0.95 0.008 85) !important;
|
color: oklch(0.95 0.008 85) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 修复 Edge-to-Edge 模式下 Portal 容器的定位问题 */
|
||||||
|
[data-reka-portal],
|
||||||
|
[data-radix-portal] {
|
||||||
|
position: fixed !important;
|
||||||
|
left: 0 !important;
|
||||||
|
top: 0 !important;
|
||||||
|
right: 0 !important;
|
||||||
|
bottom: 0 !important;
|
||||||
|
pointer-events: none;
|
||||||
|
padding: 0 !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-reka-portal] > *,
|
||||||
|
[data-radix-portal] > * {
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
@@ -1226,3 +1226,11 @@ export interface QuestNotification {
|
|||||||
rewards?: QuestReward
|
rewards?: QuestReward
|
||||||
read?: boolean
|
read?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WebDAV 配置
|
||||||
|
export interface WebDAVConfig {
|
||||||
|
serverUrl: string // WebDAV 服务器地址
|
||||||
|
username: string // 用户名
|
||||||
|
password: string // 密码或应用专用密码
|
||||||
|
basePath: string // 存档存放路径
|
||||||
|
}
|
||||||
|
|||||||
@@ -181,133 +181,6 @@
|
|||||||
</ScrollableDialogContent>
|
</ScrollableDialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
<!-- NPC互动面板 - 贸易提议、情报、联合攻击邀请 -->
|
|
||||||
<div v-if="hasNpcInteractions" class="space-y-4">
|
|
||||||
<Collapsible v-model:open="interactionPanelOpen" class="border rounded-lg">
|
|
||||||
<CollapsibleTrigger class="flex items-center justify-between w-full p-4 hover:bg-accent/50 transition-colors">
|
|
||||||
<div class="flex items-center gap-2">
|
|
||||||
<Handshake class="h-5 w-5 text-primary" />
|
|
||||||
<span class="font-semibold">{{ t('npcBehavior.trade.title') }} & {{ t('npcBehavior.intel.title') }}</span>
|
|
||||||
<Badge variant="destructive" v-if="totalInteractionCount > 0">{{ totalInteractionCount }}</Badge>
|
|
||||||
</div>
|
|
||||||
<ChevronDown class="h-4 w-4 transition-transform" :class="{ 'rotate-180': interactionPanelOpen }" />
|
|
||||||
</CollapsibleTrigger>
|
|
||||||
<CollapsibleContent class="px-4 pb-4 space-y-4">
|
|
||||||
<!-- 贸易提议 -->
|
|
||||||
<div v-if="activeTradeOffers.length > 0">
|
|
||||||
<h3 class="text-sm font-semibold mb-2 flex items-center gap-2">
|
|
||||||
<ArrowLeftRight class="h-4 w-4" />
|
|
||||||
{{ t('npcBehavior.trade.title') }} ({{ activeTradeOffers.length }})
|
|
||||||
</h3>
|
|
||||||
<div class="grid gap-2">
|
|
||||||
<Card v-for="offer in activeTradeOffers" :key="offer.id" class="p-3">
|
|
||||||
<div class="flex items-start justify-between gap-4">
|
|
||||||
<div class="flex-1 space-y-1">
|
|
||||||
<div class="font-medium">{{ getNpcName(offer.npcId) }}</div>
|
|
||||||
<div class="text-sm text-muted-foreground">
|
|
||||||
<span class="text-green-600 dark:text-green-400">{{ t('npcBehavior.trade.offers') }}:</span>
|
|
||||||
{{ formatResources(offer.offeredResources) }}
|
|
||||||
</div>
|
|
||||||
<div class="text-sm text-muted-foreground">
|
|
||||||
<span class="text-red-600 dark:text-red-400">{{ t('npcBehavior.trade.requests') }}:</span>
|
|
||||||
{{ formatResources(offer.requestedResources) }}
|
|
||||||
</div>
|
|
||||||
<div class="text-xs text-muted-foreground">
|
|
||||||
{{ t('npcBehavior.trade.expiresIn') }}: {{ formatTimeRemaining(offer.expiresAt) }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex gap-2">
|
|
||||||
<Button size="sm" variant="default" @click="acceptTradeOffer(offer)" :disabled="!canAcceptTrade(offer)">
|
|
||||||
{{ t('npcBehavior.trade.accept') }}
|
|
||||||
</Button>
|
|
||||||
<Button size="sm" variant="outline" @click="declineTradeOffer(offer)">
|
|
||||||
{{ t('npcBehavior.trade.decline') }}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 情报报告 -->
|
|
||||||
<div v-if="unreadIntelReports.length > 0">
|
|
||||||
<h3 class="text-sm font-semibold mb-2 flex items-center gap-2">
|
|
||||||
<Eye class="h-4 w-4" />
|
|
||||||
{{ t('npcBehavior.intel.title') }} ({{ unreadIntelReports.length }})
|
|
||||||
</h3>
|
|
||||||
<div class="grid gap-2">
|
|
||||||
<Card v-for="intel in unreadIntelReports" :key="intel.id" class="p-3">
|
|
||||||
<div class="flex items-start justify-between gap-4">
|
|
||||||
<div class="flex-1 space-y-1">
|
|
||||||
<div class="font-medium">{{ t('npcBehavior.intel.from') }}: {{ getNpcName(intel.fromNpcId) }}</div>
|
|
||||||
<div class="text-sm text-muted-foreground">
|
|
||||||
{{ t('npcBehavior.intel.target') }}: {{ getNpcName(intel.targetNpcId) }}
|
|
||||||
</div>
|
|
||||||
<div class="text-sm">
|
|
||||||
<Badge variant="outline">{{ t(`npcBehavior.intel.types.${intel.intelType}`) }}</Badge>
|
|
||||||
</div>
|
|
||||||
<div v-if="intel.data?.fleet" class="text-sm text-muted-foreground">
|
|
||||||
{{ t('npcBehavior.intel.fleetInfo') }}: {{ formatFleetInfo(intel.data.fleet) }}
|
|
||||||
</div>
|
|
||||||
<div v-if="intel.data?.resources" class="text-sm text-muted-foreground">
|
|
||||||
{{ t('npcBehavior.intel.resourceInfo') }}: {{ formatResources(intel.data.resources as Resources) }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Button size="sm" variant="ghost" @click="markIntelAsRead(intel)">
|
|
||||||
{{ t('npcBehavior.intel.markAsRead') }}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 联合攻击邀请 -->
|
|
||||||
<div v-if="activeJointAttackInvites.length > 0">
|
|
||||||
<h3 class="text-sm font-semibold mb-2 flex items-center gap-2">
|
|
||||||
<Swords class="h-4 w-4" />
|
|
||||||
{{ t('npcBehavior.jointAttack.title') }} ({{ activeJointAttackInvites.length }})
|
|
||||||
</h3>
|
|
||||||
<div class="grid gap-2">
|
|
||||||
<Card v-for="invite in activeJointAttackInvites" :key="invite.id" class="p-3">
|
|
||||||
<div class="flex items-start justify-between gap-4">
|
|
||||||
<div class="flex-1 space-y-1">
|
|
||||||
<div class="font-medium">{{ t('npcBehavior.jointAttack.from') }}: {{ getNpcName(invite.fromNpcId) }}</div>
|
|
||||||
<div class="text-sm text-muted-foreground">
|
|
||||||
{{ t('npcBehavior.jointAttack.target') }}: {{ getNpcName(invite.targetNpcId) }}
|
|
||||||
</div>
|
|
||||||
<div class="text-sm text-muted-foreground">
|
|
||||||
{{ t('npcBehavior.jointAttack.targetPlanet') }}: [{{ invite.targetPosition.galaxy }}:{{
|
|
||||||
invite.targetPosition.system
|
|
||||||
}}:{{ invite.targetPosition.position }}]
|
|
||||||
</div>
|
|
||||||
<div class="text-sm text-muted-foreground">
|
|
||||||
{{ t('npcBehavior.jointAttack.lootShare') }}: {{ (invite.expectedLootRatio * 100).toFixed(0) }}%
|
|
||||||
</div>
|
|
||||||
<div class="text-xs text-muted-foreground">
|
|
||||||
{{ t('npcBehavior.jointAttack.expiresIn') }}: {{ formatTimeRemaining(invite.expiresAt) }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex gap-2">
|
|
||||||
<Button size="sm" variant="default" @click="acceptJointAttack(invite)">
|
|
||||||
{{ t('npcBehavior.jointAttack.accept') }}
|
|
||||||
</Button>
|
|
||||||
<Button size="sm" variant="outline" @click="declineJointAttack(invite)">
|
|
||||||
{{ t('npcBehavior.jointAttack.decline') }}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 无互动内容提示 -->
|
|
||||||
<div v-if="!hasActiveInteractions" class="text-center py-4 text-muted-foreground">
|
|
||||||
{{ t('npcBehavior.trade.noOffers') }}
|
|
||||||
</div>
|
|
||||||
</CollapsibleContent>
|
|
||||||
</Collapsible>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 搜索框 -->
|
<!-- 搜索框 -->
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<Search class="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
<Search class="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
||||||
@@ -503,14 +376,11 @@
|
|||||||
import { useGameStore } from '@/stores/gameStore'
|
import { useGameStore } from '@/stores/gameStore'
|
||||||
import { useNPCStore } from '@/stores/npcStore'
|
import { useNPCStore } from '@/stores/npcStore'
|
||||||
import { useI18n } from '@/composables/useI18n'
|
import { useI18n } from '@/composables/useI18n'
|
||||||
import { toast } from 'vue-sonner'
|
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||||
import { Badge } from '@/components/ui/badge'
|
import { Badge } from '@/components/ui/badge'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { Card } from '@/components/ui/card'
|
|
||||||
import { Dialog, DialogDescription, DialogTitle } from '@/components/ui/dialog'
|
import { Dialog, DialogDescription, DialogTitle } from '@/components/ui/dialog'
|
||||||
import ScrollableDialogContent from '@/components/ui/dialog/ScrollableDialogContent.vue'
|
import ScrollableDialogContent from '@/components/ui/dialog/ScrollableDialogContent.vue'
|
||||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible'
|
|
||||||
import {
|
import {
|
||||||
FixedPagination,
|
FixedPagination,
|
||||||
Pagination,
|
Pagination,
|
||||||
@@ -523,7 +393,7 @@
|
|||||||
import NpcRelationCard from '@/components/npc/NpcRelationCard.vue'
|
import NpcRelationCard from '@/components/npc/NpcRelationCard.vue'
|
||||||
import NpcRelationRow from '@/components/npc/NpcRelationRow.vue'
|
import NpcRelationRow from '@/components/npc/NpcRelationRow.vue'
|
||||||
import { RelationStatus } from '@/types/game'
|
import { RelationStatus } from '@/types/game'
|
||||||
import type { DiplomaticRelation, TradeOffer, IntelReport, JointAttackInvite, Resources } from '@/types/game'
|
import type { DiplomaticRelation } from '@/types/game'
|
||||||
import * as npcBehaviorLogic from '@/logic/npcBehaviorLogic'
|
import * as npcBehaviorLogic from '@/logic/npcBehaviorLogic'
|
||||||
import {
|
import {
|
||||||
Search,
|
Search,
|
||||||
@@ -533,11 +403,7 @@
|
|||||||
Swords,
|
Swords,
|
||||||
Activity,
|
Activity,
|
||||||
LayoutGrid,
|
LayoutGrid,
|
||||||
List,
|
List
|
||||||
Handshake,
|
|
||||||
ChevronDown,
|
|
||||||
ArrowLeftRight,
|
|
||||||
Eye
|
|
||||||
} from 'lucide-vue-next'
|
} from 'lucide-vue-next'
|
||||||
import { Empty, EmptyContent, EmptyDescription } from '@/components/ui/empty'
|
import { Empty, EmptyContent, EmptyDescription } from '@/components/ui/empty'
|
||||||
|
|
||||||
@@ -548,9 +414,6 @@
|
|||||||
|
|
||||||
const activeTab = ref('all')
|
const activeTab = ref('all')
|
||||||
|
|
||||||
// NPC互动面板状态
|
|
||||||
const interactionPanelOpen = ref(true)
|
|
||||||
|
|
||||||
// 视图模式: 'card' | 'list'
|
// 视图模式: 'card' | 'list'
|
||||||
const viewMode = ref<'card' | 'list'>('list')
|
const viewMode = ref<'card' | 'list'>('list')
|
||||||
|
|
||||||
@@ -864,188 +727,6 @@
|
|||||||
currentPage.value[activeTab.value] = val
|
currentPage.value[activeTab.value] = val
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// ========== NPC互动面板相关 ==========
|
|
||||||
|
|
||||||
// 获取当前时间戳
|
|
||||||
const now = computed(() => Date.now())
|
|
||||||
|
|
||||||
// 有效的贸易提议(未过期)
|
|
||||||
const activeTradeOffers = computed(() => {
|
|
||||||
return (gameStore.player.tradeOffers || []).filter(offer => offer.expiresAt > now.value)
|
|
||||||
})
|
|
||||||
|
|
||||||
// 未读的情报报告
|
|
||||||
const unreadIntelReports = computed(() => {
|
|
||||||
return (gameStore.player.intelReports || []).filter(report => !report.read)
|
|
||||||
})
|
|
||||||
|
|
||||||
// 有效的联合攻击邀请(未过期)
|
|
||||||
const activeJointAttackInvites = computed(() => {
|
|
||||||
return (gameStore.player.jointAttackInvites || []).filter(invite => invite.expiresAt > now.value)
|
|
||||||
})
|
|
||||||
|
|
||||||
// 是否有NPC互动数据
|
|
||||||
const hasNpcInteractions = computed(() => {
|
|
||||||
return (
|
|
||||||
(gameStore.player.tradeOffers?.length || 0) > 0 ||
|
|
||||||
(gameStore.player.intelReports?.length || 0) > 0 ||
|
|
||||||
(gameStore.player.jointAttackInvites?.length || 0) > 0
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
// 是否有有效的互动内容
|
|
||||||
const hasActiveInteractions = computed(() => {
|
|
||||||
return activeTradeOffers.value.length > 0 || unreadIntelReports.value.length > 0 || activeJointAttackInvites.value.length > 0
|
|
||||||
})
|
|
||||||
|
|
||||||
// 总互动数量(用于显示徽章)
|
|
||||||
const totalInteractionCount = computed(() => {
|
|
||||||
return activeTradeOffers.value.length + unreadIntelReports.value.length + activeJointAttackInvites.value.length
|
|
||||||
})
|
|
||||||
|
|
||||||
// 获取NPC名称
|
|
||||||
const getNpcName = (npcId: string): string => {
|
|
||||||
const npc = npcStore.npcs.find(n => n.id === npcId)
|
|
||||||
return npc?.name || npcId
|
|
||||||
}
|
|
||||||
|
|
||||||
// 格式化资源显示
|
|
||||||
// 格式化资源(兼容新旧格式)
|
|
||||||
const formatResources = (resources: Resources | { type: string; amount: number }): string => {
|
|
||||||
// 新格式:{ type: 'metal', amount: 1000 }
|
|
||||||
if ('type' in resources && 'amount' in resources) {
|
|
||||||
const typeLabels: Record<string, string> = {
|
|
||||||
metal: 'M',
|
|
||||||
crystal: 'C',
|
|
||||||
deuterium: 'D'
|
|
||||||
}
|
|
||||||
return `${Math.floor(resources.amount).toLocaleString()} ${typeLabels[resources.type] || resources.type}`
|
|
||||||
}
|
|
||||||
// 旧格式:{ metal: 1000, crystal: 0, deuterium: 0 }
|
|
||||||
const parts: string[] = []
|
|
||||||
if ((resources as Resources).metal > 0) parts.push(`${Math.floor((resources as Resources).metal).toLocaleString()} M`)
|
|
||||||
if ((resources as Resources).crystal > 0) parts.push(`${Math.floor((resources as Resources).crystal).toLocaleString()} C`)
|
|
||||||
if ((resources as Resources).deuterium > 0) parts.push(`${Math.floor((resources as Resources).deuterium).toLocaleString()} D`)
|
|
||||||
return parts.join(' / ') || '-'
|
|
||||||
}
|
|
||||||
|
|
||||||
// 格式化舰队信息
|
|
||||||
const formatFleetInfo = (fleetInfo: Record<string, number>): string => {
|
|
||||||
const parts: string[] = []
|
|
||||||
for (const [shipType, count] of Object.entries(fleetInfo)) {
|
|
||||||
if (count > 0) {
|
|
||||||
parts.push(`${shipType}: ${count}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return parts.join(', ') || '-'
|
|
||||||
}
|
|
||||||
|
|
||||||
// 格式化剩余时间
|
|
||||||
const formatTimeRemaining = (expiresAt: number): string => {
|
|
||||||
const remaining = expiresAt - now.value
|
|
||||||
if (remaining <= 0) return t('npcBehavior.trade.expired')
|
|
||||||
|
|
||||||
const minutes = Math.floor(remaining / 60000)
|
|
||||||
const hours = Math.floor(minutes / 60)
|
|
||||||
const mins = minutes % 60
|
|
||||||
|
|
||||||
if (hours > 0) {
|
|
||||||
return `${hours}h ${mins}m`
|
|
||||||
}
|
|
||||||
return `${mins}m`
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查是否可以接受贸易(兼容新格式 { type, amount })
|
|
||||||
const canAcceptTrade = (offer: TradeOffer): boolean => {
|
|
||||||
const planet = gameStore.player.planets[0]
|
|
||||||
if (!planet) return false
|
|
||||||
|
|
||||||
// 新格式:{ type: 'metal', amount: 1000 }
|
|
||||||
const requestedType = offer.requestedResources.type
|
|
||||||
const requestedAmount = offer.requestedResources.amount
|
|
||||||
return planet.resources[requestedType] >= requestedAmount
|
|
||||||
}
|
|
||||||
|
|
||||||
// 接受贸易提议
|
|
||||||
const acceptTradeOffer = (offer: TradeOffer) => {
|
|
||||||
if (!canAcceptTrade(offer)) {
|
|
||||||
toast.error(t('npcBehavior.trade.acceptFailed'))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const planet = gameStore.player.planets[0]
|
|
||||||
if (!planet) return
|
|
||||||
|
|
||||||
// 新格式:{ type: 'metal', amount: 1000 }
|
|
||||||
const requestedType = offer.requestedResources.type
|
|
||||||
const requestedAmount = offer.requestedResources.amount
|
|
||||||
const offeredType = offer.offeredResources.type
|
|
||||||
const offeredAmount = offer.offeredResources.amount
|
|
||||||
|
|
||||||
// 扣除请求的资源
|
|
||||||
planet.resources[requestedType] -= requestedAmount
|
|
||||||
|
|
||||||
// 添加获得的资源
|
|
||||||
planet.resources[offeredType] += offeredAmount
|
|
||||||
|
|
||||||
// 移除贸易提议
|
|
||||||
const index = gameStore.player.tradeOffers?.indexOf(offer)
|
|
||||||
if (index !== undefined && index >= 0) {
|
|
||||||
gameStore.player.tradeOffers?.splice(index, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 提高与该NPC的好感度(使用 npcId 而不是 fromNpcId)
|
|
||||||
const npcRelation = npcStore.npcs.find(n => n.id === offer.npcId)?.relations?.[gameStore.player.id]
|
|
||||||
if (npcRelation) {
|
|
||||||
npcRelation.reputation += 10
|
|
||||||
}
|
|
||||||
|
|
||||||
toast.success(t('npcBehavior.trade.acceptSuccess'))
|
|
||||||
}
|
|
||||||
|
|
||||||
// 拒绝贸易提议
|
|
||||||
const declineTradeOffer = (offer: TradeOffer) => {
|
|
||||||
const index = gameStore.player.tradeOffers?.indexOf(offer)
|
|
||||||
if (index !== undefined && index >= 0) {
|
|
||||||
gameStore.player.tradeOffers?.splice(index, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
toast.info(t('npcBehavior.trade.declined'))
|
|
||||||
}
|
|
||||||
|
|
||||||
// 标记情报为已读
|
|
||||||
const markIntelAsRead = (intel: IntelReport) => {
|
|
||||||
intel.read = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 接受联合攻击邀请
|
|
||||||
const acceptJointAttack = (invite: JointAttackInvite) => {
|
|
||||||
// 这里可以添加联合攻击的逻辑
|
|
||||||
// 目前只是简单地移除邀请并显示提示
|
|
||||||
const index = gameStore.player.jointAttackInvites?.indexOf(invite)
|
|
||||||
if (index !== undefined && index >= 0) {
|
|
||||||
gameStore.player.jointAttackInvites?.splice(index, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 提高与该NPC的好感度(使用 npcStore)
|
|
||||||
const npcRelation = npcStore.npcs.find(n => n.id === invite.fromNpcId)?.relations?.[gameStore.player.id]
|
|
||||||
if (npcRelation) {
|
|
||||||
npcRelation.reputation += 15
|
|
||||||
}
|
|
||||||
|
|
||||||
toast.success(t('npcBehavior.jointAttack.acceptSuccess'))
|
|
||||||
}
|
|
||||||
|
|
||||||
// 拒绝联合攻击邀请
|
|
||||||
const declineJointAttack = (invite: JointAttackInvite) => {
|
|
||||||
const index = gameStore.player.jointAttackInvites?.indexOf(invite)
|
|
||||||
if (index !== undefined && index >= 0) {
|
|
||||||
gameStore.player.jointAttackInvites?.splice(index, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
toast.info(t('npcBehavior.jointAttack.declined'))
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
@@ -1117,13 +1117,17 @@
|
|||||||
// 获取任务类型名称
|
// 获取任务类型名称
|
||||||
const getMissionTypeName = (missionType: string): string => {
|
const getMissionTypeName = (missionType: string): string => {
|
||||||
const typeMap: Record<string, string> = {
|
const typeMap: Record<string, string> = {
|
||||||
|
[MissionType.Attack]: t('fleetView.attack'),
|
||||||
[MissionType.Transport]: t('fleetView.transport'),
|
[MissionType.Transport]: t('fleetView.transport'),
|
||||||
[MissionType.Colonize]: t('fleetView.colonize'),
|
[MissionType.Colonize]: t('fleetView.colonize'),
|
||||||
|
[MissionType.Spy]: t('fleetView.spy'),
|
||||||
[MissionType.Deploy]: t('fleetView.deploy'),
|
[MissionType.Deploy]: t('fleetView.deploy'),
|
||||||
[MissionType.Expedition]: t('fleetView.expedition'),
|
[MissionType.Expedition]: t('fleetView.expedition'),
|
||||||
[MissionType.Recycle]: t('fleetView.recycle'),
|
[MissionType.Recycle]: t('fleetView.recycle'),
|
||||||
[MissionType.Destroy]: t('fleetView.destroy'),
|
[MissionType.Destroy]: t('fleetView.destroy'),
|
||||||
[MissionType.MissileAttack]: t('galaxyView.missileAttack')
|
[MissionType.MissileAttack]: t('galaxyView.missileAttack'),
|
||||||
|
[MissionType.HarvestDarkMatter]: t('fleetView.harvestDarkMatter'),
|
||||||
|
[MissionType.Station]: t('fleetView.station')
|
||||||
}
|
}
|
||||||
return typeMap[missionType] || missionType
|
return typeMap[missionType] || missionType
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -334,7 +334,7 @@
|
|||||||
<PrivacyDialog v-model:open="showPrivacyDialog" />
|
<PrivacyDialog v-model:open="showPrivacyDialog" />
|
||||||
|
|
||||||
<!-- WebDAV 配置对话框 -->
|
<!-- WebDAV 配置对话框 -->
|
||||||
<WebDAVConfigDialog v-model:open="showWebDAVConfig" @saved="onWebDAVConfigSaved" />
|
<WebDAVConfigDialog v-model:open="showWebDAVConfig" />
|
||||||
|
|
||||||
<!-- WebDAV 文件列表对话框 -->
|
<!-- WebDAV 文件列表对话框 -->
|
||||||
<WebDAVFileListDialog v-model:open="showWebDAVFiles" :config="webdavConfig" @select="handleWebDAVDownload" />
|
<WebDAVFileListDialog v-model:open="showWebDAVFiles" :config="webdavConfig" @select="handleWebDAVDownload" />
|
||||||
@@ -389,12 +389,7 @@
|
|||||||
import WebDAVConfigDialog from '@/components/settings/WebDAVConfigDialog.vue'
|
import WebDAVConfigDialog from '@/components/settings/WebDAVConfigDialog.vue'
|
||||||
import WebDAVFileListDialog from '@/components/settings/WebDAVFileListDialog.vue'
|
import WebDAVFileListDialog from '@/components/settings/WebDAVFileListDialog.vue'
|
||||||
import { useHints } from '@/composables/useHints'
|
import { useHints } from '@/composables/useHints'
|
||||||
import {
|
import { uploadToWebDAV, downloadFromWebDAV } from '@/services/webdavService'
|
||||||
type WebDAVConfig,
|
|
||||||
getWebDAVConfig,
|
|
||||||
uploadToWebDAV,
|
|
||||||
downloadFromWebDAV
|
|
||||||
} from '@/services/webdavService'
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const { hintsEnabled, setHintsEnabled, resetHints } = useHints()
|
const { hintsEnabled, setHintsEnabled, resetHints } = useHints()
|
||||||
@@ -415,7 +410,7 @@
|
|||||||
// WebDAV 相关状态
|
// WebDAV 相关状态
|
||||||
const showWebDAVConfig = ref(false)
|
const showWebDAVConfig = ref(false)
|
||||||
const showWebDAVFiles = ref(false)
|
const showWebDAVFiles = ref(false)
|
||||||
const webdavConfig = ref<WebDAVConfig | null>(getWebDAVConfig())
|
const webdavConfig = computed(() => gameStore.webdavConfig)
|
||||||
const isWebDAVUploading = ref(false)
|
const isWebDAVUploading = ref(false)
|
||||||
|
|
||||||
// 确保通知设置存在
|
// 确保通知设置存在
|
||||||
@@ -758,11 +753,6 @@
|
|||||||
gameStore.player.backgroundEnabled = val
|
gameStore.player.backgroundEnabled = val
|
||||||
}
|
}
|
||||||
|
|
||||||
// WebDAV 配置保存回调
|
|
||||||
const onWebDAVConfigSaved = () => {
|
|
||||||
webdavConfig.value = getWebDAVConfig()
|
|
||||||
}
|
|
||||||
|
|
||||||
// WebDAV 上传
|
// WebDAV 上传
|
||||||
const handleWebDAVUpload = async () => {
|
const handleWebDAVUpload = async () => {
|
||||||
if (!webdavConfig.value) return
|
if (!webdavConfig.value) return
|
||||||
@@ -792,7 +782,7 @@
|
|||||||
if (result.success) {
|
if (result.success) {
|
||||||
toast.success(t('settings.webdav.uploadSuccess'))
|
toast.success(t('settings.webdav.uploadSuccess'))
|
||||||
} else {
|
} else {
|
||||||
toast.error(result.message || t('settings.webdav.uploadFailed'))
|
toast.error(t(result.messageKey) || t('settings.webdav.uploadFailed'))
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('WebDAV upload failed:', error)
|
console.error('WebDAV upload failed:', error)
|
||||||
@@ -810,7 +800,7 @@
|
|||||||
const result = await downloadFromWebDAV(webdavConfig.value, fileName)
|
const result = await downloadFromWebDAV(webdavConfig.value, fileName)
|
||||||
|
|
||||||
if (!result.success || !result.data) {
|
if (!result.success || !result.data) {
|
||||||
toast.error(result.message || t('settings.webdav.downloadFailed'))
|
toast.error(t(result.messageKey) || t('settings.webdav.downloadFailed'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ import tailwindcss from '@tailwindcss/vite'
|
|||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
import { fileURLToPath } from 'node:url'
|
import { fileURLToPath } from 'node:url'
|
||||||
import { VitePWA } from 'vite-plugin-pwa'
|
import { VitePWA } from 'vite-plugin-pwa'
|
||||||
import autoprefixer from 'autoprefixer'
|
|
||||||
import pkg from './package.json'
|
import pkg from './package.json'
|
||||||
|
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||||
|
|
||||||
|
// @ts-expect-error - Vite CSS transformer type
|
||||||
export default defineConfig(async () => {
|
export default defineConfig(async () => {
|
||||||
const plugins = [
|
const plugins = [
|
||||||
vue(),
|
vue(),
|
||||||
@@ -110,12 +110,19 @@ export default defineConfig(async () => {
|
|||||||
plugins,
|
plugins,
|
||||||
resolve: { alias: { '@': path.resolve(__dirname, './src') } },
|
resolve: { alias: { '@': path.resolve(__dirname, './src') } },
|
||||||
css: {
|
css: {
|
||||||
postcss: {
|
// 使用 lightningcss 处理 CSS,自动转换 oklch 等新语法为兼容格式
|
||||||
plugins: [
|
transformer: 'lightningcss',
|
||||||
autoprefixer({
|
lightningcss: {
|
||||||
overrideBrowserslist: ['Android >= 4.1', 'iOS >= 7.1', 'Chrome >= 31', 'Firefox >= 31', 'ie >= 8']
|
// 目标浏览器:Android 5+, iOS 10+, Chrome 60+
|
||||||
})
|
targets: {
|
||||||
]
|
android: 5 << 16, // Android 5.0
|
||||||
|
chrome: 60 << 16, // Chrome 60
|
||||||
|
ios_saf: 10 << 16 // iOS Safari 10
|
||||||
|
},
|
||||||
|
// 禁用现代 CSS 特性,确保兼容旧版浏览器
|
||||||
|
drafts: {
|
||||||
|
customMedia: false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// 优化依赖预构建
|
// 优化依赖预构建
|
||||||
|
|||||||
Reference in New Issue
Block a user