Javafx实现国际象棋游戏-成都快上网建站

Javafx实现国际象棋游戏

本文实例为大家分享了Javafx实现国际象棋游戏的具体代码,供大家参考,具体内容如下

目前创新互联建站已为成百上千家的企业提供了网站建设、域名、虚拟空间、网站托管、服务器租用、企业网站设计、庆云网站维护等服务,公司将坚持客户导向、应用为本的策略,正道将秉承"和谐、参与、激情"的文化,与客户和合作伙伴齐心协力一起成长,共同发展。

基本规则

  • 棋子马设计“日”的移动方式
  • 兵设计只能向前直走,每次只能走一格。但走第一步时,可以走一格或两格的移动方式
  • 请为后设计横、直、斜都可以走,步数不受限制,但不能越子的移动方式。
  • 车只能横向或者竖向行走
  • 国王是在以自己为中心的九宫格内行走
  • 骑士只能走对角线

项目目录结构

Javafx实现国际象棋游戏

UML类图关系

以骑士为例

Javafx实现国际象棋游戏

实现基本功能

  • 吃子
  • 不能越子
  • 游戏结束提示
  • 基本移动策略
  • 背景音乐

效果

Javafx实现国际象棋游戏

控制器

PressedAction

package com.Exercise3;

import com.Exercise3.Controller.PressedAction;
import com.Exercise3.Controller.ReleaseAction;
import com.Exercise3.Controller.ResetAction;
import com.Exercise3.view.ChessBoard;
import com.Exercise3.view.ChessPane;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;

import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.stage.Stage;

public class Test extends Application {

 public static void main(String[] args) {
 launch(args);
 }

 @Override
 public void start(Stage primaryStage) {

 String MEDIA_URL = "file:/E:/IdeaProjects/Experiment/src/com/Exercise3/music/BackgroundMusic.mp3";
 ChessBoard chessBoard = ChessBoard.getInstance(100,40,40);

 //添加媒体资源

 Media media = new Media(MEDIA_URL);
 MediaPlayer mediaPlayer = new MediaPlayer(media);
 mediaPlayer.setAutoPlay(true);
 mediaPlayer.setCycleCount(MediaPlayer.INDEFINITE);
 mediaPlayer.play();


 ChessPane pane = new ChessPane(chessBoard);
 pane.setOnMousePressed(new PressedAction(pane,mediaPlayer));

 pane.setOnMouseReleased(new ReleaseAction(pane));

 BorderPane borderPane = new BorderPane();
 borderPane.setCenter(pane);
 HBox hBox = new HBox();
 hBox.setAlignment(Pos.TOP_CENTER);

 Button button = new Button("悔棋");
 button.setOnAction(new ResetAction(pane));

 hBox.getChildren().add(button);
 borderPane.setBottom(hBox);
 Scene scene = new Scene(borderPane,900,900);
 primaryStage.setScene(scene);
 primaryStage.setTitle("国际象棋");
 primaryStage.show();

 }
}

ReleasedAction

package com.Exercise3.Controller;

import com.Exercise3.entity.Piece.ChessPiece;
import com.Exercise3.entity.PieceType;
import com.Exercise3.view.ChessBoard;
import com.Exercise3.view.ChessPane;
import javafx.event.EventHandler;
import javafx.scene.control.Alert;
import javafx.scene.input.MouseEvent;

import java.util.Stack;

public class ReleaseAction implements EventHandler {
 private ChessPane chessPane;
 static Stack stack = new Stack<>();

 public ReleaseAction(ChessPane chessPane) {
 this.chessPane = chessPane;
 }

 @Override
 public void handle(MouseEvent e) {
 chessPane.drawBoard();
 ChessBoard chessBoard = chessPane.getChessBoard();
 int x = (int) ((e.getX() - chessBoard.getStartX()) / (chessBoard.getCellLength()));
 int y = (int) ((e.getY() - chessBoard.getStartY()) / (chessBoard.getCellLength()));

 for (ChessPiece o : chessPane.getChessPieces()) {
 if (o.isSelected()) {

 System.out.println(o.isSelected()+" "+o.getRow()+" "+o.getCol());
 if (chessBoard.getCurrSide()==o.getSide()){
  if(o.getMoveStrategy().move(x, y,chessPane.getChessPieces())){
  o.setSelected(false);
  if(judgeGame(x,y)){
  printTip(o.getSide());
  }
  eatPiece(x,y);
  stack.push((ChessPiece) o.clone());
  o.setCol(x);
  o.setRow(y);

  chessBoard.changeSide();
  }

 }

 break;
 }

 }

 chessPane.drawPiece();
 }

