mirror of
https://github.com/octopusYan/alist-gui.git
synced 2025-12-08 09:11:56 +08:00
pref: 优化GUI更新功能;项目分为gui和upgrade两个模块
This commit is contained in:
174
upgrade/pom.xml
Normal file
174
upgrade/pom.xml
Normal file
@ -0,0 +1,174 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>cn.octopusyan</groupId>
|
||||
<artifactId>alist-gui</artifactId>
|
||||
<version>1.0.1</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>upgrade</artifactId>
|
||||
<version>${parent.version}</version>
|
||||
<name>upgrade</name>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
<java.version>21</java.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
|
||||
<exec.mainClass>cn.octopusyan.upgrade.AppLauncher</exec.mainClass>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- javafx -->
|
||||
<dependency>
|
||||
<groupId>org.openjfx</groupId>
|
||||
<artifactId>javafx-controls</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openjfx</groupId>
|
||||
<artifactId>javafx-fxml</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mkpaz.github.io/atlantafx/ -->
|
||||
<dependency>
|
||||
<groupId>io.github.mkpaz</groupId>
|
||||
<artifactId>atlantafx-base</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
<configuration>
|
||||
<source>21</source>
|
||||
<target>21</target>
|
||||
<encoding>UTF-8</encoding>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok.version}</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.openjfx</groupId>
|
||||
<artifactId>javafx-maven-plugin</artifactId>
|
||||
<version>0.0.8</version>
|
||||
<configuration>
|
||||
<stripDebug>true</stripDebug>
|
||||
<compress>2</compress>
|
||||
<noHeaderFiles>true</noHeaderFiles>
|
||||
<noManPages>true</noManPages>
|
||||
<launcher>upgrade</launcher>
|
||||
<jlinkImageName>app</jlinkImageName>
|
||||
<jlinkZipName>app</jlinkZipName>
|
||||
<mainClass>cn.octopusyan.upgrade/${exec.mainClass}</mainClass>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<!-- Default configuration for running with: mvn clean javafx:run -->
|
||||
<id>default-cli</id>
|
||||
<configuration>
|
||||
<stripDebug>true</stripDebug>
|
||||
<compress>2</compress>
|
||||
<noHeaderFiles>true</noHeaderFiles>
|
||||
<noManPages>true</noManPages>
|
||||
<launcher>upgrade</launcher>
|
||||
<jlinkImageName>app</jlinkImageName>
|
||||
<jlinkZipName>app</jlinkZipName>
|
||||
<mainClass>cn.octopusyan.upgrade/${exec.mainClass}</mainClass>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<!-- https://github.com/fvarrui/JavaPackager -->
|
||||
<plugin>
|
||||
<groupId>io.github.fvarrui</groupId>
|
||||
<artifactId>javapackager</artifactId>
|
||||
<version>1.7.7-SNAPSHOT</version>
|
||||
<configuration>
|
||||
<mainClass>${exec.mainClass}</mainClass>
|
||||
<bundleJre>true</bundleJre>
|
||||
<generateInstaller>false</generateInstaller>
|
||||
<copyDependencies>true</copyDependencies>
|
||||
<vmArgs>
|
||||
<arg>-Xmx100m</arg>
|
||||
</vmArgs>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>windows</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>package</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<platform>windows</platform>
|
||||
<createZipball>false</createZipball>
|
||||
<winConfig>
|
||||
<headerType>gui</headerType>
|
||||
<generateMsi>false</generateMsi>
|
||||
</winConfig>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-resources</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<encoding>UTF-8</encoding>
|
||||
<!--打成jar包后复制到的路径-->
|
||||
<outputDirectory>../gui/src/main/resources/static</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<!--项目中需要复制的文件路径-->
|
||||
<directory>./target/upgrade</directory>
|
||||
<includes>
|
||||
<include>upgrade.exe</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
<!--可配置多个提取复制路径只需要 “<id>”名字不一样即可-->
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
13
upgrade/src/main/java/cn/octopusyan/upgrade/AppLauncher.java
Normal file
13
upgrade/src/main/java/cn/octopusyan/upgrade/AppLauncher.java
Normal file
@ -0,0 +1,13 @@
|
||||
package cn.octopusyan.upgrade;
|
||||
|
||||
/**
|
||||
* 启动类
|
||||
*
|
||||
* @author octopus_yan@foxmail.com
|
||||
*/
|
||||
public class AppLauncher {
|
||||
|
||||
public static void main(String[] args) {
|
||||
Application.launch(Application.class, args);
|
||||
}
|
||||
}
|
||||
35
upgrade/src/main/java/cn/octopusyan/upgrade/Application.java
Normal file
35
upgrade/src/main/java/cn/octopusyan/upgrade/Application.java
Normal file
@ -0,0 +1,35 @@
|
||||
package cn.octopusyan.upgrade;
|
||||
|
||||
import atlantafx.base.theme.PrimerLight;
|
||||
import cn.octopusyan.upgrade.util.FxmlUtil;
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
public class Application extends javafx.application.Application {
|
||||
|
||||
@Override
|
||||
public void start(Stage primaryStage) throws IOException {
|
||||
|
||||
// 主题样式
|
||||
Application.setUserAgentStylesheet(new PrimerLight().getUserAgentStylesheet());
|
||||
|
||||
// 启动主界面
|
||||
Pane root = FxmlUtil.init("/fxml/hello-view.fxml").load();
|
||||
Scene scene = new Scene(root, 420, 240);
|
||||
primaryStage.setTitle(ResourceBundle.getBundle("language/language").getString("title"));
|
||||
primaryStage.setScene(scene);
|
||||
primaryStage.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
// 关闭主界面
|
||||
Platform.exit();
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,75 @@
|
||||
package cn.octopusyan.upgrade.alert;
|
||||
|
||||
import cn.octopusyan.upgrade.alert.builder.*;
|
||||
import javafx.scene.control.Alert;
|
||||
import javafx.scene.control.ButtonType;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.stage.Window;
|
||||
|
||||
/**
|
||||
* 弹窗工具
|
||||
*
|
||||
* @author octopus_yan@foxmail.com
|
||||
*/
|
||||
public class AlertUtil {
|
||||
private static Window mOwner;
|
||||
|
||||
public static void initOwner(Stage stage) {
|
||||
AlertUtil.mOwner = stage;
|
||||
}
|
||||
|
||||
public static AlertBuilder info(String content) {
|
||||
return info().content(content).header(null);
|
||||
}
|
||||
|
||||
public static AlertBuilder info() {
|
||||
return alert(Alert.AlertType.INFORMATION);
|
||||
}
|
||||
|
||||
public static AlertBuilder error(String message) {
|
||||
return alert(Alert.AlertType.ERROR).header(null).content(message);
|
||||
}
|
||||
|
||||
public static AlertBuilder warning() {
|
||||
return alert(Alert.AlertType.WARNING);
|
||||
}
|
||||
|
||||
public static AlertBuilder exception(Exception ex) {
|
||||
return alert(Alert.AlertType.ERROR).exception(ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* 确认对话框
|
||||
*/
|
||||
public static AlertBuilder confirm() {
|
||||
return alert(Alert.AlertType.CONFIRMATION);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义确认对话框 <p>
|
||||
*
|
||||
* @param buttons <code>"Cancel"</code> OR <code>"取消"</code> 为取消按钮
|
||||
*/
|
||||
public static AlertBuilder confirm(String... buttons) {
|
||||
return confirm().buttons(buttons);
|
||||
}
|
||||
|
||||
public static AlertBuilder confirm(ButtonType... buttons) {
|
||||
return confirm().buttons(buttons);
|
||||
}
|
||||
|
||||
public static AlertBuilder alert(Alert.AlertType type) {
|
||||
return new AlertBuilder(mOwner, type);
|
||||
}
|
||||
|
||||
public interface OnChoseListener {
|
||||
void confirm();
|
||||
|
||||
default void cancelOrClose(ButtonType buttonType) {
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnClickListener {
|
||||
void onClicked(String result);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,108 @@
|
||||
package cn.octopusyan.upgrade.alert.builder;
|
||||
|
||||
import cn.octopusyan.upgrade.alert.AlertUtil;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import javafx.scene.layout.Priority;
|
||||
import javafx.stage.Window;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author octopus_yan
|
||||
*/
|
||||
public class AlertBuilder extends BaseBuilder<AlertBuilder, Alert> {
|
||||
|
||||
public AlertBuilder(Window owner, Alert.AlertType alertType) {
|
||||
super(new Alert(alertType), owner);
|
||||
}
|
||||
|
||||
public AlertBuilder buttons(String... buttons) {
|
||||
dialog.getButtonTypes().addAll(getButtonList(buttons));
|
||||
return this;
|
||||
}
|
||||
|
||||
public AlertBuilder buttons(ButtonType... buttons) {
|
||||
dialog.getButtonTypes().addAll(buttons);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AlertBuilder exception(Exception ex) {
|
||||
dialog.setTitle("Exception Dialog");
|
||||
dialog.setHeaderText(ex.getClass().getSimpleName());
|
||||
dialog.setContentText(ex.getMessage());
|
||||
|
||||
// 创建可扩展的异常。
|
||||
var sw = new StringWriter();
|
||||
var pw = new PrintWriter(sw);
|
||||
ex.printStackTrace(pw);
|
||||
var exceptionText = sw.toString();
|
||||
|
||||
var label = new Label("The exception stacktrace was :");
|
||||
|
||||
var textArea = new TextArea(exceptionText);
|
||||
textArea.setEditable(false);
|
||||
textArea.setWrapText(true);
|
||||
|
||||
textArea.setMaxWidth(Double.MAX_VALUE);
|
||||
textArea.setMaxHeight(Double.MAX_VALUE);
|
||||
GridPane.setVgrow(textArea, Priority.ALWAYS);
|
||||
GridPane.setHgrow(textArea, Priority.ALWAYS);
|
||||
|
||||
var expContent = new GridPane();
|
||||
expContent.setMaxWidth(Double.MAX_VALUE);
|
||||
expContent.add(label, 0, 0);
|
||||
expContent.add(textArea, 0, 1);
|
||||
|
||||
// 将可扩展异常设置到对话框窗格中。
|
||||
dialog.getDialogPane().setExpandableContent(expContent);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取按钮列表
|
||||
*
|
||||
* @param buttons "Cancel" / "取消" 为取消按钮
|
||||
*/
|
||||
private List<ButtonType> getButtonList(String[] buttons) {
|
||||
if (ArrayUtils.isEmpty(buttons)) return Collections.emptyList();
|
||||
|
||||
return Arrays.stream(buttons).map((type) -> {
|
||||
ButtonBar.ButtonData buttonData = ButtonBar.ButtonData.OTHER;
|
||||
if ("cancel".equals(StringUtils.lowerCase(type)) || "取消".equals(type)) {
|
||||
return ButtonType.CANCEL;
|
||||
}
|
||||
return new ButtonType(type, buttonData);
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* AlertUtil.confirm
|
||||
*/
|
||||
public void show(AlertUtil.OnClickListener listener) {
|
||||
Optional<ButtonType> result = dialog.showAndWait();
|
||||
result.ifPresent(r -> listener.onClicked(r.getText()));
|
||||
}
|
||||
|
||||
/**
|
||||
* AlertUtil.confirm
|
||||
*/
|
||||
public void show(AlertUtil.OnChoseListener listener) {
|
||||
Optional<ButtonType> result = dialog.showAndWait();
|
||||
result.ifPresent(r -> {
|
||||
if (r == ButtonType.OK) {
|
||||
listener.confirm();
|
||||
} else {
|
||||
listener.cancelOrClose(r);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
package cn.octopusyan.upgrade.alert.builder;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.control.Dialog;
|
||||
import javafx.stage.Window;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @author octopus_yan
|
||||
*/
|
||||
@Getter
|
||||
public abstract class BaseBuilder<T extends BaseBuilder<T, ?>, D extends Dialog<?>> {
|
||||
protected D dialog;
|
||||
|
||||
public BaseBuilder(D dialog, Window mOwner) {
|
||||
this.dialog = dialog;
|
||||
if (mOwner != null)
|
||||
this.dialog.initOwner(mOwner);
|
||||
}
|
||||
|
||||
public T title(String title) {
|
||||
dialog.setTitle(title);
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
public T header(String header) {
|
||||
dialog.setHeaderText(header);
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
public T content(String content) {
|
||||
dialog.setContentText(content);
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
public void show() {
|
||||
Platform.runLater(() -> dialog.showAndWait());
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (dialog.isShowing())
|
||||
dialog.close();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package cn.octopusyan.upgrade.util;
|
||||
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.fxml.JavaFXBuilderFactory;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
public class FxmlUtil {
|
||||
public static FXMLLoader init(String path) {
|
||||
return new FXMLLoader(
|
||||
FxmlUtil.class.getResource(path),
|
||||
ResourceBundle.getBundle("language/language"),
|
||||
new JavaFXBuilderFactory(),
|
||||
null,
|
||||
StandardCharsets.UTF_8
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,83 @@
|
||||
package cn.octopusyan.upgrade.view;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.ZipUtil;
|
||||
import cn.octopusyan.upgrade.alert.AlertUtil;
|
||||
import javafx.application.Platform;
|
||||
import javafx.fxml.FXML;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
public class HelloController {
|
||||
public static final String APP_NAME = "alist-gui";
|
||||
public static final String PARENT_PATH = Paths.get("").toFile().getAbsolutePath();
|
||||
|
||||
@FXML
|
||||
public void onUpdate() {
|
||||
// 更新检查
|
||||
File file = new File(PARENT_PATH + File.separator + APP_NAME + "-windows-nojre.zip");
|
||||
if (!file.exists()) {
|
||||
AlertUtil.error("The upgrade file does not exist!").show();
|
||||
return;
|
||||
}
|
||||
|
||||
// 解压
|
||||
unzip();
|
||||
// 启动GUI
|
||||
startGui();
|
||||
// 退出
|
||||
onExit();
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void onExit() {
|
||||
Platform.exit();
|
||||
}
|
||||
|
||||
private void startGui() {
|
||||
Runtime runtime = Runtime.getRuntime(); //获取Runtime实例
|
||||
//执行命令
|
||||
String[] command = {APP_NAME + ".exe"};
|
||||
try {
|
||||
Process process = runtime.exec(command);
|
||||
} catch (IOException ignored) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void unzip() {
|
||||
|
||||
|
||||
File file = new File(PARENT_PATH + File.separator + APP_NAME + "-windows-nojre.zip");
|
||||
ZipFile zipFile = ZipUtil.toZipFile(file, CharsetUtil.defaultCharset());
|
||||
ZipUtil.read(zipFile, zipEntry -> {
|
||||
String path = zipEntry.getName();
|
||||
if (FileUtil.isWindows()) {
|
||||
// Win系统下
|
||||
path = StrUtil.replace(path, "*", "_");
|
||||
}
|
||||
|
||||
// 打包后文件都在alist-gui文件夹下,解压时去掉
|
||||
path = path.replaceFirst(APP_NAME, "");
|
||||
|
||||
final File outItemFile = FileUtil.file(PARENT_PATH, path);
|
||||
if (zipEntry.isDirectory()) {
|
||||
// 目录
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
outItemFile.mkdirs();
|
||||
} else {
|
||||
InputStream in = ZipUtil.getStream(zipFile, zipEntry);
|
||||
// 文件
|
||||
FileUtil.writeFromStream(in, outItemFile, false);
|
||||
}
|
||||
});
|
||||
|
||||
// 解压完成后删除
|
||||
FileUtil.del(file);
|
||||
}
|
||||
}
|
||||
16
upgrade/src/main/java/module-info.java
Normal file
16
upgrade/src/main/java/module-info.java
Normal file
@ -0,0 +1,16 @@
|
||||
module cn.octopusyan.upgrade {
|
||||
requires javafx.controls;
|
||||
requires javafx.fxml;
|
||||
requires atlantafx.base;
|
||||
requires cn.hutool.core;
|
||||
requires static lombok;
|
||||
requires org.apache.commons.lang3;
|
||||
|
||||
|
||||
opens cn.octopusyan.upgrade to javafx.fxml;
|
||||
exports cn.octopusyan.upgrade;
|
||||
exports cn.octopusyan.upgrade.util;
|
||||
opens cn.octopusyan.upgrade.util to javafx.fxml;
|
||||
exports cn.octopusyan.upgrade.view;
|
||||
opens cn.octopusyan.upgrade.view to javafx.fxml;
|
||||
}
|
||||
14
upgrade/src/main/resources/fxml/hello-view.fxml
Normal file
14
upgrade/src/main/resources/fxml/hello-view.fxml
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<HBox alignment="CENTER" spacing="20.0" xmlns:fx="http://javafx.com/fxml/1"
|
||||
xmlns="http://javafx.com/javafx/11.0.14-internal"
|
||||
fx:controller="cn.octopusyan.upgrade.view.HelloController">
|
||||
<padding>
|
||||
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0"/>
|
||||
</padding>
|
||||
<Button onAction="#onUpdate" styleClass="large" text="%update"/>
|
||||
<Button onAction="#onExit" styleClass="large" text="%exit"/>
|
||||
</HBox>
|
||||
7
upgrade/src/main/resources/language/language.properties
Normal file
7
upgrade/src/main/resources/language/language.properties
Normal file
@ -0,0 +1,7 @@
|
||||
exit=\u9000\u51FA
|
||||
title=\u66F4\u65B0
|
||||
update=\u66F4\u65B0
|
||||
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,6 @@
|
||||
exit=Exit
|
||||
title=Upgrade App
|
||||
update=Upgrade
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,6 @@
|
||||
exit=\u9000\u51FA
|
||||
title=\u66F4\u65B0
|
||||
update=\u66F4\u65B0
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user