 public void eatPiece(int x,int y){
 chessPane.getChessPieces().removeIf(e->{
 if(e.getCol()==x&&e.getRow()==y){
 stack.push(e);
 return true;
 }
 return false;
 });
 }

 public boolean judgeGame(int x,int y){
 for(ChessPiece e:chessPane.getChessPieces()){
 if(e.getCol()==x&&e.getRow()==y&&(
  e.getType()== PieceType.KINGBLACK||e.getType()==PieceType.KINGWHITE))
 return true;
 }

 return false;
 }

 public void printTip(char side){
 Alert alert = new Alert(Alert.AlertType.INFORMATION);
 alert.setContentText((side=='B'?"黑":"白")+"方取得胜利");
 alert.setTitle("游戏结束");
 alert.showAndWait();
 }


}

ResetAction

package com.Exercise3.Controller;

import com.Exercise3.entity.Piece.ChessPiece;
import com.Exercise3.view.ChessPane;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;


import java.util.Stack;

public class ResetAction implements EventHandler{
 private ChessPane chessPane;
 public ResetAction(ChessPane chessPane) {
 this.chessPane = chessPane;
 }

 @Override
 public void handle(ActionEvent e) {
 Stack stack = ReleaseAction.stack;
 if(!stack.empty()){
 chessPane.getChessPieces().removeIf(o->o.equals(stack.peek()));//去除原来的棋子
 chessPane.getChessPieces().add(stack.pop());//将以前压入堆栈的棋子重新加入

 chessPane.drawBoard();
 chessPane.drawPiece();
 }
 }
}

实体

棋子

ChessPiece

package com.Exercise3.entity.Piece;

import com.Exercise3.entity.PieceType;
import com.Exercise3.entity.Strategy.CarStrategy;

public class Car extends ChessPiece {
 public Car(PieceType type, int row, int col) {
 super(type, row, col);
 setMoveStrategy(new CarStrategy(getCol(),getRow()));
 }
}

Car

package com.Exercise3.entity.Piece;

import com.Exercise3.entity.PieceType;
import com.Exercise3.entity.Strategy.CarStrategy;

public class Car extends ChessPiece {
 public Car(PieceType type, int row, int col) {
 super(type, row, col);
 setMoveStrategy(new CarStrategy(getCol(),getRow()));
 }
}

Horse

package com.Exercise3.entity.Piece;

import com.Exercise3.entity.PieceType;
import com.Exercise3.entity.Strategy.HorseStategy;

public class Horse extends ChessPiece{
 public Horse(PieceType type, int row, int col) {
 super(type, row, col);
 setMoveStrategy(new HorseStategy(getCol(),getRow()));
 }
}

King

package com.Exercise3.entity.Piece;

import com.Exercise3.entity.PieceType;
import com.Exercise3.entity.Strategy.KingStrategy;

public class King extends ChessPiece {
 public King(PieceType type, int row, int col) {
 super(type, row, col);
 setMoveStrategy(new KingStrategy(getCol(),getRow()));
 }
}

Knight

package com.Exercise3.entity.Piece;

import com.Exercise3.entity.PieceType;
import com.Exercise3.entity.Strategy.KnightStrategy;

public class Knight extends ChessPiece {
 public Knight(PieceType type, int row, int col) {
 super(type, row, col);
 setMoveStrategy(new KnightStrategy(getCol(),getRow()));
 }
}

Queen

package com.Exercise3.entity.Piece;

import com.Exercise3.entity.PieceType;
import com.Exercise3.entity.Strategy.QueenStrategy;

public class Queen extends ChessPiece {
 public Queen(PieceType type, int row, int col) {
 super(type, row, col);
 setMoveStrategy(new QueenStrategy(getCol(),getRow()));
 }
}

Soldier

package com.Exercise3.entity.Piece;

import com.Exercise3.entity.PieceType;
import com.Exercise3.entity.Strategy.SoldierStategy;

public class Soldier extends ChessPiece{
 public Soldier(PieceType type, int row, int col) {
 super(type, row, col);
 setMoveStrategy(new SoldierStategy(getCol(),getRow(),getSide()));
 }

}

移动策略

MoveStategy

package com.Exercise3.entity.Strategy;

import com.Exercise3.entity.Piece.ChessPiece;

import java.util.Set;

public interface MoveStrategy {
 boolean move(int x, int y, Set chessPieces);
}

CarStrategy

package com.Exercise3.entity.Strategy;

import com.Exercise3.entity.Piece.ChessPiece;

import java.util.List;
import java.util.Set;

public class CarStrategy implements MoveStrategy {
 private int curX;
 private int curY;

 public CarStrategy() {
 }

 public CarStrategy(int curX, int curY) {
 this.curX = curX;
 this.curY = curY;
 }

 public boolean move(int x, int y, Set chessPieces) {
 if(x!=curX&&y!=curY)
 return false;
 if(isOverPiece(Math.min(curX,x),Math.min(curY,y),
 Math.max(curX,x),Math.max(curY,y),chessPieces))
 return false;
 curX = x;
 curY = y;
 return true;
 }

 public static boolean isOverPiece(int stX,int stY,int edX,int edY,Set chessPieces){
 for(ChessPiece e:chessPieces)
 if((e.getRow()>stY&&e.getRow()stX&&e.getCol()

HorseStrategy

package com.Exercise3.entity.Strategy;

import com.Exercise3.entity.Piece.ChessPiece;

import java.util.List;
import java.util.Set;

public class HorseStategy implements MoveStrategy{
 private int curX;
 private int curY;

 public HorseStategy(int curX, int curY) {
 this.curX = curX;
 this.curY = curY;
 }


 @Override
 public boolean move(int x, int y, Set chessPieces) {
 if((Math.abs(curX-x)==1&&Math.abs(curY-y)==2)||
 (Math.abs(curX-x)==2&&Math.abs(curY-y)==1)){
 curX = x;
 curY = y;
 return true;
 }
 return false;
 }

 public int getCurX() {
 return curX;
 }

 public void setCurX(int curX) {
 this.curX = curX;
 }

 public int getCurY() {
 return curY;
 }

 public void setCurY(int curY) {
 this.curY = curY;
 }
}

KingStrategy

package com.Exercise3.entity.Strategy;

import com.Exercise3.entity.Piece.ChessPiece;

import java.util.List;
import java.util.Set;

public class KingStrategy implements MoveStrategy {
 private int curX;
 private int curY;

 public KingStrategy(int curX, int cuY) {
 this.curX = curX;
 this.curY = cuY;
 }

 @Override
 public boolean move(int x, int y, Set chessPieces) {
 if(Math.abs(curX-x)<=1&&Math.abs(curY-y)<=1){
 curX = x;
 curY = y;
 return true;
 }

 return false;
 }

 public int getCurX() {
 return curX;
 }

 public void setCurX(int curX) {
 this.curX = curX;
 }

 public int getCurY() {
 return curY;
 }

 public void setCurY(int curY) {
 this.curY = curY;
 }
}

KnightStrage

package com.Exercise3.entity.Strategy;

import com.Exercise3.entity.Piece.ChessPiece;

import java.util.List;
import java.util.Map;
import java.util.Set;

public class KnightStrategy implements MoveStrategy {
 private int curX;
 private int curY;

 public KnightStrategy(int curX, int curY) {
 this.curX = curX;
 this.curY = curY;
 }

 @Override
 public boolean move(int x, int y, Set chessPieces) {
 if(Math.abs(x-curX)==Math.abs(y-curY)){
 if(isOverPiece(Math.min(curX,x),Math.min(curY,y),
  Math.max(curX,x),Math.max(curY,y),chessPieces))
 return false;
 curX=x;
 curY=y;
 return true;
 }
 return false;
 }

 public static boolean isOverPiece(int stX,int stY,int edX,int edY,Set chessPieces){
 for(ChessPiece e:chessPieces){
 if(e.getCol()-stX==edX-e.getCol()&&edY-e.getRow()==e.getRow()-stY){
 System.out.println(e.isSelected()+" "+e.getRow()+" "+e.getCol());
 return true;
 }
 }

 return false;
 }
 public int getCurX() {
 return curX;
 }

 public void setCurX(int curX) {
 this.curX = curX;
 }

 public int getCurY() {
 return curY;
 }

 public void setCurY(int curY) {
 this.curY = curY;
 }
}

QueeStrategy

package com.Exercise3.entity.Strategy;

import com.Exercise3.entity.Piece.ChessPiece;

import java.util.List;
import java.util.Set;


public class QueenStrategy implements MoveStrategy{
 private int curX;
 private int curY;

 public QueenStrategy(int curX, int curY) {
 this.curX = curX;
 this.curY = curY;
 }

 @Override
 public boolean move (int x, int y, Set chessPieces) {
 if(Math.abs(x-curX)==Math.abs(y-curY)||!(x!=curX&&y!=curY)){
 if(isOverPiece(Math.min(curX,x),Math.min(curY,y),
  Math.max(curX,x),Math.max(curY,y),chessPieces))
 return false;
 curX = x;
 curY = y;
 return true;
 }
 return false;
 }

 public boolean isOverPiece (int stX,int stY,int edX,int edY,Set chessPieces) {
 for(ChessPiece e:chessPieces){
 if(e.getRow()!=stY&&e.getCol()!=stX){
 return KnightStrategy.isOverPiece(stX,stY,edX,edY,chessPieces);
 }
 else{
 return CarStrategy.isOverPiece(stX,stY,edX,edY,chessPieces);
 }
 }
 return false;
 }


 public int getCurX() {
 return curX;
 }

 public void setCurX(int curX) {
 this.curX = curX;
 }

 public int getCurY() {
 return curY;
 }

 public void setCurY(int curY) {
 this.curY = curY;
 }
}

SoldierStrategy

package com.Exercise3.entity.Strategy;

import com.Exercise3.entity.Piece.ChessPiece;

import java.util.List;
import java.util.Set;

public class SoldierStategy implements MoveStrategy{
 private int curX;
 private int curY;
 private char side;
 private boolean firstMove = true;

 public SoldierStategy(int curX, int curY,char side) {
 this.curX = curX;
 this.curY = curY;
 this.side = side;
 }


 @Override
 public boolean move(int x, int y, Set chessPieces) {
 //直线移动
 if(curY==y){
 switch (side){
 case 'B': {
  if(isFirstMove()&&(x==curX+1||curX+2==x)){
  setFirstMove(false);
  curY = y;
  curX = x;
  return true;
  }
  else if(!isFirstMove()&&curX+1==x){
  curY = y;
  curX = x;
  return true;
  }
  break;
 }

 case 'W':{
  if(isFirstMove()&&(x==curX-1||x==curX-2)){
  setFirstMove(false);
  curY = y;
  curX = x;
  return true;
  }
  else if(!isFirstMove()&&curX-1==x){
  curY = y;
  curX = x;
  return true;
  }
  break;
 }
 }
 }

 //吃子移动
 for(ChessPiece e:chessPieces){
 if(Math.abs(e.getRow()-curY)==1){
 if(e.getCol()-curX==1&&e.getSide()=='W'||
 curX-e.getCol()==1&&e.getSide()=='B'){
  curY = y;
  curX = x;
  return true;
 }

 }
 }

 return false;
 }



 public boolean isFirstMove() {
 return firstMove;
 }

 public void setFirstMove(boolean firstMove) {
 this.firstMove = firstMove;
 }

 public int getCurX() {
 return curX;
 }

 public void setCurX(int curX) {
 this.curX = curX;
 }

 public int getCurY() {
 return curY;
 }

 public void setCurY(int curY) {
 this.curY = curY;
 }
}

棋子类型

package com.Exercise3.entity;

public enum PieceType {
 KINGBLACK("KingBlack","com/Exercise3/img/KingBlack.jpg"),
 QUEENBLACK("QueenBlack","com/Exercise3/img/QueenBlack.jpg"),
 CARBLACK("CarBlack","com/Exercise3/img/CarBlack.jpg"),
 HORSEBLACK("HorseBlack","com/Exercise3/img/HorseBlack.jpg"),
 SOLDIERBLACK("SoldierBlack","com/Exercise3/img/SoldierBlack.jpg"),
 KNIGHTBLACK("KnightBlack","com/Exercise3/img/KnightBlack.jpg"),

 KINGWHITE("KingWhite","com/Exercise3/img/KingWhite.jpg"),
 QUEENWHITE("QueenWhite","com/Exercise3/img/QueenWhite.jpg"),
 CARWHITE("CarWhite","com/Exercise3/img/CarWhite.jpg"),
 HORSEWHITE("HorseWhite","com/Exercise3/img/HorseWhite.jpg"),
 SOLDIERWHITE("SoldierWhite","com/Exercise3/img/SoldierWhite.jpg"),
 KNIGHTWHITE("KnightWhite","com/Exercise3/img/KnightWhite.jpg");


 private String desc;
 private PieceType(String desc,String url ){
 this.desc = desc;
 this.url = url;
 }

 private String url;

 public String getDesc(){
 return desc;
 }

 public String getUrl() {
 return url;
 }
}

视图

package com.Exercise3.view;

public class ChessBoard {
 static ChessBoard chessBoard = null;
 private int row;
 private int col;
 private double cellLength;
 private double startX;
 private double startY;
 private char currSide;

 private ChessBoard(double cellLength, double startX, double startY) {
 this.row = 8;
 this.col = 8;
 this.cellLength = cellLength;
 this.startX = startX;
 this.startY = startY;
 this.currSide = 'B';
 }

 public static ChessBoard getInstance(double cellLength, double startX, double startY){
 if(chessBoard == null)
 return new ChessBoard(cellLength,startX,startY);
 return chessBoard;
 }

 public ChessBoard getInstance(){
 return chessBoard;
 }

 public int getCol() {
 return col;
 }


 public int getRow() {
 return row;
 }

 public double getCellLength() {
 return cellLength;
 }

 public void changeSide(){
 currSide=(currSide=='B'?'W':'B');
 }

 public void setCellLength(double cellLength) {
 this.cellLength = cellLength;
 }

 public double getStartX() {
 return startX;
 }

 public void setStartX(double startX) {
 this.startX = startX;
 }

 public double getStartY() {
 return startY;
 }

 public void setStartY(double startY) {
 this.startY = startY;
 }

 public char getCurrSide() {
 return currSide;
 }
}
package com.Exercise3.view;

import com.Exercise3.entity.Piece.*;
import com.Exercise3.entity.PieceType;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.image.*;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;

import java.util.*;

public class ChessPane extends Pane {
 private Set chessPieces;
 private ChessBoard chessBoard;
 private Canvas canvas;
 private GraphicsContext gc;

 public ChessPane(ChessBoard chessBoard) {
 this.chessBoard = chessBoard;
 setChessPiece();
 canvas = new Canvas(900,900);
 gc = canvas.getGraphicsContext2D();
 draw();
 }


 public void draw(){
 drawBoard();
 drawPiece();
 getChildren().add(canvas);
 }


 public void drawBoard(){
 gc.clearRect(0,0,900,900);
 double x = chessBoard.getStartX();
 double y = chessBoard.getStartY();
 double cell = chessBoard.getCellLength();


 boolean flag = false;
 for(int i=0;i{
 if(e.isSelected()){
 gc.setFill(Color.valueOf("#6495ED"));
 gc.fillRect(chessBoard.getStartX()+e.getCol()*cell,
  chessBoard.getStartY()+e.getRow()*cell,
  cell,cell);
 }

 Image image = new Image(e.getType().getUrl());
 gc.drawImage(image,
  chessBoard.getStartX()+10 + e.getCol() * cell,
  chessBoard.getStartY()+10 + e.getRow() * cell,
  cell-20, cell-20);
 });
 }


 //加入棋子
 public void setChessPiece() {
 chessPieces = new HashSet<>();
 chessPieces.add(new Car(PieceType.CARBLACK,0,0));
 chessPieces.add(new Horse(PieceType.HORSEBLACK,1,0));
 chessPieces.add(new Knight(PieceType.KNIGHTBLACK,2,0));
 chessPieces.add(new King(PieceType.KINGBLACK,3,0));
 chessPieces.add(new Queen(PieceType.QUEENBLACK,4,0));
 chessPieces.add(new Knight(PieceType.KNIGHTBLACK,5,0));
 chessPieces.add(new Horse(PieceType.HORSEBLACK,6,0));
 chessPieces.add(new Car(PieceType.CARBLACK,7,0));
 for(int i=0;i<8;i++){
 chessPieces.add(new Soldier(PieceType.SOLDIERBLACK,i,1));
 }


 chessPieces.add(new Car(PieceType.CARWHITE,0,7));
 chessPieces.add(new Horse(PieceType.HORSEWHITE,1,7));
 chessPieces.add(new Knight(PieceType.KNIGHTWHITE,2,7));
 chessPieces.add(new King(PieceType.KINGWHITE,3,7));
 chessPieces.add(new Queen(PieceType.QUEENWHITE,4,7));
 chessPieces.add(new Knight(PieceType.KNIGHTWHITE,5,7));
 chessPieces.add(new Horse(PieceType.HORSEWHITE,6,7));
 chessPieces.add(new Car(PieceType.CARWHITE,7,7));
 for(int i=0;i<8;i++){
 chessPieces.add(new Soldier(PieceType.SOLDIERWHITE,i,6));
 }
 }

 public ChessBoard getChessBoard() {
 return chessBoard;
 }

 public void setChessBoard(ChessBoard chessBoard) {
 this.chessBoard = chessBoard;
 }

 public Set getChessPieces() {
 return chessPieces;
 }

 public void setChessPieces(Set chessPieces) {
 this.chessPieces = chessPieces;
 }

 public Canvas getCanvas() {
 return canvas;
 }

 public void setCanvas(Canvas canvas) {
 this.canvas = canvas;
 }

 public GraphicsContext getGc() {
 return gc;
 }

 public void setGc(GraphicsContext gc) {
 this.gc = gc;
 }
}

测试

package com.Exercise3;

import com.Exercise3.Controller.PressedAction;
import com.Exercise3.Controller.ReleaseAction;
import com.Exercise3.Controller.ResetAction;
import com.Exercise3.view.ChessBoard;
import com.Exercise3.view.ChessPane;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;

import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.stage.Stage;

public class Test extends Application {

 public static void main(String[] args) {
 launch(args);
 }

 @Override
 public void start(Stage primaryStage) {

 String MEDIA_URL = "file:/E:/IdeaProjects/Experiment/src/com/Exercise3/music/BackgroundMusic.mp3";
 ChessBoard chessBoard = ChessBoard.getInstance(100,40,40);

 //添加媒体资源

 Media media = new Media(MEDIA_URL);
 MediaPlayer mediaPlayer = new MediaPlayer(media);
 mediaPlayer.setAutoPlay(true);
 mediaPlayer.setCycleCount(MediaPlayer.INDEFINITE);
 mediaPlayer.play();


 ChessPane pane = new ChessPane(chessBoard);
 pane.setOnMousePressed(new PressedAction(pane,mediaPlayer));

 pane.setOnMouseReleased(new ReleaseAction(pane));

 BorderPane borderPane = new BorderPane();
 borderPane.setCenter(pane);
 HBox hBox = new HBox();
 hBox.setAlignment(Pos.TOP_CENTER);

 Button button = new Button("悔棋");
 button.setOnAction(new ResetAction(pane));

 hBox.getChildren().add(button);
 borderPane.setBottom(hBox);
 Scene scene = new Scene(borderPane,900,900);
 primaryStage.setScene(scene);
 primaryStage.setTitle("国际象棋");
 primaryStage.show();

 }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持创新互联。


当前名称:Javafx实现国际象棋游戏
本文路径:http://kswjz.com/article/jesheg.html
扫二维码与项目经理沟通

我们在微信上24小时期待你的声音

解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流